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.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +39 -39
- data/.github/ISSUE_TEMPLATE/feature_request.md +22 -22
- data/.github/ISSUE_TEMPLATE/help-wanted.md +13 -13
- data/.github/ISSUE_TEMPLATE/release-todo.md +45 -33
- data/.github/PULL_REQUEST_TEMPLATE.md +13 -13
- data/.github/workflows/tests.yml +22 -22
- data/.gitignore +41 -41
- data/.vscode/settings.json +4 -3
- data/CHANGELOG.md +460 -460
- data/CONTRIBUTING.md +40 -40
- data/Dockerfile +27 -27
- data/Gemfile +2 -2
- data/Guardfile +8 -8
- data/LICENSE.txt +22 -22
- data/README.md +139 -139
- data/Rakefile +51 -51
- data/bin/squib +5 -5
- data/lib/squib/api/settings.rb +21 -21
- data/lib/squib/args/arg_loader.rb +138 -138
- data/lib/squib/args/box.rb +59 -59
- data/lib/squib/args/card_range.rb +34 -34
- data/lib/squib/args/color_validator.rb +7 -7
- data/lib/squib/args/coords.rb +49 -49
- data/lib/squib/args/csv_opts.rb +22 -22
- data/lib/squib/args/dir_validator.rb +11 -11
- data/lib/squib/args/draw.rb +93 -93
- data/lib/squib/args/drop_shadow.rb +39 -39
- data/lib/squib/args/embed_adjust.rb +22 -22
- data/lib/squib/args/embed_key.rb +12 -12
- data/lib/squib/args/hand_special.rb +37 -37
- data/lib/squib/args/import.rb +67 -67
- data/lib/squib/args/input_file.rb +55 -55
- data/lib/squib/args/paint.rb +43 -43
- data/lib/squib/args/paragraph.rb +118 -118
- data/lib/squib/args/save_batch.rb +65 -65
- data/lib/squib/args/scale_box.rb +57 -57
- data/lib/squib/args/sheet.rb +165 -165
- data/lib/squib/args/showcase_special.rb +41 -41
- data/lib/squib/args/sprue_file.rb +44 -44
- data/lib/squib/args/svg_special.rb +37 -37
- data/lib/squib/args/transform.rb +55 -55
- data/lib/squib/args/typographer.rb +115 -115
- data/lib/squib/args/unit_conversion.rb +27 -27
- data/lib/squib/args/xywh_shorthands.rb +50 -50
- data/lib/squib/builtin/layouts/economy.yml +85 -85
- data/lib/squib/builtin/layouts/fantasy.yml +101 -101
- data/lib/squib/builtin/layouts/hand.yml +62 -62
- data/lib/squib/builtin/layouts/party.yml +94 -94
- data/lib/squib/builtin/layouts/playing-card.yml +35 -35
- data/lib/squib/builtin/layouts/tuck_box.yml +46 -46
- data/lib/squib/builtin/projects/advanced/.gitignore +4 -4
- data/lib/squib/builtin/projects/advanced/ABOUT.md +19 -19
- data/lib/squib/builtin/projects/advanced/Gemfile +11 -11
- data/lib/squib/builtin/projects/advanced/Guardfile +21 -21
- data/lib/squib/builtin/projects/advanced/IDEAS.md +22 -22
- data/lib/squib/builtin/projects/advanced/PLAYTESTING.md +26 -26
- data/lib/squib/builtin/projects/advanced/Rakefile +27 -27
- data/lib/squib/builtin/projects/advanced/config.yml +49 -49
- data/lib/squib/builtin/projects/advanced/docs/PNP NOTES.md +3 -3
- data/lib/squib/builtin/projects/advanced/docs/RULES.md +21 -21
- data/lib/squib/builtin/projects/advanced/img/example.svg +60 -60
- data/lib/squib/builtin/projects/advanced/layouts/deck.yml +27 -27
- data/lib/squib/builtin/projects/advanced/src/deck.rb +34 -34
- data/lib/squib/builtin/projects/advanced/src/version.rb +3 -3
- data/lib/squib/builtin/projects/basic/.gitignore +4 -4
- data/lib/squib/builtin/projects/basic/ABOUT.md +19 -19
- data/lib/squib/builtin/projects/basic/Gemfile +3 -3
- data/lib/squib/builtin/projects/basic/IDEAS.md +22 -22
- data/lib/squib/builtin/projects/basic/PLAYTESTING.md +26 -26
- data/lib/squib/builtin/projects/basic/PNP NOTES.md +3 -3
- data/lib/squib/builtin/projects/basic/RULES.md +21 -21
- data/lib/squib/builtin/projects/basic/Rakefile +7 -7
- data/lib/squib/builtin/projects/basic/config.yml +50 -50
- data/lib/squib/builtin/projects/basic/deck.rb +6 -6
- data/lib/squib/builtin/sprues/a4_euro_card.yml +42 -42
- data/lib/squib/builtin/sprues/a4_poker_card_8up.yml +40 -40
- data/lib/squib/builtin/sprues/a4_poker_card_9up.yml +42 -42
- data/lib/squib/builtin/sprues/a4_usa_card.yml +42 -42
- data/lib/squib/builtin/sprues/drivethrucards_1up.yml +10 -10
- data/lib/squib/builtin/sprues/letter_poker_card_9up.yml +25 -25
- data/lib/squib/builtin/sprues/letter_poker_foldable_8up.yml +52 -52
- data/lib/squib/builtin/sprues/printplaygames_18up.yml +68 -68
- data/lib/squib/card.rb +75 -75
- data/lib/squib/commands/cli.rb +39 -39
- data/lib/squib/commands/data/template_option.rb +109 -109
- data/lib/squib/commands/make_sprue.rb +277 -277
- data/lib/squib/commands/new.rb +77 -77
- data/lib/squib/conf.rb +149 -149
- data/lib/squib/constants.rb +17 -17
- data/lib/squib/deck.rb +138 -138
- data/lib/squib/dsl/background.rb +35 -35
- data/lib/squib/dsl/circle.rb +39 -39
- data/lib/squib/dsl/csv.rb +42 -42
- data/lib/squib/dsl/curve.rb +35 -35
- data/lib/squib/dsl/cut_zone.rb +47 -47
- data/lib/squib/dsl/ellipse.rb +37 -37
- data/lib/squib/dsl/grid.rb +35 -35
- data/lib/squib/dsl/groups.rb +54 -54
- data/lib/squib/dsl/hand.rb +42 -42
- data/lib/squib/dsl/line.rb +35 -35
- data/lib/squib/dsl/png.rb +57 -57
- data/lib/squib/dsl/polygon.rb +36 -36
- data/lib/squib/dsl/rect.rb +37 -37
- data/lib/squib/dsl/safe_zone.rb +48 -48
- data/lib/squib/dsl/save.rb +21 -21
- data/lib/squib/dsl/save_pdf.rb +50 -50
- data/lib/squib/dsl/save_png.rb +48 -48
- data/lib/squib/dsl/save_sheet.rb +53 -53
- data/lib/squib/dsl/showcase.rb +43 -43
- data/lib/squib/dsl/star.rb +37 -37
- data/lib/squib/dsl/svg.rb +63 -63
- data/lib/squib/dsl/text.rb +54 -54
- data/lib/squib/dsl/text_embed.rb +79 -79
- data/lib/squib/dsl/triangle.rb +35 -35
- data/lib/squib/dsl/units.rb +37 -37
- data/lib/squib/dsl/xlsx.rb +40 -40
- data/lib/squib/dsl/yaml.rb +40 -40
- data/lib/squib/errors_warnings/warn_unexpected_params.rb +14 -14
- data/lib/squib/graphics/background.rb +14 -14
- data/lib/squib/graphics/cairo_context_wrapper.rb +115 -115
- data/lib/squib/graphics/embedding_utils.rb +28 -28
- data/lib/squib/graphics/gradient_regex.rb +47 -47
- data/lib/squib/graphics/hand.rb +42 -42
- data/lib/squib/graphics/image.rb +123 -123
- data/lib/squib/graphics/save_doc.rb +77 -77
- data/lib/squib/graphics/save_images.rb +91 -91
- data/lib/squib/graphics/save_pdf.rb +90 -90
- data/lib/squib/graphics/save_sprue.rb +231 -231
- data/lib/squib/graphics/shapes.rb +143 -143
- data/lib/squib/graphics/showcase.rb +85 -85
- data/lib/squib/graphics/text.rb +202 -202
- data/lib/squib/import/csv_importer.rb +45 -45
- data/lib/squib/import/data_frame.rb +108 -108
- data/lib/squib/import/quantity_exploder.rb +17 -17
- data/lib/squib/import/xlsx_importer.rb +28 -28
- data/lib/squib/import/yaml_importer.rb +30 -30
- data/lib/squib/layout_parser.rb +155 -155
- data/lib/squib/progress.rb +38 -38
- data/lib/squib/sample_helpers.rb +34 -34
- data/lib/squib/sprues/crop_line.rb +28 -28
- data/lib/squib/sprues/crop_line_dash.rb +35 -35
- data/lib/squib/sprues/invalid_sprue_definition.rb +9 -9
- data/lib/squib/sprues/sprue.rb +208 -208
- data/lib/squib/sprues/sprue_schema.rb +51 -51
- data/lib/squib/system_fonts.rb +16 -16
- data/lib/squib/version.rb +11 -11
- data/lib/squib.rb +35 -35
- data/samples/autoscale_font/_autoscale_font.rb +98 -98
- data/samples/backend/_backend.rb +26 -26
- data/samples/basic.rb +19 -19
- data/samples/build_groups/build_groups.rb +36 -36
- data/samples/colors/_colors.rb +44 -44
- data/samples/colors/_gradients.rb +34 -34
- data/samples/colors/_switch_color.rb +33 -33
- data/samples/config/config_text_markup.rb +20 -20
- data/samples/config/custom_config.rb +18 -18
- data/samples/data/_csv.rb +33 -33
- data/samples/data/_excel.rb +55 -55
- data/samples/data/_yaml.rb +12 -12
- data/samples/hello_world.rb +6 -6
- data/samples/images/_cairo_access.rb +39 -39
- data/samples/images/_images.rb +104 -104
- data/samples/images/_more_load_images.rb +102 -102
- data/samples/images/_placeholders.rb +48 -48
- data/samples/intro/01_hello.rb +8 -8
- data/samples/intro/02_options.rb +14 -14
- data/samples/intro/03_layout.rb +11 -11
- data/samples/intro/04_arrays.rb +15 -15
- data/samples/intro/05_excel.rb +14 -14
- data/samples/layouts/builtin_layouts.rb +97 -97
- data/samples/layouts/layouts.rb +71 -71
- data/samples/project/src/characters.rb +8 -8
- data/samples/project/src/skills.rb +7 -7
- data/samples/proofs/_tgc_proofs.rb +16 -16
- data/samples/ranges/_ranges.rb +64 -64
- data/samples/saves/_hand.rb +23 -23
- data/samples/saves/_portrait_landscape.rb +23 -23
- data/samples/saves/_save_filenames.rb +28 -28
- data/samples/saves/_save_pdf.rb +29 -29
- data/samples/saves/_saves.rb +75 -75
- data/samples/saves/_showcase.rb +25 -25
- data/samples/shadows/_shadow.rb +71 -71
- data/samples/shapes/_draw_shapes.rb +60 -60
- data/samples/shapes/_proofs.rb +22 -22
- data/samples/sprues/_advanced_sprues.rb +25 -25
- data/samples/sprues/_builtin_sprues.rb +22 -22
- data/samples/sprues/_fold_sheet.rb +30 -30
- data/samples/sprues/_hex_tiles.rb +15 -15
- data/samples/sprues/_mints.rb +11 -11
- data/samples/sprues/_negative_coords.rb +6 -6
- data/samples/sprues/_sprue_example.rb +11 -11
- data/samples/system_font_debug/_list_fonts.rb +14 -14
- data/samples/text/_embed_text.rb +128 -128
- data/samples/text/_text.rb +52 -52
- data/samples/text/_text_options.rb +103 -103
- data/samples/text/bug134.rb +14 -14
- data/samples/units/_cells.rb +50 -50
- data/samples/units/_shorthands.rb +48 -48
- data/samples/units/_units.rb +39 -39
- data/squib.gemspec +58 -58
- 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
|