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,90 +1,90 @@
1
- require_relative '../args/sheet'
2
-
3
- module Squib
4
- module Graphics
5
- class SavePDF
6
-
7
- def initialize(deck)
8
- @deck = deck
9
- end
10
-
11
- # :nodoc:
12
- # @api private
13
- def render_pdf(range, sheet)
14
- cc = init_cc(sheet)
15
- cc.scale(POINTS_PER_IN / @deck.dpi, POINTS_PER_IN / @deck.dpi) # for bug #62
16
- card_width = @deck.width - 2 * sheet.trim
17
- card_height = @deck.height - 2 * sheet.trim
18
- start_x_pos = sheet.rtl ? sheet.width - sheet.margin - card_width - 2 * sheet.trim : sheet.margin
19
- x_increment = (card_width + sheet.gap) * (sheet.rtl ? -1 : 1)
20
- y = sheet.margin
21
- x = start_x_pos
22
- track_progress(range, sheet) do |bar|
23
- range.each do |i|
24
- card = @deck.cards[i]
25
- cc.translate(x, y)
26
- cc.rectangle(sheet.trim, sheet.trim, card_width, card_height)
27
- cc.clip
28
- case card.backend.downcase.to_sym
29
- when :memory
30
- cc.set_source(card.cairo_surface, 0, 0)
31
- cc.paint
32
- when :svg
33
- card.cairo_surface.finish
34
- cc.save
35
- cc.scale(0.8, 0.8) # I really don't know why I needed to do this at all. But 0.8 is the magic number to get this to scale right
36
- cc.render_rsvg_handle(Rsvg::Handle.new_from_file(card.svgfile))
37
- cc.restore
38
- else
39
- abort "No such back end supported for save_pdf: #{backend}"
40
- end
41
- bar.increment
42
- cc.reset_clip
43
- cc.translate(-x, -y)
44
-
45
- draw_crop_marks(cc, x, y, sheet)
46
- x += x_increment
47
- if (x > (sheet.width - card_width - sheet.margin)) or (x < sheet.margin)
48
- x = start_x_pos
49
- y += card.height + sheet.gap - 2 * sheet.trim
50
- if y > (sheet.height - card_height - sheet.margin)
51
- cc.show_page # next page
52
- y = sheet.margin
53
- x = start_x_pos
54
- end
55
- end
56
- end
57
- end
58
- cc.target.finish
59
- end
60
-
61
- private
62
-
63
- # Initialize the Cairo Context
64
- def init_cc(sheet)
65
- Cairo::Context.new(Cairo::PDFSurface.new(
66
- "#{sheet.dir}/#{sheet.file}",
67
- sheet.width * POINTS_PER_IN / @deck.dpi, #PDF thinks in 72 DPI "points"
68
- sheet.height * POINTS_PER_IN / @deck.dpi)
69
- )
70
- end
71
-
72
- def track_progress(range, sheet)
73
- msg = "Saving PDF to #{sheet.full_filename}"
74
- @deck.progress_bar.start(msg, range.size) { |bar| yield(bar) }
75
- end
76
-
77
- def draw_crop_marks(cc, x, y, sheet)
78
- sheet.crop_coords(x, y, @deck.width, @deck.height).each do |coord|
79
- cc.move_to(coord[:x1], coord[:y1])
80
- cc.line_to(coord[:x2], coord[:y2])
81
- cc.set_source_color(sheet.crop_stroke_color)
82
- cc.set_dash(sheet.crop_stroke_dash)
83
- cc.set_line_width(sheet.crop_stroke_width)
84
- cc.stroke
85
- end
86
- end
87
-
88
- end
89
- end
90
- end
1
+ require_relative '../args/sheet'
2
+
3
+ module Squib
4
+ module Graphics
5
+ class SavePDF
6
+
7
+ def initialize(deck)
8
+ @deck = deck
9
+ end
10
+
11
+ # :nodoc:
12
+ # @api private
13
+ def render_pdf(range, sheet)
14
+ cc = init_cc(sheet)
15
+ cc.scale(POINTS_PER_IN / @deck.dpi, POINTS_PER_IN / @deck.dpi) # for bug #62
16
+ card_width = @deck.width - 2 * sheet.trim
17
+ card_height = @deck.height - 2 * sheet.trim
18
+ start_x_pos = sheet.rtl ? sheet.width - sheet.margin - card_width - 2 * sheet.trim : sheet.margin
19
+ x_increment = (card_width + sheet.gap) * (sheet.rtl ? -1 : 1)
20
+ y = sheet.margin
21
+ x = start_x_pos
22
+ track_progress(range, sheet) do |bar|
23
+ range.each do |i|
24
+ card = @deck.cards[i]
25
+ cc.translate(x, y)
26
+ cc.rectangle(sheet.trim, sheet.trim, card_width, card_height)
27
+ cc.clip
28
+ case card.backend.downcase.to_sym
29
+ when :memory
30
+ cc.set_source(card.cairo_surface, 0, 0)
31
+ cc.paint
32
+ when :svg
33
+ card.cairo_surface.finish
34
+ cc.save
35
+ cc.scale(0.8, 0.8) # I really don't know why I needed to do this at all. But 0.8 is the magic number to get this to scale right
36
+ cc.render_rsvg_handle(Rsvg::Handle.new_from_file(card.svgfile))
37
+ cc.restore
38
+ else
39
+ abort "No such back end supported for save_pdf: #{backend}"
40
+ end
41
+ bar.increment
42
+ cc.reset_clip
43
+ cc.translate(-x, -y)
44
+
45
+ draw_crop_marks(cc, x, y, sheet)
46
+ x += x_increment
47
+ if (x > (sheet.width - card_width - sheet.margin)) or (x < sheet.margin)
48
+ x = start_x_pos
49
+ y += card.height + sheet.gap - 2 * sheet.trim
50
+ if y > (sheet.height - card_height - sheet.margin)
51
+ cc.show_page # next page
52
+ y = sheet.margin
53
+ x = start_x_pos
54
+ end
55
+ end
56
+ end
57
+ end
58
+ cc.target.finish
59
+ end
60
+
61
+ private
62
+
63
+ # Initialize the Cairo Context
64
+ def init_cc(sheet)
65
+ Cairo::Context.new(Cairo::PDFSurface.new(
66
+ "#{sheet.dir}/#{sheet.file}",
67
+ sheet.width * POINTS_PER_IN / @deck.dpi, #PDF thinks in 72 DPI "points"
68
+ sheet.height * POINTS_PER_IN / @deck.dpi)
69
+ )
70
+ end
71
+
72
+ def track_progress(range, sheet)
73
+ msg = "Saving PDF to #{sheet.full_filename}"
74
+ @deck.progress_bar.start(msg, range.size) { |bar| yield(bar) }
75
+ end
76
+
77
+ def draw_crop_marks(cc, x, y, sheet)
78
+ sheet.crop_coords(x, y, @deck.width, @deck.height).each do |coord|
79
+ cc.move_to(coord[:x1], coord[:y1])
80
+ cc.line_to(coord[:x2], coord[:y2])
81
+ cc.set_source_color(sheet.crop_stroke_color)
82
+ cc.set_dash(sheet.crop_stroke_dash)
83
+ cc.set_line_width(sheet.crop_stroke_width)
84
+ cc.stroke
85
+ end
86
+ end
87
+
88
+ end
89
+ end
90
+ end
@@ -1,231 +1,231 @@
1
- module Squib
2
- module Graphics
3
- # Helper class to generate templated sheet.
4
- class SaveSprue
5
- def initialize(deck, tmpl, sheet_args)
6
- @deck = deck
7
- @tmpl = tmpl
8
- @page_number = 0
9
- @sheet_args = sheet_args # might be Args::Sheet or Args::SaveBatch
10
- @overlay_lines = @tmpl.crop_lines.select do |line|
11
- line['overlay_on_cards']
12
- end
13
- end
14
-
15
- def render_sheet(range)
16
- cc = init_cc
17
- cc.set_source_color(:white) # white backdrop TODO make option
18
- cc.paint
19
- slots = @tmpl.cards
20
- per_sheet = slots.size
21
- check_oversized_card
22
-
23
- draw_overlay_below_cards cc if range.size
24
-
25
- track_progress(range) do |bar|
26
- range.each do |i|
27
- cc = next_page_if_needed(cc, i, per_sheet)
28
-
29
- card = @deck.cards[i]
30
- slot = slots[i % per_sheet]
31
-
32
- draw_card cc, card,
33
- slot['x'] - @sheet_args.trim,
34
- slot['y'] - @sheet_args.trim,
35
- slot['rotate'],
36
- slot['flip_vertical'], slot['flip_horizontal'],
37
- @sheet_args.trim, @sheet_args.trim_radius
38
- bar.increment
39
- end
40
-
41
- draw_overlay_above_cards cc
42
- draw_final_page cc # See bug #320
43
- end
44
- end
45
-
46
- protected
47
-
48
- # Initialize the Cairo Context
49
- def init_cc
50
- raise NotImplementedError
51
- end
52
-
53
- def draw_page(cc)
54
- raise NotImplementedError
55
- end
56
-
57
- def full_filename
58
- raise NotImplementedError
59
- end
60
-
61
- private
62
-
63
- def next_page_if_needed(cc, i, per_sheet)
64
- return cc unless (i != 0) && (i % per_sheet) == 0
65
-
66
- draw_overlay_above_cards cc
67
- cc = draw_page cc
68
- draw_overlay_below_cards cc
69
- @page_number += 1
70
- cc
71
- end
72
-
73
- def track_progress(range)
74
- msg = "Saving templated sheet to #{full_filename}"
75
- @deck.progress_bar.start(msg, range.size) { |bar| yield(bar) }
76
- end
77
-
78
- def draw_overlay_below_cards(cc)
79
- if @tmpl.crop_line_overlay == :on_margin
80
- add_margin_overlay_clip_mask cc
81
- cc.clip
82
- draw_crop_line cc, @tmpl.crop_lines
83
- cc.reset_clip
84
- elsif @tmpl.crop_line_overlay == :beneath_cards
85
- draw_crop_line cc, @tmpl.crop_lines
86
- end
87
- end
88
-
89
- def draw_overlay_above_cards(cc)
90
- if @tmpl.crop_line_overlay == :overlay_on_cards
91
- draw_crop_line cc, @tmpl.crop_lines
92
- else
93
- draw_crop_line cc, @overlay_lines
94
- end
95
- end
96
-
97
- def add_margin_overlay_clip_mask(cc)
98
- margin = @tmpl.margin
99
- cc.new_path
100
- cc.rectangle(
101
- margin[:left], margin[:top],
102
- margin[:right] - margin[:left],
103
- margin[:bottom] - margin[:top]
104
- )
105
- cc.new_sub_path
106
- cc.move_to @tmpl.sheet_width, 0
107
- cc.line_to 0, 0
108
- cc.line_to 0, @tmpl.sheet_height
109
- cc.line_to @tmpl.sheet_width, @tmpl.sheet_height
110
- cc.close_path
111
- end
112
-
113
- def draw_crop_line(cc, crop_lines)
114
- crop_lines.each do |line|
115
- cc.move_to line['line'].x1, line['line'].y1
116
- cc.line_to line['line'].x2, line['line'].y2
117
- cc.set_source_color line['color']
118
- cc.set_line_width line['width']
119
- cc.set_dash(line['style'].pattern) if line['style'].pattern
120
- cc.stroke
121
- end
122
- end
123
-
124
- def check_oversized_card
125
- Squib.logger.warn {
126
- "Card size is larger than sprue's expected card size "\
127
- "of #{@tmpl.card_width}x#{@tmpl.card_height}. Cards may overlap."
128
- } if (@deck.width - 2.0 * @sheet_args.trim) > @tmpl.card_width ||
129
- (@deck.height - 2.0 * @sheet_args.trim) > @tmpl.card_height
130
- end
131
-
132
- def draw_card(cc, card, x, y, angle, flip_v, flip_h, trim, trim_radius)
133
- # Compute the true size of the card after trimming
134
- w = @deck.width - 2.0 * trim
135
- h = @deck.height - 2.0 * trim
136
-
137
- # Normalize the angles first
138
- # TODO do this in the args class
139
- angle = angle % (2 * Math::PI)
140
- angle = 2 * Math::PI - angle if angle < 0
141
-
142
- # Perform the actual rotation and drawing
143
- mat = cc.matrix # Save the transformation matrix to revert later
144
- cc.translate x, y
145
- cc.translate @deck.width / 2.0, @deck.height / 2.0
146
- cc.flip(flip_v, flip_h, 0, 0)
147
- cc.rotate angle
148
- cc.translate -@deck.width / 2.0, -@deck.height / 2.0
149
- cc.rounded_rectangle(trim, trim, w, h, trim_radius, trim_radius) # clip
150
- cc.clip
151
- cc.set_source card.cairo_surface, 0, 0
152
- cc.matrix = mat
153
- cc.paint
154
- cc.reset_clip
155
- end
156
- end
157
-
158
- # Templated sheet renderer in PDF format.
159
- class SaveSpruePDF < SaveSprue
160
- def init_cc
161
- ratio = 72.0 / @deck.dpi
162
-
163
- slots = @tmpl.cards
164
- per_sheet = slots.size
165
-
166
- surface = if per_sheet == 1
167
- Cairo::PDFSurface.new(
168
- full_filename,
169
- (@tmpl.sheet_width - 2 * @sheet_args.trim) * ratio,
170
- (@tmpl.sheet_height - 2 *@sheet_args.trim) * ratio
171
- )
172
- else
173
- Cairo::PDFSurface.new(
174
- full_filename,
175
- @tmpl.sheet_width * ratio,
176
- @tmpl.sheet_height * ratio
177
- )
178
- end
179
-
180
- cc = CairoContextWrapper.new(Cairo::Context.new(surface))
181
- # cc = Cairo::Context.new(surface)
182
- cc.scale(72.0 / @deck.dpi, 72.0 / @deck.dpi) # make it like pixels
183
- cc
184
- end
185
-
186
- def draw_page(cc)
187
- cc.show_page
188
- cc.set_source_color(:white) # white backdrop TODO make option
189
- cc.paint
190
- cc
191
- end
192
-
193
- def draw_final_page(cc)
194
- # PDF doesn't need to create a last page. See bug #320
195
- cc.target.finish
196
- end
197
-
198
- def full_filename
199
- @sheet_args.full_filename
200
- end
201
- end
202
-
203
- # Templated sheet renderer in PNG format.
204
- class SaveSpruePNG < SaveSprue
205
- def init_cc
206
- surface = Cairo::ImageSurface.new @tmpl.sheet_width, @tmpl.sheet_height
207
- CairoContextWrapper.new(Cairo::Context.new(surface))
208
- # Cairo::Context.new(surface)
209
- end
210
-
211
- def draw_page(cc)
212
- cc.target.write_to_png(full_filename)
213
- init_cc
214
- cc.set_source_color(:white) # white backdrop TODO make option
215
- cc.paint
216
- cc
217
- end
218
-
219
- # The last page always gets written out for PNGs because they are separate
220
- # files and don't get "flushed" automatically. See bug #320.
221
- def draw_final_page(cc)
222
- draw_page cc
223
- cc.target.finish
224
- end
225
-
226
- def full_filename
227
- @sheet_args.full_filename @page_number
228
- end
229
- end
230
- end
231
- end
1
+ module Squib
2
+ module Graphics
3
+ # Helper class to generate templated sheet.
4
+ class SaveSprue
5
+ def initialize(deck, tmpl, sheet_args)
6
+ @deck = deck
7
+ @tmpl = tmpl
8
+ @page_number = 0
9
+ @sheet_args = sheet_args # might be Args::Sheet or Args::SaveBatch
10
+ @overlay_lines = @tmpl.crop_lines.select do |line|
11
+ line['overlay_on_cards']
12
+ end
13
+ end
14
+
15
+ def render_sheet(range)
16
+ cc = init_cc
17
+ cc.set_source_color(:white) # white backdrop TODO make option
18
+ cc.paint
19
+ slots = @tmpl.cards
20
+ per_sheet = slots.size
21
+ check_oversized_card
22
+
23
+ draw_overlay_below_cards cc if range.size
24
+
25
+ track_progress(range) do |bar|
26
+ range.each do |i|
27
+ cc = next_page_if_needed(cc, i, per_sheet)
28
+
29
+ card = @deck.cards[i]
30
+ slot = slots[i % per_sheet]
31
+
32
+ draw_card cc, card,
33
+ slot['x'] - @sheet_args.trim,
34
+ slot['y'] - @sheet_args.trim,
35
+ slot['rotate'],
36
+ slot['flip_vertical'], slot['flip_horizontal'],
37
+ @sheet_args.trim, @sheet_args.trim_radius
38
+ bar.increment
39
+ end
40
+
41
+ draw_overlay_above_cards cc
42
+ draw_final_page cc # See bug #320
43
+ end
44
+ end
45
+
46
+ protected
47
+
48
+ # Initialize the Cairo Context
49
+ def init_cc
50
+ raise NotImplementedError
51
+ end
52
+
53
+ def draw_page(cc)
54
+ raise NotImplementedError
55
+ end
56
+
57
+ def full_filename
58
+ raise NotImplementedError
59
+ end
60
+
61
+ private
62
+
63
+ def next_page_if_needed(cc, i, per_sheet)
64
+ return cc unless (i != 0) && (i % per_sheet) == 0
65
+
66
+ draw_overlay_above_cards cc
67
+ cc = draw_page cc
68
+ draw_overlay_below_cards cc
69
+ @page_number += 1
70
+ cc
71
+ end
72
+
73
+ def track_progress(range)
74
+ msg = "Saving templated sheet to #{full_filename}"
75
+ @deck.progress_bar.start(msg, range.size) { |bar| yield(bar) }
76
+ end
77
+
78
+ def draw_overlay_below_cards(cc)
79
+ if @tmpl.crop_line_overlay == :on_margin
80
+ add_margin_overlay_clip_mask cc
81
+ cc.clip
82
+ draw_crop_line cc, @tmpl.crop_lines
83
+ cc.reset_clip
84
+ elsif @tmpl.crop_line_overlay == :beneath_cards
85
+ draw_crop_line cc, @tmpl.crop_lines
86
+ end
87
+ end
88
+
89
+ def draw_overlay_above_cards(cc)
90
+ if @tmpl.crop_line_overlay == :overlay_on_cards
91
+ draw_crop_line cc, @tmpl.crop_lines
92
+ else
93
+ draw_crop_line cc, @overlay_lines
94
+ end
95
+ end
96
+
97
+ def add_margin_overlay_clip_mask(cc)
98
+ margin = @tmpl.margin
99
+ cc.new_path
100
+ cc.rectangle(
101
+ margin[:left], margin[:top],
102
+ margin[:right] - margin[:left],
103
+ margin[:bottom] - margin[:top]
104
+ )
105
+ cc.new_sub_path
106
+ cc.move_to @tmpl.sheet_width, 0
107
+ cc.line_to 0, 0
108
+ cc.line_to 0, @tmpl.sheet_height
109
+ cc.line_to @tmpl.sheet_width, @tmpl.sheet_height
110
+ cc.close_path
111
+ end
112
+
113
+ def draw_crop_line(cc, crop_lines)
114
+ crop_lines.each do |line|
115
+ cc.move_to line['line'].x1, line['line'].y1
116
+ cc.line_to line['line'].x2, line['line'].y2
117
+ cc.set_source_color line['color']
118
+ cc.set_line_width line['width']
119
+ cc.set_dash(line['style'].pattern) if line['style'].pattern
120
+ cc.stroke
121
+ end
122
+ end
123
+
124
+ def check_oversized_card
125
+ Squib.logger.warn {
126
+ "Card size is larger than sprue's expected card size "\
127
+ "of #{@tmpl.card_width}x#{@tmpl.card_height}. Cards may overlap."
128
+ } if (@deck.width - 2.0 * @sheet_args.trim) > @tmpl.card_width ||
129
+ (@deck.height - 2.0 * @sheet_args.trim) > @tmpl.card_height
130
+ end
131
+
132
+ def draw_card(cc, card, x, y, angle, flip_v, flip_h, trim, trim_radius)
133
+ # Compute the true size of the card after trimming
134
+ w = @deck.width - 2.0 * trim
135
+ h = @deck.height - 2.0 * trim
136
+
137
+ # Normalize the angles first
138
+ # TODO do this in the args class
139
+ angle = angle % (2 * Math::PI)
140
+ angle = 2 * Math::PI - angle if angle < 0
141
+
142
+ # Perform the actual rotation and drawing
143
+ mat = cc.matrix # Save the transformation matrix to revert later
144
+ cc.translate x, y
145
+ cc.translate @deck.width / 2.0, @deck.height / 2.0
146
+ cc.flip(flip_v, flip_h, 0, 0)
147
+ cc.rotate angle
148
+ cc.translate -@deck.width / 2.0, -@deck.height / 2.0
149
+ cc.rounded_rectangle(trim, trim, w, h, trim_radius, trim_radius) # clip
150
+ cc.clip
151
+ cc.set_source card.cairo_surface, 0, 0
152
+ cc.matrix = mat
153
+ cc.paint
154
+ cc.reset_clip
155
+ end
156
+ end
157
+
158
+ # Templated sheet renderer in PDF format.
159
+ class SaveSpruePDF < SaveSprue
160
+ def init_cc
161
+ ratio = 72.0 / @deck.dpi
162
+
163
+ slots = @tmpl.cards
164
+ per_sheet = slots.size
165
+
166
+ surface = if per_sheet == 1
167
+ Cairo::PDFSurface.new(
168
+ full_filename,
169
+ (@tmpl.sheet_width - 2 * @sheet_args.trim) * ratio,
170
+ (@tmpl.sheet_height - 2 *@sheet_args.trim) * ratio
171
+ )
172
+ else
173
+ Cairo::PDFSurface.new(
174
+ full_filename,
175
+ @tmpl.sheet_width * ratio,
176
+ @tmpl.sheet_height * ratio
177
+ )
178
+ end
179
+
180
+ cc = CairoContextWrapper.new(Cairo::Context.new(surface))
181
+ # cc = Cairo::Context.new(surface)
182
+ cc.scale(72.0 / @deck.dpi, 72.0 / @deck.dpi) # make it like pixels
183
+ cc
184
+ end
185
+
186
+ def draw_page(cc)
187
+ cc.show_page
188
+ cc.set_source_color(:white) # white backdrop TODO make option
189
+ cc.paint
190
+ cc
191
+ end
192
+
193
+ def draw_final_page(cc)
194
+ # PDF doesn't need to create a last page. See bug #320
195
+ cc.target.finish
196
+ end
197
+
198
+ def full_filename
199
+ @sheet_args.full_filename
200
+ end
201
+ end
202
+
203
+ # Templated sheet renderer in PNG format.
204
+ class SaveSpruePNG < SaveSprue
205
+ def init_cc
206
+ surface = Cairo::ImageSurface.new @tmpl.sheet_width, @tmpl.sheet_height
207
+ CairoContextWrapper.new(Cairo::Context.new(surface))
208
+ # Cairo::Context.new(surface)
209
+ end
210
+
211
+ def draw_page(cc)
212
+ cc.target.write_to_png(full_filename)
213
+ init_cc
214
+ cc.set_source_color(:white) # white backdrop TODO make option
215
+ cc.paint
216
+ cc
217
+ end
218
+
219
+ # The last page always gets written out for PNGs because they are separate
220
+ # files and don't get "flushed" automatically. See bug #320.
221
+ def draw_final_page(cc)
222
+ draw_page cc
223
+ cc.target.finish
224
+ end
225
+
226
+ def full_filename
227
+ @sheet_args.full_filename @page_number
228
+ end
229
+ end
230
+ end
231
+ end