squib 0.14.2 → 0.14.3.pre1

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 (163) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +41 -41
  3. data/.travis.yml +19 -19
  4. data/CHANGELOG.md +366 -357
  5. data/CONTRIBUTING.md +40 -40
  6. data/Gemfile +2 -2
  7. data/LICENSE.txt +22 -22
  8. data/README.md +121 -121
  9. data/RELEASE TODO.md +21 -21
  10. data/Rakefile +48 -48
  11. data/appveyor.yml +24 -24
  12. data/bin/squib +4 -4
  13. data/lib/squib.rb +32 -32
  14. data/lib/squib/api/background.rb +15 -15
  15. data/lib/squib/api/data.rb +137 -137
  16. data/lib/squib/api/groups.rb +54 -54
  17. data/lib/squib/api/image.rb +49 -49
  18. data/lib/squib/api/save.rb +83 -83
  19. data/lib/squib/api/settings.rb +21 -21
  20. data/lib/squib/api/shapes.rb +124 -124
  21. data/lib/squib/api/text.rb +25 -25
  22. data/lib/squib/api/text_embed.rb +71 -71
  23. data/lib/squib/api/units.rb +27 -27
  24. data/lib/squib/args/arg_loader.rb +126 -126
  25. data/lib/squib/args/box.rb +55 -55
  26. data/lib/squib/args/card_range.rb +32 -32
  27. data/lib/squib/args/color_validator.rb +12 -12
  28. data/lib/squib/args/coords.rb +35 -35
  29. data/lib/squib/args/csv_opts.rb +25 -25
  30. data/lib/squib/args/dir_validator.rb +16 -16
  31. data/lib/squib/args/draw.rb +92 -92
  32. data/lib/squib/args/embed_adjust.rb +25 -25
  33. data/lib/squib/args/embed_key.rb +17 -17
  34. data/lib/squib/args/hand_special.rb +37 -37
  35. data/lib/squib/args/import.rb +40 -40
  36. data/lib/squib/args/input_file.rb +37 -37
  37. data/lib/squib/args/paint.rb +44 -44
  38. data/lib/squib/args/paragraph.rb +116 -116
  39. data/lib/squib/args/save_batch.rb +63 -63
  40. data/lib/squib/args/scale_box.rb +53 -53
  41. data/lib/squib/args/sheet.rb +172 -172
  42. data/lib/squib/args/showcase_special.rb +38 -38
  43. data/lib/squib/args/sprue_file.rb +44 -44
  44. data/lib/squib/args/svg_special.rb +37 -37
  45. data/lib/squib/args/transform.rb +61 -61
  46. data/lib/squib/args/typographer.rb +119 -119
  47. data/lib/squib/args/unit_conversion.rb +29 -29
  48. data/lib/squib/builtin/layouts/economy.yml +85 -85
  49. data/lib/squib/builtin/layouts/fantasy.yml +101 -101
  50. data/lib/squib/builtin/layouts/hand.yml +62 -62
  51. data/lib/squib/builtin/layouts/party.yml +94 -94
  52. data/lib/squib/builtin/layouts/playing-card.yml +35 -35
  53. data/lib/squib/builtin/layouts/tuck_box.yml +46 -46
  54. data/lib/squib/builtin/projects/advanced/.gitignore +4 -4
  55. data/lib/squib/builtin/projects/advanced/ABOUT.md +19 -19
  56. data/lib/squib/builtin/projects/advanced/Gemfile +11 -11
  57. data/lib/squib/builtin/projects/advanced/Guardfile +21 -21
  58. data/lib/squib/builtin/projects/advanced/IDEAS.md +22 -22
  59. data/lib/squib/builtin/projects/advanced/PLAYTESTING.md +26 -26
  60. data/lib/squib/builtin/projects/advanced/Rakefile +27 -27
  61. data/lib/squib/builtin/projects/advanced/config.yml +53 -49
  62. data/lib/squib/builtin/projects/advanced/docs/PNP NOTES.md +3 -3
  63. data/lib/squib/builtin/projects/advanced/docs/RULES.md +21 -21
  64. data/lib/squib/builtin/projects/advanced/img/example.svg +60 -60
  65. data/lib/squib/builtin/projects/advanced/layouts/deck.yml +27 -27
  66. data/lib/squib/builtin/projects/advanced/src/deck.rb +34 -34
  67. data/lib/squib/builtin/projects/advanced/src/version.rb +3 -3
  68. data/lib/squib/builtin/projects/basic/.gitignore +4 -4
  69. data/lib/squib/builtin/projects/basic/ABOUT.md +19 -19
  70. data/lib/squib/builtin/projects/basic/Gemfile +3 -3
  71. data/lib/squib/builtin/projects/basic/IDEAS.md +22 -22
  72. data/lib/squib/builtin/projects/basic/PLAYTESTING.md +26 -26
  73. data/lib/squib/builtin/projects/basic/PNP NOTES.md +3 -3
  74. data/lib/squib/builtin/projects/basic/RULES.md +21 -21
  75. data/lib/squib/builtin/projects/basic/Rakefile +7 -7
  76. data/lib/squib/builtin/projects/basic/config.yml +53 -49
  77. data/lib/squib/builtin/projects/basic/deck.rb +6 -6
  78. data/lib/squib/builtin/sprues/a4_euro_card.yml +42 -42
  79. data/lib/squib/builtin/sprues/a4_poker_card_8up.yml +40 -40
  80. data/lib/squib/builtin/sprues/a4_poker_card_9up.yml +42 -42
  81. data/lib/squib/builtin/sprues/a4_usa_card.yml +42 -42
  82. data/lib/squib/builtin/sprues/drivethrucards_1up.yml +10 -10
  83. data/lib/squib/builtin/sprues/letter_poker_card_9up.yml +25 -25
  84. data/lib/squib/builtin/sprues/letter_poker_foldable_8up.yml +52 -52
  85. data/lib/squib/builtin/sprues/printplaygames_18up.yml +68 -68
  86. data/lib/squib/card.rb +75 -75
  87. data/lib/squib/commands/cli.rb +39 -39
  88. data/lib/squib/commands/data/template_option.rb +109 -109
  89. data/lib/squib/commands/make_sprue.rb +275 -275
  90. data/lib/squib/commands/new.rb +77 -77
  91. data/lib/squib/conf.rb +144 -139
  92. data/lib/squib/constants.rb +17 -17
  93. data/lib/squib/deck.rb +116 -116
  94. data/lib/squib/graphics/background.rb +14 -14
  95. data/lib/squib/graphics/cairo_context_wrapper.rb +113 -113
  96. data/lib/squib/graphics/embedding_utils.rb +28 -28
  97. data/lib/squib/graphics/gradient_regex.rb +47 -47
  98. data/lib/squib/graphics/hand.rb +42 -42
  99. data/lib/squib/graphics/image.rb +129 -108
  100. data/lib/squib/graphics/save_doc.rb +61 -61
  101. data/lib/squib/graphics/save_images.rb +52 -52
  102. data/lib/squib/graphics/save_pdf.rb +90 -90
  103. data/lib/squib/graphics/save_sprue.rb +204 -204
  104. data/lib/squib/graphics/shapes.rb +143 -143
  105. data/lib/squib/graphics/showcase.rb +85 -85
  106. data/lib/squib/graphics/text.rb +174 -174
  107. data/lib/squib/import/data_frame.rb +108 -108
  108. data/lib/squib/layout_parser.rb +138 -138
  109. data/lib/squib/progress.rb +38 -38
  110. data/lib/squib/sample_helpers.rb +34 -34
  111. data/lib/squib/sprues/crop_line.rb +28 -28
  112. data/lib/squib/sprues/crop_line_dash.rb +35 -35
  113. data/lib/squib/sprues/invalid_sprue_definition.rb +9 -9
  114. data/lib/squib/sprues/sprue.rb +203 -203
  115. data/lib/squib/sprues/sprue_schema.rb +48 -48
  116. data/lib/squib/version.rb +10 -10
  117. data/samples/autoscale_font/_autoscale_font.rb +29 -29
  118. data/samples/backend/_backend.rb +26 -26
  119. data/samples/basic.rb +19 -19
  120. data/samples/bug256/_bug256.rb +13 -0
  121. data/samples/build_groups/build_groups.rb +36 -36
  122. data/samples/colors/_colors.rb +38 -38
  123. data/samples/colors/_gradients.rb +34 -34
  124. data/samples/config/config_text_markup.rb +20 -20
  125. data/samples/config/custom_config.rb +18 -18
  126. data/samples/data/_csv.rb +33 -33
  127. data/samples/data/_excel.rb +55 -55
  128. data/samples/data/_yaml.rb +12 -12
  129. data/samples/hello_world.rb +6 -6
  130. data/samples/images/_cairo_access.rb +39 -39
  131. data/samples/images/_images.rb +104 -104
  132. data/samples/images/_more_load_images.rb +102 -102
  133. data/samples/intro/01_hello.rb +8 -8
  134. data/samples/intro/02_options.rb +14 -14
  135. data/samples/intro/03_layout.rb +11 -11
  136. data/samples/intro/04_arrays.rb +15 -15
  137. data/samples/intro/05_excel.rb +14 -14
  138. data/samples/layouts/builtin_layouts.rb +97 -97
  139. data/samples/layouts/layouts.rb +71 -71
  140. data/samples/project/src/characters.rb +8 -8
  141. data/samples/project/src/skills.rb +7 -7
  142. data/samples/proofs/_tgc_proofs.rb +16 -16
  143. data/samples/ranges/_ranges.rb +64 -64
  144. data/samples/saves/_hand.rb +23 -23
  145. data/samples/saves/_portrait_landscape.rb +23 -23
  146. data/samples/saves/_save_pdf.rb +29 -29
  147. data/samples/saves/_saves.rb +51 -51
  148. data/samples/saves/_showcase.rb +25 -25
  149. data/samples/shapes/_draw_shapes.rb +60 -60
  150. data/samples/shapes/_proofs.rb +22 -22
  151. data/samples/sprues/_advanced_sprues.rb +24 -24
  152. data/samples/sprues/_builtin_sprues.rb +21 -21
  153. data/samples/sprues/_fold_sheet.rb +27 -27
  154. data/samples/sprues/_hex_tiles.rb +15 -15
  155. data/samples/sprues/_mints.rb +11 -11
  156. data/samples/sprues/_sprue_example.rb +11 -11
  157. data/samples/text/_embed_text.rb +128 -128
  158. data/samples/text/_text.rb +47 -47
  159. data/samples/text/_text_options.rb +102 -102
  160. data/samples/text/bug134.rb +14 -14
  161. data/samples/units/_units.rb +32 -32
  162. data/squib.gemspec +52 -52
  163. metadata +51 -48
