squib 0.18.0 → 0.19.0a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +39 -39
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +22 -22
  4. data/.github/ISSUE_TEMPLATE/help-wanted.md +13 -13
  5. data/.github/ISSUE_TEMPLATE/release-todo.md +45 -33
  6. data/.github/PULL_REQUEST_TEMPLATE.md +13 -13
  7. data/.github/workflows/tests.yml +22 -22
  8. data/.gitignore +41 -41
  9. data/.vscode/settings.json +4 -3
  10. data/CHANGELOG.md +460 -460
  11. data/CONTRIBUTING.md +40 -40
  12. data/Dockerfile +27 -27
  13. data/Gemfile +2 -2
  14. data/Guardfile +8 -8
  15. data/LICENSE.txt +22 -22
  16. data/README.md +139 -139
  17. data/Rakefile +51 -51
  18. data/bin/squib +5 -5
  19. data/lib/squib/api/settings.rb +21 -21
  20. data/lib/squib/args/arg_loader.rb +138 -138
  21. data/lib/squib/args/box.rb +59 -59
  22. data/lib/squib/args/card_range.rb +34 -34
  23. data/lib/squib/args/color_validator.rb +7 -7
  24. data/lib/squib/args/coords.rb +49 -49
  25. data/lib/squib/args/csv_opts.rb +22 -22
  26. data/lib/squib/args/dir_validator.rb +11 -11
  27. data/lib/squib/args/draw.rb +93 -93
  28. data/lib/squib/args/drop_shadow.rb +39 -39
  29. data/lib/squib/args/embed_adjust.rb +22 -22
  30. data/lib/squib/args/embed_key.rb +12 -12
  31. data/lib/squib/args/hand_special.rb +37 -37
  32. data/lib/squib/args/import.rb +67 -67
  33. data/lib/squib/args/input_file.rb +55 -55
  34. data/lib/squib/args/paint.rb +43 -43
  35. data/lib/squib/args/paragraph.rb +118 -118
  36. data/lib/squib/args/save_batch.rb +65 -65
  37. data/lib/squib/args/scale_box.rb +57 -57
  38. data/lib/squib/args/sheet.rb +165 -165
  39. data/lib/squib/args/showcase_special.rb +41 -41
  40. data/lib/squib/args/sprue_file.rb +44 -44
  41. data/lib/squib/args/svg_special.rb +37 -37
  42. data/lib/squib/args/transform.rb +55 -55
  43. data/lib/squib/args/typographer.rb +115 -115
  44. data/lib/squib/args/unit_conversion.rb +27 -27
  45. data/lib/squib/args/xywh_shorthands.rb +50 -50
  46. data/lib/squib/builtin/layouts/economy.yml +85 -85
  47. data/lib/squib/builtin/layouts/fantasy.yml +101 -101
  48. data/lib/squib/builtin/layouts/hand.yml +62 -62
  49. data/lib/squib/builtin/layouts/party.yml +94 -94
  50. data/lib/squib/builtin/layouts/playing-card.yml +35 -35
  51. data/lib/squib/builtin/layouts/tuck_box.yml +46 -46
  52. data/lib/squib/builtin/projects/advanced/.gitignore +4 -4
  53. data/lib/squib/builtin/projects/advanced/ABOUT.md +19 -19
  54. data/lib/squib/builtin/projects/advanced/Gemfile +11 -11
  55. data/lib/squib/builtin/projects/advanced/Guardfile +21 -21
  56. data/lib/squib/builtin/projects/advanced/IDEAS.md +22 -22
  57. data/lib/squib/builtin/projects/advanced/PLAYTESTING.md +26 -26
  58. data/lib/squib/builtin/projects/advanced/Rakefile +27 -27
  59. data/lib/squib/builtin/projects/advanced/config.yml +49 -49
  60. data/lib/squib/builtin/projects/advanced/docs/PNP NOTES.md +3 -3
  61. data/lib/squib/builtin/projects/advanced/docs/RULES.md +21 -21
  62. data/lib/squib/builtin/projects/advanced/img/example.svg +60 -60
  63. data/lib/squib/builtin/projects/advanced/layouts/deck.yml +27 -27
  64. data/lib/squib/builtin/projects/advanced/src/deck.rb +34 -34
  65. data/lib/squib/builtin/projects/advanced/src/version.rb +3 -3
  66. data/lib/squib/builtin/projects/basic/.gitignore +4 -4
  67. data/lib/squib/builtin/projects/basic/ABOUT.md +19 -19
  68. data/lib/squib/builtin/projects/basic/Gemfile +3 -3
  69. data/lib/squib/builtin/projects/basic/IDEAS.md +22 -22
  70. data/lib/squib/builtin/projects/basic/PLAYTESTING.md +26 -26
  71. data/lib/squib/builtin/projects/basic/PNP NOTES.md +3 -3
  72. data/lib/squib/builtin/projects/basic/RULES.md +21 -21
  73. data/lib/squib/builtin/projects/basic/Rakefile +7 -7
  74. data/lib/squib/builtin/projects/basic/config.yml +50 -50
  75. data/lib/squib/builtin/projects/basic/deck.rb +6 -6
  76. data/lib/squib/builtin/sprues/a4_euro_card.yml +42 -42
  77. data/lib/squib/builtin/sprues/a4_poker_card_8up.yml +40 -40
  78. data/lib/squib/builtin/sprues/a4_poker_card_9up.yml +42 -42
  79. data/lib/squib/builtin/sprues/a4_usa_card.yml +42 -42
  80. data/lib/squib/builtin/sprues/drivethrucards_1up.yml +10 -10
  81. data/lib/squib/builtin/sprues/letter_poker_card_9up.yml +25 -25
  82. data/lib/squib/builtin/sprues/letter_poker_foldable_8up.yml +52 -52
  83. data/lib/squib/builtin/sprues/printplaygames_18up.yml +68 -68
  84. data/lib/squib/card.rb +75 -75
  85. data/lib/squib/commands/cli.rb +39 -39
  86. data/lib/squib/commands/data/template_option.rb +109 -109
  87. data/lib/squib/commands/make_sprue.rb +277 -277
  88. data/lib/squib/commands/new.rb +77 -77
  89. data/lib/squib/conf.rb +149 -149
  90. data/lib/squib/constants.rb +17 -17
  91. data/lib/squib/deck.rb +138 -138
  92. data/lib/squib/dsl/background.rb +35 -35
  93. data/lib/squib/dsl/circle.rb +39 -39
  94. data/lib/squib/dsl/csv.rb +42 -42
  95. data/lib/squib/dsl/curve.rb +35 -35
  96. data/lib/squib/dsl/cut_zone.rb +47 -47
  97. data/lib/squib/dsl/ellipse.rb +37 -37
  98. data/lib/squib/dsl/grid.rb +35 -35
  99. data/lib/squib/dsl/groups.rb +54 -54
  100. data/lib/squib/dsl/hand.rb +42 -42
  101. data/lib/squib/dsl/line.rb +35 -35
  102. data/lib/squib/dsl/png.rb +57 -57
  103. data/lib/squib/dsl/polygon.rb +36 -36
  104. data/lib/squib/dsl/rect.rb +37 -37
  105. data/lib/squib/dsl/safe_zone.rb +48 -48
  106. data/lib/squib/dsl/save.rb +21 -21
  107. data/lib/squib/dsl/save_pdf.rb +50 -50
  108. data/lib/squib/dsl/save_png.rb +48 -48
  109. data/lib/squib/dsl/save_sheet.rb +53 -53
  110. data/lib/squib/dsl/showcase.rb +43 -43
  111. data/lib/squib/dsl/star.rb +37 -37
  112. data/lib/squib/dsl/svg.rb +63 -63
  113. data/lib/squib/dsl/text.rb +54 -54
  114. data/lib/squib/dsl/text_embed.rb +79 -79
  115. data/lib/squib/dsl/triangle.rb +35 -35
  116. data/lib/squib/dsl/units.rb +37 -37
  117. data/lib/squib/dsl/xlsx.rb +40 -40
  118. data/lib/squib/dsl/yaml.rb +40 -40
  119. data/lib/squib/errors_warnings/warn_unexpected_params.rb +14 -14
  120. data/lib/squib/graphics/background.rb +14 -14
  121. data/lib/squib/graphics/cairo_context_wrapper.rb +115 -115
  122. data/lib/squib/graphics/embedding_utils.rb +28 -28
  123. data/lib/squib/graphics/gradient_regex.rb +47 -47
  124. data/lib/squib/graphics/hand.rb +42 -42
  125. data/lib/squib/graphics/image.rb +123 -123
  126. data/lib/squib/graphics/save_doc.rb +77 -77
  127. data/lib/squib/graphics/save_images.rb +91 -91
  128. data/lib/squib/graphics/save_pdf.rb +90 -90
  129. data/lib/squib/graphics/save_sprue.rb +231 -231
  130. data/lib/squib/graphics/shapes.rb +143 -143
  131. data/lib/squib/graphics/showcase.rb +85 -85
  132. data/lib/squib/graphics/text.rb +202 -202
  133. data/lib/squib/import/csv_importer.rb +45 -45
  134. data/lib/squib/import/data_frame.rb +108 -108
  135. data/lib/squib/import/quantity_exploder.rb +17 -17
  136. data/lib/squib/import/xlsx_importer.rb +28 -28
  137. data/lib/squib/import/yaml_importer.rb +30 -30
  138. data/lib/squib/layout_parser.rb +155 -155
  139. data/lib/squib/progress.rb +38 -38
  140. data/lib/squib/sample_helpers.rb +34 -34
  141. data/lib/squib/sprues/crop_line.rb +28 -28
  142. data/lib/squib/sprues/crop_line_dash.rb +35 -35
  143. data/lib/squib/sprues/invalid_sprue_definition.rb +9 -9
  144. data/lib/squib/sprues/sprue.rb +208 -208
  145. data/lib/squib/sprues/sprue_schema.rb +51 -51
  146. data/lib/squib/system_fonts.rb +16 -16
  147. data/lib/squib/version.rb +11 -11
  148. data/lib/squib.rb +35 -35
  149. data/samples/autoscale_font/_autoscale_font.rb +98 -98
  150. data/samples/backend/_backend.rb +26 -26
  151. data/samples/basic.rb +19 -19
  152. data/samples/build_groups/build_groups.rb +36 -36
  153. data/samples/colors/_colors.rb +44 -44
  154. data/samples/colors/_gradients.rb +34 -34
  155. data/samples/colors/_switch_color.rb +33 -33
  156. data/samples/config/config_text_markup.rb +20 -20
  157. data/samples/config/custom_config.rb +18 -18
  158. data/samples/data/_csv.rb +33 -33
  159. data/samples/data/_excel.rb +55 -55
  160. data/samples/data/_yaml.rb +12 -12
  161. data/samples/hello_world.rb +6 -6
  162. data/samples/images/_cairo_access.rb +39 -39
  163. data/samples/images/_images.rb +104 -104
  164. data/samples/images/_more_load_images.rb +102 -102
  165. data/samples/images/_placeholders.rb +48 -48
  166. data/samples/intro/01_hello.rb +8 -8
  167. data/samples/intro/02_options.rb +14 -14
  168. data/samples/intro/03_layout.rb +11 -11
  169. data/samples/intro/04_arrays.rb +15 -15
  170. data/samples/intro/05_excel.rb +14 -14
  171. data/samples/layouts/builtin_layouts.rb +97 -97
  172. data/samples/layouts/layouts.rb +71 -71
  173. data/samples/project/src/characters.rb +8 -8
  174. data/samples/project/src/skills.rb +7 -7
  175. data/samples/proofs/_tgc_proofs.rb +16 -16
  176. data/samples/ranges/_ranges.rb +64 -64
  177. data/samples/saves/_hand.rb +23 -23
  178. data/samples/saves/_portrait_landscape.rb +23 -23
  179. data/samples/saves/_save_filenames.rb +28 -28
  180. data/samples/saves/_save_pdf.rb +29 -29
  181. data/samples/saves/_saves.rb +75 -75
  182. data/samples/saves/_showcase.rb +25 -25
  183. data/samples/shadows/_shadow.rb +71 -71
  184. data/samples/shapes/_draw_shapes.rb +60 -60
  185. data/samples/shapes/_proofs.rb +22 -22
  186. data/samples/sprues/_advanced_sprues.rb +25 -25
  187. data/samples/sprues/_builtin_sprues.rb +22 -22
  188. data/samples/sprues/_fold_sheet.rb +30 -30
  189. data/samples/sprues/_hex_tiles.rb +15 -15
  190. data/samples/sprues/_mints.rb +11 -11
  191. data/samples/sprues/_negative_coords.rb +6 -6
  192. data/samples/sprues/_sprue_example.rb +11 -11
  193. data/samples/system_font_debug/_list_fonts.rb +14 -14
  194. data/samples/text/_embed_text.rb +128 -128
  195. data/samples/text/_text.rb +52 -52
  196. data/samples/text/_text_options.rb +103 -103
  197. data/samples/text/bug134.rb +14 -14
  198. data/samples/units/_cells.rb +50 -50
  199. data/samples/units/_shorthands.rb +48 -48
  200. data/samples/units/_units.rb +39 -39
  201. data/squib.gemspec +58 -58
  202. metadata +21 -21
@@ -1,42 +1,42 @@
1
- require_relative 'cairo_context_wrapper'
2
-
3
- module Squib
4
- class Deck
5
-
6
- # Draw cards in a fan.
7
- # @api private
8
- def render_hand(range, sheet, hand)
9
- cards = range.collect { |i| @cards[i] }
10
- center_x = width / 2.0
11
- center_y = hand.radius + height
12
- out_size = 3.0 * center_y
13
- angle_delta = (hand.angle_range.last - hand.angle_range.first) / cards.size
14
- cxt = Cairo::Context.new(Cairo::RecordingSurface.new(0, 0, out_size, out_size))
15
- cxt.translate(out_size / 2.0, out_size / 2.0)
16
- cxt.rotate(hand.angle_range.first)
17
- cxt.translate(-width, -width)
18
- cards.each_with_index do |card, i|
19
- cxt.translate(center_x, center_y)
20
- cxt.rotate(angle_delta)
21
- cxt.translate(-center_x, -center_y)
22
- card.use_cairo do |card_cxt|
23
- cxt.rounded_rectangle(sheet.trim, sheet.trim,
24
- width - (2 * sheet.trim), height - (2 * sheet.trim),
25
- sheet.trim_radius, sheet.trim_radius)
26
- cxt.clip
27
- cxt.set_source(card_cxt.target)
28
- cxt.paint
29
- cxt.reset_clip
30
- end
31
- end
32
- x, y, w, h = cxt.target.ink_extents # I love Ruby assignment ;)
33
- png_cxt = Squib::Graphics::CairoContextWrapper.new(Cairo::Context.new(Cairo::ImageSurface.new(w + 2 * sheet.margin, h + 2 * sheet.margin)))
34
- png_cxt.set_source_squibcolor(sheet.fill_color)
35
- png_cxt.paint
36
- png_cxt.translate(-x + sheet.margin, -y + sheet.margin)
37
- png_cxt.set_source(cxt.target)
38
- png_cxt.paint
39
- png_cxt.target.write_to_png sheet.full_filename
40
- end
41
- end
42
- end
1
+ require_relative 'cairo_context_wrapper'
2
+
3
+ module Squib
4
+ class Deck
5
+
6
+ # Draw cards in a fan.
7
+ # @api private
8
+ def render_hand(range, sheet, hand)
9
+ cards = range.collect { |i| @cards[i] }
10
+ center_x = width / 2.0
11
+ center_y = hand.radius + height
12
+ out_size = 3.0 * center_y
13
+ angle_delta = (hand.angle_range.last - hand.angle_range.first) / cards.size
14
+ cxt = Cairo::Context.new(Cairo::RecordingSurface.new(0, 0, out_size, out_size))
15
+ cxt.translate(out_size / 2.0, out_size / 2.0)
16
+ cxt.rotate(hand.angle_range.first)
17
+ cxt.translate(-width, -width)
18
+ cards.each_with_index do |card, i|
19
+ cxt.translate(center_x, center_y)
20
+ cxt.rotate(angle_delta)
21
+ cxt.translate(-center_x, -center_y)
22
+ card.use_cairo do |card_cxt|
23
+ cxt.rounded_rectangle(sheet.trim, sheet.trim,
24
+ width - (2 * sheet.trim), height - (2 * sheet.trim),
25
+ sheet.trim_radius, sheet.trim_radius)
26
+ cxt.clip
27
+ cxt.set_source(card_cxt.target)
28
+ cxt.paint
29
+ cxt.reset_clip
30
+ end
31
+ end
32
+ x, y, w, h = cxt.target.ink_extents # I love Ruby assignment ;)
33
+ png_cxt = Squib::Graphics::CairoContextWrapper.new(Cairo::Context.new(Cairo::ImageSurface.new(w + 2 * sheet.margin, h + 2 * sheet.margin)))
34
+ png_cxt.set_source_squibcolor(sheet.fill_color)
35
+ png_cxt.paint
36
+ png_cxt.translate(-x + sheet.margin, -y + sheet.margin)
37
+ png_cxt.set_source(cxt.target)
38
+ png_cxt.paint
39
+ png_cxt.target.write_to_png sheet.full_filename
40
+ end
41
+ end
42
+ end
@@ -1,123 +1,123 @@
1
- module Squib
2
-
3
- # Cache all pngs we've already loaded
4
- #
5
- # :nodoc:
6
- # @api private
7
- def cache_load_image(file)
8
- @img_cache ||= {}
9
- @img_cache[file] ||= open_png file
10
- end
11
- module_function :cache_load_image
12
-
13
- # Open a PNG file, checking magic bytes if it's a real PNG
14
- # Magic bytes taken from:
15
- # https://en.wikipedia.org/wiki/List_of_file_signatures
16
- # :nodoc:
17
- # @api private
18
- PNG_MAGIC = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]
19
- def open_png(file)
20
- if PNG_MAGIC == File.read(file, 8).bytes
21
- return Cairo::ImageSurface.from_png(file)
22
- else
23
- raise ArgumentError.new("ERROR: #{file} is not a PNG file")
24
- end
25
- end
26
- module_function :open_png
27
-
28
- class Card
29
-
30
- # :nodoc:
31
- # @api private
32
- def png(file, box, paint, trans)
33
- Squib.logger.debug {"RENDERING PNG: \n file: #{file}\n box: #{box}\n paint: #{paint}\n trans: #{trans}"}
34
- return if file.nil? or file.eql? ''
35
- png = Squib.cache_load_image(file)
36
- use_cairo do |cc|
37
- cc.translate(box.x, box.y)
38
- box.width = png.width.to_f if box.width == :native
39
- box.height = png.height.to_f if box.height == :native
40
- box.width = png.width.to_f * box.height.to_f / png.height.to_f if box.width == :scale
41
- box.height = png.height.to_f * box.width.to_f / png.width.to_f if box.height == :scale
42
-
43
- scale_width = box.width.to_f / png.width.to_f
44
- scale_height = box.height.to_f / png.height.to_f
45
- warn_png_scale(file, scale_width, scale_height)
46
- cc.scale(scale_width, scale_height)
47
-
48
- cc.rotate(trans.angle)
49
- cc.flip(trans.flip_vertical, trans.flip_horizontal, box.width / 2, box.height / 2)
50
- cc.translate(-box.x, -box.y)
51
-
52
- trans.crop_width = png.width.to_f if trans.crop_width == :native
53
- trans.crop_height = png.height.to_f if trans.crop_height == :native
54
- cc.rounded_rectangle(box.x, box.y, trans.crop_width, trans.crop_height, trans.crop_corner_x_radius, trans.crop_corner_y_radius)
55
- cc.clip
56
- cc.translate(-trans.crop_x, -trans.crop_y)
57
-
58
-
59
- cc.set_source(png, box.x, box.y)
60
- cc.operator = paint.blend unless paint.blend == :none
61
- if paint.mask.empty?
62
- cc.paint(paint.alpha)
63
- else
64
- cc.set_source_squibcolor(paint.mask)
65
- cc.mask(png, box.x, box.y)
66
- end
67
- end
68
- end
69
-
70
- # :nodoc:
71
- # @api private
72
- def warn_png_scale(file, scale_width, scale_height)
73
- if @deck.conf.warn_png_scale? && (scale_width > 1.0 || scale_height > 1.0)
74
- Squib.logger.warn "PNG is being upscaled - antialiasing could result: #{file}"
75
- end
76
- end
77
-
78
- # :nodoc:
79
- # @api private
80
- def svg(file, svg_args, box, paint, trans)
81
- Squib.logger.debug {"Rendering: #{file}, id: #{id} @#{x},#{y} #{width}x#{height}, alpha: #{alpha}, blend: #{blend}, angle: #{angle}, mask: #{mask}"}
82
- Squib.logger.warn 'Both an SVG file and SVG data were specified' unless file.to_s.empty? || svg_args.data.to_s.empty?
83
- return if (file.nil? or file.eql? '') and svg_args.data.nil? # nothing specified TODO Move this out to arg validator
84
- svg_args.data = File.read(file) if svg_args.data.to_s.empty?
85
- begin
86
- svg = Rsvg::Handle.new_from_data(svg_args.data)
87
- rescue Rsvg::Error::Failed
88
- Squib.logger.error "Invalid SVG data. Is '#{file}' a valid svg file?"
89
- return
90
- end
91
- box.width = svg.width if box.width == :native
92
- box.height = svg.height if box.height == :native
93
- box.width = svg.width.to_f * box.height.to_f / svg.height.to_f if box.width == :scale
94
- box.height = svg.height.to_f * box.width.to_f / svg.width.to_f if box.height == :scale
95
- scale_width = box.width.to_f / svg.width.to_f
96
- scale_height = box.height.to_f / svg.height.to_f
97
- use_cairo do |cc|
98
- cc.translate(box.x, box.y)
99
- cc.flip(trans.flip_vertical, trans.flip_horizontal, box.width / 2, box.height / 2)
100
- cc.rotate(trans.angle)
101
- cc.scale(scale_width, scale_height)
102
-
103
- trans.crop_width = box.width if trans.crop_width == :native
104
- trans.crop_height = box.height if trans.crop_height == :native
105
- cc.rounded_rectangle(0, 0, trans.crop_width / scale_width, trans.crop_height / scale_height, trans.crop_corner_x_radius, trans.crop_corner_y_radius)
106
- cc.clip
107
- cc.translate(-trans.crop_x, -trans.crop_y)
108
-
109
- cc.operator = paint.blend unless paint.blend == :none
110
- if paint.mask.to_s.empty?
111
- cc.render_rsvg_handle(svg, id: svg_args.id)
112
- else
113
- tmp = Cairo::ImageSurface.new(box.width / scale_width, box.height / scale_height)
114
- tmp_cc = Cairo::Context.new(tmp)
115
- tmp_cc.render_rsvg_handle(svg, id: svg_args.id)
116
- cc.set_source_squibcolor(paint.mask)
117
- cc.mask(tmp, 0, 0)
118
- end
119
- end
120
- end
121
-
122
- end
123
- end
1
+ module Squib
2
+
3
+ # Cache all pngs we've already loaded
4
+ #
5
+ # :nodoc:
6
+ # @api private
7
+ def cache_load_image(file)
8
+ @img_cache ||= {}
9
+ @img_cache[file] ||= open_png file
10
+ end
11
+ module_function :cache_load_image
12
+
13
+ # Open a PNG file, checking magic bytes if it's a real PNG
14
+ # Magic bytes taken from:
15
+ # https://en.wikipedia.org/wiki/List_of_file_signatures
16
+ # :nodoc:
17
+ # @api private
18
+ PNG_MAGIC = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]
19
+ def open_png(file)
20
+ if PNG_MAGIC == File.read(file, 8).bytes
21
+ return Cairo::ImageSurface.from_png(file)
22
+ else
23
+ raise ArgumentError.new("ERROR: #{file} is not a PNG file")
24
+ end
25
+ end
26
+ module_function :open_png
27
+
28
+ class Card
29
+
30
+ # :nodoc:
31
+ # @api private
32
+ def png(file, box, paint, trans)
33
+ Squib.logger.debug {"RENDERING PNG: \n file: #{file}\n box: #{box}\n paint: #{paint}\n trans: #{trans}"}
34
+ return if file.nil? or file.eql? ''
35
+ png = Squib.cache_load_image(file)
36
+ use_cairo do |cc|
37
+ cc.translate(box.x, box.y)
38
+ box.width = png.width.to_f if box.width == :native
39
+ box.height = png.height.to_f if box.height == :native
40
+ box.width = png.width.to_f * box.height.to_f / png.height.to_f if box.width == :scale
41
+ box.height = png.height.to_f * box.width.to_f / png.width.to_f if box.height == :scale
42
+
43
+ scale_width = box.width.to_f / png.width.to_f
44
+ scale_height = box.height.to_f / png.height.to_f
45
+ warn_png_scale(file, scale_width, scale_height)
46
+ cc.scale(scale_width, scale_height)
47
+
48
+ cc.rotate(trans.angle)
49
+ cc.flip(trans.flip_vertical, trans.flip_horizontal, box.width / 2, box.height / 2)
50
+ cc.translate(-box.x, -box.y)
51
+
52
+ trans.crop_width = png.width.to_f if trans.crop_width == :native
53
+ trans.crop_height = png.height.to_f if trans.crop_height == :native
54
+ cc.rounded_rectangle(box.x, box.y, trans.crop_width, trans.crop_height, trans.crop_corner_x_radius, trans.crop_corner_y_radius)
55
+ cc.clip
56
+ cc.translate(-trans.crop_x, -trans.crop_y)
57
+
58
+
59
+ cc.set_source(png, box.x, box.y)
60
+ cc.operator = paint.blend unless paint.blend == :none
61
+ if paint.mask.empty?
62
+ cc.paint(paint.alpha)
63
+ else
64
+ cc.set_source_squibcolor(paint.mask)
65
+ cc.mask(png, box.x, box.y)
66
+ end
67
+ end
68
+ end
69
+
70
+ # :nodoc:
71
+ # @api private
72
+ def warn_png_scale(file, scale_width, scale_height)
73
+ if @deck.conf.warn_png_scale? && (scale_width > 1.0 || scale_height > 1.0)
74
+ Squib.logger.warn "PNG is being upscaled - antialiasing could result: #{file}"
75
+ end
76
+ end
77
+
78
+ # :nodoc:
79
+ # @api private
80
+ def svg(file, svg_args, box, paint, trans)
81
+ Squib.logger.debug {"Rendering: #{file}, id: #{id} @#{x},#{y} #{width}x#{height}, alpha: #{alpha}, blend: #{blend}, angle: #{angle}, mask: #{mask}"}
82
+ Squib.logger.warn 'Both an SVG file and SVG data were specified' unless file.to_s.empty? || svg_args.data.to_s.empty?
83
+ return if (file.nil? or file.eql? '') and svg_args.data.nil? # nothing specified TODO Move this out to arg validator
84
+ svg_args.data = File.read(file) if svg_args.data.to_s.empty?
85
+ begin
86
+ svg = Rsvg::Handle.new_from_data(svg_args.data)
87
+ rescue Rsvg::Error::Failed
88
+ Squib.logger.error "Invalid SVG data. Is '#{file}' a valid svg file?"
89
+ return
90
+ end
91
+ box.width = svg.width if box.width == :native
92
+ box.height = svg.height if box.height == :native
93
+ box.width = svg.width.to_f * box.height.to_f / svg.height.to_f if box.width == :scale
94
+ box.height = svg.height.to_f * box.width.to_f / svg.width.to_f if box.height == :scale
95
+ scale_width = box.width.to_f / svg.width.to_f
96
+ scale_height = box.height.to_f / svg.height.to_f
97
+ use_cairo do |cc|
98
+ cc.translate(box.x, box.y)
99
+ cc.flip(trans.flip_vertical, trans.flip_horizontal, box.width / 2, box.height / 2)
100
+ cc.rotate(trans.angle)
101
+ cc.scale(scale_width, scale_height)
102
+
103
+ trans.crop_width = box.width if trans.crop_width == :native
104
+ trans.crop_height = box.height if trans.crop_height == :native
105
+ cc.rounded_rectangle(0, 0, trans.crop_width / scale_width, trans.crop_height / scale_height, trans.crop_corner_x_radius, trans.crop_corner_y_radius)
106
+ cc.clip
107
+ cc.translate(-trans.crop_x, -trans.crop_y)
108
+
109
+ cc.operator = paint.blend unless paint.blend == :none
110
+ if paint.mask.to_s.empty?
111
+ cc.render_rsvg_handle(svg, id: svg_args.id)
112
+ else
113
+ tmp = Cairo::ImageSurface.new(box.width / scale_width, box.height / scale_height)
114
+ tmp_cc = Cairo::Context.new(tmp)
115
+ tmp_cc.render_rsvg_handle(svg, id: svg_args.id)
116
+ cc.set_source_squibcolor(paint.mask)
117
+ cc.mask(tmp, 0, 0)
118
+ end
119
+ end
120
+ end
121
+
122
+ end
123
+ end
@@ -1,77 +1,77 @@
1
- module Squib
2
- class Deck
3
-
4
- # :nodoc:
5
- # @api private
6
- def render_sheet(range, batch, sheet)
7
- rotate = batch.rotate.any? true # either rotate all or none
8
- w,h,sheet_width,sheet_height = compute_dimensions(sheet, rotate)
9
- cc = Cairo::Context.new(Cairo::ImageSurface.new(sheet_width, sheet_height))
10
- num_this_sheet = 0
11
- sheet_num = 0
12
- y = sheet.margin
13
- x = sheet.rtl ? rtl_start_x(sheet_width, sheet, w) : sheet.margin
14
- @progress_bar.start("Saving PNG sheet to #{batch.summary}", @cards.size + 1) do |bar|
15
- range.each do |i|
16
- if num_this_sheet >= (sheet.columns * sheet.rows) # new sheet
17
- filename = batch.full_filename(sheet_num)
18
- cc.target.write_to_png(filename)
19
- new_sheet = false
20
- num_this_sheet = 0
21
- sheet_num += 1
22
- y = sheet.margin
23
- x = sheet.rtl ? rtl_start_x(sheet_width, sheet, w) : sheet.margin
24
- cc = Cairo::Context.new(Cairo::ImageSurface.new(sheet_width, sheet_height))
25
- end
26
- surface = preprocess(@cards[i].cairo_surface,
27
- sheet.trim, w, h,
28
- rotate, batch.angle[i])
29
- cc.set_source(surface, x, y)
30
- cc.paint
31
- num_this_sheet += 1
32
- x += (w + sheet.gap) * (sheet.rtl ? -1 : 1)
33
- if num_this_sheet % sheet.columns == 0 # new row
34
- x = sheet.rtl ? rtl_start_x(sheet_width, sheet, w) : sheet.margin
35
- y += h + sheet.gap
36
- end
37
- bar.increment
38
- end
39
- cc.target.write_to_png(batch.full_filename(sheet_num))
40
- end
41
- end
42
-
43
- def compute_dimensions(sheet, rotate)
44
- w,h = rotate ? [@height,@width] : [@width,@height]
45
- w -= 2 * sheet.trim
46
- h -= 2 * sheet.trim
47
- sheet_width = (sheet.columns * (w + 2 * sheet.gap)) + (2 * sheet.margin)
48
- sheet_height = (sheet.rows * (h + 2 * sheet.gap)) + (2 * sheet.margin)
49
- return [w, h, sheet_width, sheet_height]
50
- end
51
-
52
- def rtl_start_x(sheet_width, sheet, w)
53
- return sheet_width - sheet.margin - sheet.gap - w
54
- end
55
-
56
- # Return a new Cairo::ImageSurface that is trimmed and rotated
57
- # from the original
58
- # :nodoc:
59
- # @api private
60
- def preprocess(surface, trim, w, h, rotate, angle)
61
- if trim > 0 || rotate
62
- tmp = Cairo::ImageSurface.new(w, h)
63
- cc = Cairo::Context.new(tmp)
64
- if rotate
65
- cc.translate w * 0.5, h * 0.5
66
- cc.rotate angle
67
- cc.translate h * -0.5, w * -0.5
68
- end
69
- cc.set_source(surface, -1 * trim, -1 * trim)
70
- cc.paint
71
- surface = tmp
72
- end
73
- surface
74
- end
75
-
76
- end
77
- end
1
+ module Squib
2
+ class Deck
3
+
4
+ # :nodoc:
5
+ # @api private
6
+ def render_sheet(range, batch, sheet)
7
+ rotate = batch.rotate.any? true # either rotate all or none
8
+ w,h,sheet_width,sheet_height = compute_dimensions(sheet, rotate)
9
+ cc = Cairo::Context.new(Cairo::ImageSurface.new(sheet_width, sheet_height))
10
+ num_this_sheet = 0
11
+ sheet_num = 0
12
+ y = sheet.margin
13
+ x = sheet.rtl ? rtl_start_x(sheet_width, sheet, w) : sheet.margin
14
+ @progress_bar.start("Saving PNG sheet to #{batch.summary}", @cards.size + 1) do |bar|
15
+ range.each do |i|
16
+ if num_this_sheet >= (sheet.columns * sheet.rows) # new sheet
17
+ filename = batch.full_filename(sheet_num)
18
+ cc.target.write_to_png(filename)
19
+ new_sheet = false
20
+ num_this_sheet = 0
21
+ sheet_num += 1
22
+ y = sheet.margin
23
+ x = sheet.rtl ? rtl_start_x(sheet_width, sheet, w) : sheet.margin
24
+ cc = Cairo::Context.new(Cairo::ImageSurface.new(sheet_width, sheet_height))
25
+ end
26
+ surface = preprocess(@cards[i].cairo_surface,
27
+ sheet.trim, w, h,
28
+ rotate, batch.angle[i])
29
+ cc.set_source(surface, x, y)
30
+ cc.paint
31
+ num_this_sheet += 1
32
+ x += (w + sheet.gap) * (sheet.rtl ? -1 : 1)
33
+ if num_this_sheet % sheet.columns == 0 # new row
34
+ x = sheet.rtl ? rtl_start_x(sheet_width, sheet, w) : sheet.margin
35
+ y += h + sheet.gap
36
+ end
37
+ bar.increment
38
+ end
39
+ cc.target.write_to_png(batch.full_filename(sheet_num))
40
+ end
41
+ end
42
+
43
+ def compute_dimensions(sheet, rotate)
44
+ w,h = rotate ? [@height,@width] : [@width,@height]
45
+ w -= 2 * sheet.trim
46
+ h -= 2 * sheet.trim
47
+ sheet_width = (sheet.columns * (w + 2 * sheet.gap)) + (2 * sheet.margin)
48
+ sheet_height = (sheet.rows * (h + 2 * sheet.gap)) + (2 * sheet.margin)
49
+ return [w, h, sheet_width, sheet_height]
50
+ end
51
+
52
+ def rtl_start_x(sheet_width, sheet, w)
53
+ return sheet_width - sheet.margin - sheet.gap - w
54
+ end
55
+
56
+ # Return a new Cairo::ImageSurface that is trimmed and rotated
57
+ # from the original
58
+ # :nodoc:
59
+ # @api private
60
+ def preprocess(surface, trim, w, h, rotate, angle)
61
+ if trim > 0 || rotate
62
+ tmp = Cairo::ImageSurface.new(w, h)
63
+ cc = Cairo::Context.new(tmp)
64
+ if rotate
65
+ cc.translate w * 0.5, h * 0.5
66
+ cc.rotate angle
67
+ cc.translate h * -0.5, w * -0.5
68
+ end
69
+ cc.set_source(surface, -1 * trim, -1 * trim)
70
+ cc.paint
71
+ surface = tmp
72
+ end
73
+ surface
74
+ end
75
+
76
+ end
77
+ end
@@ -1,91 +1,91 @@
1
- require_relative 'cairo_context_wrapper'
2
-
3
- module Squib
4
- class Card
5
-
6
- # :nodoc:
7
- # @api private
8
- def save_png(batch, shadow)
9
- surface = if preprocess_save?(batch, shadow)
10
- w, h = compute_dimensions(batch.rotate, batch.trim)
11
- preprocessed_save(w, h, batch, shadow)
12
- else
13
- @cairo_surface
14
- end
15
- write_png(surface, index, batch)
16
- end
17
-
18
- # :nodoc:
19
- # @api private
20
- def preprocess_save?(batch, shadow)
21
- batch.rotate != false || batch.trim > 0 || !(shadow.shadow_radius.nil?)
22
- end
23
-
24
- def compute_dimensions(rotate, trim)
25
- if rotate
26
- [ @height - 2 * trim, @width - 2 * trim ]
27
- else
28
- [ @width - 2 * trim, @height - 2 * trim ]
29
- end
30
- end
31
-
32
- def preprocessed_save(w, h, batch, shadow)
33
- new_cc = Cairo::Context.new(Cairo::ImageSurface.new(w, h))
34
- trim_radius = batch.trim_radius
35
- if batch.rotate != false
36
- new_cc.translate w * 0.5, h * 0.5
37
- new_cc.rotate batch.angle
38
- new_cc.translate h * -0.5, w * -0.5
39
- new_cc.rounded_rectangle(0, 0, h, w, trim_radius, trim_radius)
40
- else
41
- new_cc.rounded_rectangle(0, 0, w, h, trim_radius, trim_radius)
42
- end
43
- new_cc.clip
44
- new_cc.set_source(@cairo_surface, -batch.trim, -batch.trim)
45
- new_cc.paint
46
- new_cc.reset_clip
47
- new_cc = drop_shadow(new_cc, shadow, batch) unless shadow.shadow_radius.nil?
48
- return new_cc.target
49
- end
50
-
51
- # pseudo-blur behave weirdly with a radius of 0 - wrapping
52
- def blur(cc, r, &block)
53
- if r == 0
54
- yield(block)
55
- else
56
- cc.pseudo_blur(r, &block)
57
- end
58
- end
59
-
60
- def drop_shadow(cc, s, batch)
61
- off_x = s.shadow_offset_x
62
- off_y = s.shadow_offset_y
63
- s_trim = s.shadow_trim
64
- s_rad = s.shadow_radius
65
- new_w = cc.target.width + off_x + 3 * s_rad - (2 * s_trim)
66
- new_h = cc.target.height + off_y + 3 * s_rad - (2 * s_trim)
67
- new_cc = Squib::Graphics::CairoContextWrapper.new(
68
- Cairo::Context.new(Cairo::ImageSurface.new(new_w, new_h)))
69
- blur(new_cc, s_rad) do
70
- # fill in with shadow color
71
- new_cc.set_source_squibcolor s.shadow_color
72
- new_cc.rectangle 0, 0, new_cc.target.width, new_cc.target.height
73
- new_cc.fill
74
- # then, paint but blend with :dest_in to get a shadow-shaped drawing
75
- new_cc.set_source cc.target, s_rad + off_x, s_rad + off_y
76
- new_cc.operator = :dest_in # see https://www.cairographics.org/operators/
77
- new_cc.paint
78
- end
79
- new_cc.set_source cc.target, s_rad, s_rad
80
- new_cc.operator = :over
81
- new_cc.paint
82
- return new_cc
83
- end
84
-
85
- def write_png(surface, i, b)
86
- filename = "#{b.dir}/#{b.prefix}#{b.count_format % i}#{b.suffix}.png"
87
- surface.write_to_png filename
88
- end
89
-
90
- end
91
- end
1
+ require_relative 'cairo_context_wrapper'
2
+
3
+ module Squib
4
+ class Card
5
+
6
+ # :nodoc:
7
+ # @api private
8
+ def save_png(batch, shadow)
9
+ surface = if preprocess_save?(batch, shadow)
10
+ w, h = compute_dimensions(batch.rotate, batch.trim)
11
+ preprocessed_save(w, h, batch, shadow)
12
+ else
13
+ @cairo_surface
14
+ end
15
+ write_png(surface, index, batch)
16
+ end
17
+
18
+ # :nodoc:
19
+ # @api private
20
+ def preprocess_save?(batch, shadow)
21
+ batch.rotate != false || batch.trim > 0 || !(shadow.shadow_radius.nil?)
22
+ end
23
+
24
+ def compute_dimensions(rotate, trim)
25
+ if rotate
26
+ [ @height - 2 * trim, @width - 2 * trim ]
27
+ else
28
+ [ @width - 2 * trim, @height - 2 * trim ]
29
+ end
30
+ end
31
+
32
+ def preprocessed_save(w, h, batch, shadow)
33
+ new_cc = Cairo::Context.new(Cairo::ImageSurface.new(w, h))
34
+ trim_radius = batch.trim_radius
35
+ if batch.rotate != false
36
+ new_cc.translate w * 0.5, h * 0.5
37
+ new_cc.rotate batch.angle
38
+ new_cc.translate h * -0.5, w * -0.5
39
+ new_cc.rounded_rectangle(0, 0, h, w, trim_radius, trim_radius)
40
+ else
41
+ new_cc.rounded_rectangle(0, 0, w, h, trim_radius, trim_radius)
42
+ end
43
+ new_cc.clip
44
+ new_cc.set_source(@cairo_surface, -batch.trim, -batch.trim)
45
+ new_cc.paint
46
+ new_cc.reset_clip
47
+ new_cc = drop_shadow(new_cc, shadow, batch) unless shadow.shadow_radius.nil?
48
+ return new_cc.target
49
+ end
50
+
51
+ # pseudo-blur behave weirdly with a radius of 0 - wrapping
52
+ def blur(cc, r, &block)
53
+ if r == 0
54
+ yield(block)
55
+ else
56
+ cc.pseudo_blur(r, &block)
57
+ end
58
+ end
59
+
60
+ def drop_shadow(cc, s, batch)
61
+ off_x = s.shadow_offset_x
62
+ off_y = s.shadow_offset_y
63
+ s_trim = s.shadow_trim
64
+ s_rad = s.shadow_radius
65
+ new_w = cc.target.width + off_x + 3 * s_rad - (2 * s_trim)
66
+ new_h = cc.target.height + off_y + 3 * s_rad - (2 * s_trim)
67
+ new_cc = Squib::Graphics::CairoContextWrapper.new(
68
+ Cairo::Context.new(Cairo::ImageSurface.new(new_w, new_h)))
69
+ blur(new_cc, s_rad) do
70
+ # fill in with shadow color
71
+ new_cc.set_source_squibcolor s.shadow_color
72
+ new_cc.rectangle 0, 0, new_cc.target.width, new_cc.target.height
73
+ new_cc.fill
74
+ # then, paint but blend with :dest_in to get a shadow-shaped drawing
75
+ new_cc.set_source cc.target, s_rad + off_x, s_rad + off_y
76
+ new_cc.operator = :dest_in # see https://www.cairographics.org/operators/
77
+ new_cc.paint
78
+ end
79
+ new_cc.set_source cc.target, s_rad, s_rad
80
+ new_cc.operator = :over
81
+ new_cc.paint
82
+ return new_cc
83
+ end
84
+
85
+ def write_png(surface, i, b)
86
+ filename = "#{b.dir}/#{b.prefix}#{b.count_format % i}#{b.suffix}.png"
87
+ surface.write_to_png filename
88
+ end
89
+
90
+ end
91
+ end