@@ -1,61 +1,61 @@
1
- module Squib
2
- class Deck
3
-
4
- # :nodoc:
5
- # @api private
6
- def render_sheet(range, batch, sheet)
7
- sheet_width = (sheet.columns * (@width + 2 * sheet.gap - 2 * sheet.trim)) + (2 * sheet.margin)
8
- sheet_height = (sheet.rows * (@height + 2 * sheet.gap - 2 * sheet.trim)) + (2 * sheet.margin)
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 ? (sheet_width - sheet.margin - sheet.gap - @width) : 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 ? (sheet_width - sheet.margin - sheet.gap - @width) : sheet.margin
24
- cc = Cairo::Context.new(Cairo::ImageSurface.new(sheet_width, sheet_height))
25
- end
26
- surface = trim(@cards[i].cairo_surface, sheet.trim, @width, @height)
27
- cc.set_source(surface, x, y)
28
- cc.paint
29
- num_this_sheet += 1
30
- x += (surface.width + sheet.gap) * (sheet.rtl ? -1 : 1)
31
- if num_this_sheet % sheet.columns == 0 # new row
32
- x = sheet.rtl ? (sheet_width - sheet.margin - sheet.gap - @width) : sheet.margin
33
- y += surface.height + sheet.gap
34
- end
35
- bar.increment
36
- end
37
- cc.target.write_to_png(batch.full_filename(sheet_num))
38
- end
39
- end
40
-
41
- # Return a new Cairo::ImageSurface that is trimmed from the original
42
- #
43
- # @param surface The surface to trim
44
- # @param trim The number of pixels around the edge to trim
45
- # @param width The width of the surface prior to the trim
46
- # @param height The height of the surface prior to the trim
47
- # :nodoc:
48
- # @api private
49
- def trim(surface, trim, width, height)
50
- if trim > 0
51
- tmp = Cairo::ImageSurface.new(width - 2 * trim, height - 2 * trim)
52
- cc = Cairo::Context.new(tmp)
53
- cc.set_source(surface, -1 * trim, -1 * trim)
54
- cc.paint
55
- surface = tmp
56
- end
57
- surface
58
- end
59
-
60
- end
61
- end
1
+ module Squib
2
+ class Deck
3
+
4
+ # :nodoc:
5
+ # @api private
6
+ def render_sheet(range, batch, sheet)
7
+ sheet_width = (sheet.columns * (@width + 2 * sheet.gap - 2 * sheet.trim)) + (2 * sheet.margin)
8
+ sheet_height = (sheet.rows * (@height + 2 * sheet.gap - 2 * sheet.trim)) + (2 * sheet.margin)
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 ? (sheet_width - sheet.margin - sheet.gap - @width) : 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 ? (sheet_width - sheet.margin - sheet.gap - @width) : sheet.margin
24
+ cc = Cairo::Context.new(Cairo::ImageSurface.new(sheet_width, sheet_height))
25
+ end
26
+ surface = trim(@cards[i].cairo_surface, sheet.trim, @width, @height)
27
+ cc.set_source(surface, x, y)
28
+ cc.paint
29
+ num_this_sheet += 1
30
+ x += (surface.width + sheet.gap) * (sheet.rtl ? -1 : 1)
31
+ if num_this_sheet % sheet.columns == 0 # new row
32
+ x = sheet.rtl ? (sheet_width - sheet.margin - sheet.gap - @width) : sheet.margin
33
+ y += surface.height + sheet.gap
34
+ end
35
+ bar.increment
36
+ end
37
+ cc.target.write_to_png(batch.full_filename(sheet_num))
38
+ end
39
+ end
40
+
41
+ # Return a new Cairo::ImageSurface that is trimmed from the original
42
+ #
43
+ # @param surface The surface to trim
44
+ # @param trim The number of pixels around the edge to trim
45
+ # @param width The width of the surface prior to the trim
46
+ # @param height The height of the surface prior to the trim
47
+ # :nodoc:
48
+ # @api private
49
+ def trim(surface, trim, width, height)
50
+ if trim > 0
51
+ tmp = Cairo::ImageSurface.new(width - 2 * trim, height - 2 * trim)
52
+ cc = Cairo::Context.new(tmp)
53
+ cc.set_source(surface, -1 * trim, -1 * trim)
54
+ cc.paint
55
+ surface = tmp
56
+ end
57
+ surface
58
+ end
59
+
60
+ end
61
+ end
@@ -1,52 +1,52 @@
1
- module Squib
2
- class Card
3
-
4
- # :nodoc:
5
- # @api private
6
- def save_png(batch)
7
- surface = if preprocess_save?(batch)
8
- w, h = compute_dimensions(batch.rotate, batch.trim)
9
- preprocessed_save(w, h, batch)
10
- else
11
- @cairo_surface
12
- end
13
- write_png(surface, index, batch.dir, batch.prefix, batch.count_format)
14
- end
15
-
16
- # :nodoc:
17
- # @api private
18
- def preprocess_save?(batch)
19
- batch.rotate != false || batch.trim > 0
20
- end
21
-
22
- def compute_dimensions(rotate, trim)
23
- if rotate
24
- [ @height - 2 * trim, @width - 2 * trim ]
25
- else
26
- [ @width - 2 * trim, @height - 2 * trim ]
27
- end
28
- end
29
-
30
- def preprocessed_save(width, height, batch)
31
- new_cc = Cairo::Context.new(Cairo::ImageSurface.new(width, height))
32
- trim_radius = batch.trim_radius
33
- if batch.rotate != false
34
- new_cc.translate(width * 0.5, height * 0.5)
35
- new_cc.rotate(batch.angle)
36
- new_cc.translate(height * -0.5, width * -0.5)
37
- new_cc.rounded_rectangle(0, 0, height, width, trim_radius, trim_radius)
38
- else
39
- new_cc.rounded_rectangle(0, 0, width, height, trim_radius, trim_radius)
40
- end
41
- new_cc.clip
42
- new_cc.set_source(@cairo_surface, -batch.trim, -batch.trim)
43
- new_cc.paint
44
- return new_cc.target
45
- end
46
-
47
- def write_png(surface, i, dir, prefix, count_format)
48
- surface.write_to_png("#{dir}/#{prefix}#{count_format % i}.png")
49
- end
50
-
51
- end
52
- end
1
+ module Squib
2
+ class Card
3
+
4
+ # :nodoc:
5
+ # @api private
6
+ def save_png(batch)
7
+ surface = if preprocess_save?(batch)
8
+ w, h = compute_dimensions(batch.rotate, batch.trim)
9
+ preprocessed_save(w, h, batch)
10
+ else
11
+ @cairo_surface
12
+ end
13
+ write_png(surface, index, batch.dir, batch.prefix, batch.count_format)
14
+ end
15
+
16
+ # :nodoc:
17
+ # @api private
18
+ def preprocess_save?(batch)
19
+ batch.rotate != false || batch.trim > 0
20
+ end
21
+
22
+ def compute_dimensions(rotate, trim)
23
+ if rotate
24
+ [ @height - 2 * trim, @width - 2 * trim ]
25
+ else
26
+ [ @width - 2 * trim, @height - 2 * trim ]
27
+ end
28
+ end
29
+
30
+ def preprocessed_save(width, height, batch)
31
+ new_cc = Cairo::Context.new(Cairo::ImageSurface.new(width, height))
32
+ trim_radius = batch.trim_radius
33
+ if batch.rotate != false
34
+ new_cc.translate(width * 0.5, height * 0.5)
35
+ new_cc.rotate(batch.angle)
36
+ new_cc.translate(height * -0.5, width * -0.5)
37
+ new_cc.rounded_rectangle(0, 0, height, width, trim_radius, trim_radius)
38
+ else
39
+ new_cc.rounded_rectangle(0, 0, width, height, trim_radius, trim_radius)
40
+ end
41
+ new_cc.clip
42
+ new_cc.set_source(@cairo_surface, -batch.trim, -batch.trim)
43
+ new_cc.paint
44
+ return new_cc.target
45
+ end
46
+
47
+ def write_png(surface, i, dir, prefix, count_format)
48
+ surface.write_to_png("#{dir}/#{prefix}#{count_format % i}.png")
49
+ end
50
+
51
+ end
52
+ end
@@ -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,204 +1,204 @@
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 = 1
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'], slot['y'],
34
- slot['rotate'],
35
- @sheet_args.trim, @sheet_args.trim_radius
36
-
37
- bar.increment
38
- end
39
-
40
- draw_overlay_above_cards cc
41
- cc.target.finish
42
- end
43
- end
44
-
45
- protected
46
-
47
- # Initialize the Cairo Context
48
- def init_cc
49
- raise NotImplementedError
50
- end
51
-
52
- def draw_page(cc)
53
- raise NotImplementedError
54
- end
55
-
56
- def full_filename
57
- raise NotImplementedError
58
- end
59
-
60
- private
61
-
62
- def next_page_if_needed(cc, i, per_sheet)
63
- return cc unless (i != 0) && (i % per_sheet) == 0
64
-
65
- draw_overlay_above_cards cc
66
- cc = draw_page cc
67
- draw_overlay_below_cards cc
68
- @page_number += 1
69
- cc
70
- end
71
-
72
- def track_progress(range)
73
- msg = "Saving templated sheet to #{full_filename}"
74
- @deck.progress_bar.start(msg, range.size) { |bar| yield(bar) }
75
- end
76
-
77
- def draw_overlay_below_cards(cc)
78
- if @tmpl.crop_line_overlay == :on_margin
79
- add_margin_overlay_clip_mask cc
80
- cc.clip
81
- draw_crop_line cc, @tmpl.crop_lines
82
- cc.reset_clip
83
- elsif @tmpl.crop_line_overlay == :beneath_cards
84
- draw_crop_line cc, @tmpl.crop_lines
85
- end
86
- end
87
-
88
- def draw_overlay_above_cards(cc)
89
- if @tmpl.crop_line_overlay == :overlay_on_cards
90
- draw_crop_line cc, @tmpl.crop_lines
91
- else
92
- draw_crop_line cc, @overlay_lines
93
- end
94
- end
95
-
96
- def add_margin_overlay_clip_mask(cc)
97
- margin = @tmpl.margin
98
- cc.new_path
99
- cc.rectangle(
100
- margin[:left], margin[:top],
101
- margin[:right] - margin[:left],
102
- margin[:bottom] - margin[:top]
103
- )
104
- cc.new_sub_path
105
- cc.move_to @tmpl.sheet_width, 0
106
- cc.line_to 0, 0
107
- cc.line_to 0, @tmpl.sheet_height
108
- cc.line_to @tmpl.sheet_width, @tmpl.sheet_height
109
- cc.close_path
110
- end
111
-
112
- def draw_crop_line(cc, crop_lines)
113
- crop_lines.each do |line|
114
- cc.move_to line['line'].x1, line['line'].y1
115
- cc.line_to line['line'].x2, line['line'].y2
116
- cc.set_source_color line['color']
117
- cc.set_line_width line['width']
118
- cc.set_dash(line['style'].pattern) if line['style'].pattern
119
- cc.stroke
120
- end
121
- end
122
-
123
- def check_oversized_card
124
- Squib.logger.warn {
125
- "Card size is larger than sprue's expected card size "\
126
- "of #{@tmpl.card_width}x#{@tmpl.card_height}. Cards may overlap."
127
- } if (@deck.width - 2.0 * @sheet_args.trim) > @tmpl.card_width ||
128
- (@deck.height - 2.0 * @sheet_args.trim) > @tmpl.card_height
129
- end
130
-
131
- def draw_card(cc, card, x, y, angle, trim, trim_radius)
132
- # Compute the true size of the card after trimming
133
- w = @deck.width - 2.0 * trim
134
- h = @deck.height - 2.0 * trim
135
-
136
- # Normalize the angles first
137
- # TODO do this in the args class
138
- angle = angle % (2 * Math::PI)
139
- angle = 2 * Math::PI - angle if angle < 0
140
-
141
- # Perform the actual rotation and drawing
142
- mat = cc.matrix # Save the transformation matrix to revert later
143
- cc.translate x, y
144
- cc.translate @deck.width / 2.0, @deck.height / 2.0
145
- cc.rotate angle
146
- cc.translate -@deck.width / 2.0, -@deck.height / 2.0
147
- cc.rounded_rectangle(trim, trim, w, h, trim_radius, trim_radius) # clip
148
- cc.clip
149
- cc.set_source card.cairo_surface, 0, 0
150
- cc.matrix = mat
151
- cc.paint
152
- cc.reset_clip
153
- end
154
- end
155
-
156
- # Templated sheet renderer in PDF format.
157
- class SaveSpruePDF < SaveSprue
158
- def init_cc
159
- ratio = 72.0 / @deck.dpi
160
-
161
- surface = Cairo::PDFSurface.new(
162
- full_filename,
163
- @tmpl.sheet_width * ratio,
164
- @tmpl.sheet_height * ratio
165
- )
166
-
167
- cc = Cairo::Context.new(surface)
168
- cc.scale(72.0 / @deck.dpi, 72.0 / @deck.dpi) # make it like pixels
169
- cc
170
- end
171
-
172
- def draw_page(cc)
173
- cc.show_page
174
- cc.set_source_color(:white) # white backdrop TODO make option
175
- cc.paint
176
- cc
177
- end
178
-
179
- def full_filename
180
- @sheet_args.full_filename
181
- end
182
- end
183
-
184
- # Templated sheet renderer in PNG format.
185
- class SaveSpruePNG < SaveSprue
186
- def init_cc
187
- surface = Cairo::ImageSurface.new @tmpl.sheet_width, @tmpl.sheet_height
188
- Cairo::Context.new(surface)
189
- end
190
-
191
- def draw_page(cc)
192
- cc.target.write_to_png(full_filename)
193
- init_cc
194
- cc.set_source_color(:white) # white backdrop TODO make option
195
- cc.paint
196
- cc
197
- end
198
-
199
- def full_filename
200
- @sheet_args.full_filename @page_number
201
- end
202
- end
203
- end
204
- 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 = 1
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'], slot['y'],
34
+ slot['rotate'],
35
+ @sheet_args.trim, @sheet_args.trim_radius
36
+
37
+ bar.increment
38
+ end
39
+
40
+ draw_overlay_above_cards cc
41
+ cc.target.finish
42
+ end
43
+ end
44
+
45
+ protected
46
+
47
+ # Initialize the Cairo Context
48
+ def init_cc
49
+ raise NotImplementedError
50
+ end
51
+
52
+ def draw_page(cc)
53
+ raise NotImplementedError
54
+ end
55
+
56
+ def full_filename
57
+ raise NotImplementedError
58
+ end
59
+
60
+ private
61
+
62
+ def next_page_if_needed(cc, i, per_sheet)
63
+ return cc unless (i != 0) && (i % per_sheet) == 0
64
+
65
+ draw_overlay_above_cards cc
66
+ cc = draw_page cc
67
+ draw_overlay_below_cards cc
68
+ @page_number += 1
69
+ cc
70
+ end
71
+
72
+ def track_progress(range)
73
+ msg = "Saving templated sheet to #{full_filename}"
74
+ @deck.progress_bar.start(msg, range.size) { |bar| yield(bar) }
75
+ end
76
+
77
+ def draw_overlay_below_cards(cc)
78
+ if @tmpl.crop_line_overlay == :on_margin
79
+ add_margin_overlay_clip_mask cc
80
+ cc.clip
81
+ draw_crop_line cc, @tmpl.crop_lines
82
+ cc.reset_clip
83
+ elsif @tmpl.crop_line_overlay == :beneath_cards
84
+ draw_crop_line cc, @tmpl.crop_lines
85
+ end
86
+ end
87
+
88
+ def draw_overlay_above_cards(cc)
89
+ if @tmpl.crop_line_overlay == :overlay_on_cards
90
+ draw_crop_line cc, @tmpl.crop_lines
91
+ else
92
+ draw_crop_line cc, @overlay_lines
93
+ end
94
+ end
95
+
96
+ def add_margin_overlay_clip_mask(cc)
97
+ margin = @tmpl.margin
98
+ cc.new_path
99
+ cc.rectangle(
100
+ margin[:left], margin[:top],
101
+ margin[:right] - margin[:left],
102
+ margin[:bottom] - margin[:top]
103
+ )
104
+ cc.new_sub_path
105
+ cc.move_to @tmpl.sheet_width, 0
106
+ cc.line_to 0, 0
107
+ cc.line_to 0, @tmpl.sheet_height
108
+ cc.line_to @tmpl.sheet_width, @tmpl.sheet_height
109
+ cc.close_path
110
+ end
111
+
112
+ def draw_crop_line(cc, crop_lines)
113
+ crop_lines.each do |line|
114
+ cc.move_to line['line'].x1, line['line'].y1
115
+ cc.line_to line['line'].x2, line['line'].y2
116
+ cc.set_source_color line['color']
117
+ cc.set_line_width line['width']
118
+ cc.set_dash(line['style'].pattern) if line['style'].pattern
119
+ cc.stroke
120
+ end
121
+ end
122
+
123
+ def check_oversized_card
124
+ Squib.logger.warn {
125
+ "Card size is larger than sprue's expected card size "\
126
+ "of #{@tmpl.card_width}x#{@tmpl.card_height}. Cards may overlap."
127
+ } if (@deck.width - 2.0 * @sheet_args.trim) > @tmpl.card_width ||
128
+ (@deck.height - 2.0 * @sheet_args.trim) > @tmpl.card_height
129
+ end
130
+
131
+ def draw_card(cc, card, x, y, angle, trim, trim_radius)
132
+ # Compute the true size of the card after trimming
133
+ w = @deck.width - 2.0 * trim
134
+ h = @deck.height - 2.0 * trim
135
+
136
+ # Normalize the angles first
137
+ # TODO do this in the args class
138
+ angle = angle % (2 * Math::PI)
139
+ angle = 2 * Math::PI - angle if angle < 0
140
+
141
+ # Perform the actual rotation and drawing
142
+ mat = cc.matrix # Save the transformation matrix to revert later
143
+ cc.translate x, y
144
+ cc.translate @deck.width / 2.0, @deck.height / 2.0
145
+ cc.rotate angle
146
+ cc.translate -@deck.width / 2.0, -@deck.height / 2.0
147
+ cc.rounded_rectangle(trim, trim, w, h, trim_radius, trim_radius) # clip
148
+ cc.clip
149
+ cc.set_source card.cairo_surface, 0, 0
150
+ cc.matrix = mat
151
+ cc.paint
152
+ cc.reset_clip
153
+ end
154
+ end
155
+
156
+ # Templated sheet renderer in PDF format.
157
+ class SaveSpruePDF < SaveSprue
158
+ def init_cc
159
+ ratio = 72.0 / @deck.dpi
160
+
161
+ surface = Cairo::PDFSurface.new(
162
+ full_filename,
163
+ @tmpl.sheet_width * ratio,
164
+ @tmpl.sheet_height * ratio
165
+ )
166
+
167
+ cc = Cairo::Context.new(surface)
168
+ cc.scale(72.0 / @deck.dpi, 72.0 / @deck.dpi) # make it like pixels
169
+ cc
170
+ end
171
+
172
+ def draw_page(cc)
173
+ cc.show_page
174
+ cc.set_source_color(:white) # white backdrop TODO make option
175
+ cc.paint
176
+ cc
177
+ end
178
+
179
+ def full_filename
180
+ @sheet_args.full_filename
181
+ end
182
+ end
183
+
184
+ # Templated sheet renderer in PNG format.
185
+ class SaveSpruePNG < SaveSprue
186
+ def init_cc
187
+ surface = Cairo::ImageSurface.new @tmpl.sheet_width, @tmpl.sheet_height
188
+ Cairo::Context.new(surface)
189
+ end
190
+
191
+ def draw_page(cc)
192
+ cc.target.write_to_png(full_filename)
193
+ init_cc
194
+ cc.set_source_color(:white) # white backdrop TODO make option
195
+ cc.paint
196
+ cc
197
+ end
198
+
199
+ def full_filename
200
+ @sheet_args.full_filename @page_number
201
+ end
202
+ end
203
+ end
204
+ end