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
data/lib/squib/sprues/sprue.rb
CHANGED
|
@@ -1,208 +1,208 @@
|
|
|
1
|
-
require 'yaml'
|
|
2
|
-
require 'classy_hash'
|
|
3
|
-
require_relative '../args/color_validator'
|
|
4
|
-
require_relative '../args/unit_conversion'
|
|
5
|
-
require_relative 'crop_line'
|
|
6
|
-
require_relative 'crop_line_dash'
|
|
7
|
-
require_relative 'invalid_sprue_definition'
|
|
8
|
-
require_relative 'sprue_schema'
|
|
9
|
-
|
|
10
|
-
module Squib
|
|
11
|
-
class Sprue
|
|
12
|
-
include Args::ColorValidator
|
|
13
|
-
|
|
14
|
-
# Defaults are set for poker sized deck on a A4 sheet, with no cards
|
|
15
|
-
DEFAULTS = {
|
|
16
|
-
'sheet_width' => nil,
|
|
17
|
-
'sheet_height' => nil,
|
|
18
|
-
'card_width' => nil,
|
|
19
|
-
'card_height' => nil,
|
|
20
|
-
'dpi' => 300,
|
|
21
|
-
'cell_px' => 37.5,
|
|
22
|
-
'position_reference' => :topleft,
|
|
23
|
-
'rotate' => 0.0,
|
|
24
|
-
'crop_line' => {
|
|
25
|
-
'style' => :solid,
|
|
26
|
-
'width' => '0.02mm',
|
|
27
|
-
'color' => :black,
|
|
28
|
-
'overlay' => :on_margin,
|
|
29
|
-
'lines' => []
|
|
30
|
-
},
|
|
31
|
-
'cards' => []
|
|
32
|
-
}.freeze
|
|
33
|
-
|
|
34
|
-
attr_reader :dpi
|
|
35
|
-
|
|
36
|
-
def initialize(template_hash, dpi, cell_px)
|
|
37
|
-
@template_hash = template_hash
|
|
38
|
-
@dpi = dpi
|
|
39
|
-
@cell_px = cell_px
|
|
40
|
-
@crop_line_default = @template_hash['crop_line'].select do |k, _|
|
|
41
|
-
%w[style width color].include? k
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# Load the template definition file
|
|
46
|
-
def self.load(file, dpi, cell_px)
|
|
47
|
-
yaml = {}
|
|
48
|
-
thefile = file if File.exist?(file) # use custom first
|
|
49
|
-
thefile = builtin(file) if File.exist?(builtin(file)) # then builtin
|
|
50
|
-
unless File.exist?(thefile)
|
|
51
|
-
Squib::logger.error("Sprue not found: #{file}. Falling back to defaults.")
|
|
52
|
-
end
|
|
53
|
-
yaml = YAML.load_file(thefile) || {} if File.exist? thefile
|
|
54
|
-
# Bake the default values into our sprue
|
|
55
|
-
new_hash = DEFAULTS.merge(yaml)
|
|
56
|
-
new_hash['crop_line'] = DEFAULTS['crop_line'].
|
|
57
|
-
merge(new_hash['crop_line'])
|
|
58
|
-
warn_unrecognized(yaml)
|
|
59
|
-
|
|
60
|
-
# Validate
|
|
61
|
-
begin
|
|
62
|
-
require 'benchmark'
|
|
63
|
-
ClassyHash.validate(new_hash, Sprues::SCHEMA)
|
|
64
|
-
rescue ClassyHash::SchemaViolationError => e
|
|
65
|
-
raise Sprues::InvalidSprueDefinition.new(thefile, e)
|
|
66
|
-
end
|
|
67
|
-
Sprue.new new_hash, dpi, cell_px
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def sheet_width
|
|
71
|
-
Args::UnitConversion.parse @template_hash['sheet_width'], @dpi, @cell_px
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def sheet_height
|
|
75
|
-
Args::UnitConversion.parse @template_hash['sheet_height'], @dpi, @cell_px
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def card_width
|
|
79
|
-
Args::UnitConversion.parse @template_hash['card_width'], @dpi, @cell_px
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def card_height
|
|
83
|
-
Args::UnitConversion.parse @template_hash['card_height'], @dpi, @cell_px
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def card_default_rotation
|
|
87
|
-
parse_rotate_param @template_hash['rotate']
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def crop_line_overlay
|
|
91
|
-
@template_hash['crop_line']['overlay']
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def crop_lines
|
|
95
|
-
lines = @template_hash['crop_line']['lines'].map(
|
|
96
|
-
&method(:parse_crop_line)
|
|
97
|
-
)
|
|
98
|
-
if block_given?
|
|
99
|
-
lines.each { |v| yield v }
|
|
100
|
-
else
|
|
101
|
-
lines
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def cards
|
|
106
|
-
parsed_cards = @template_hash['cards'].map(&method(:parse_card))
|
|
107
|
-
if block_given?
|
|
108
|
-
parsed_cards.each { |v| yield v }
|
|
109
|
-
else
|
|
110
|
-
parsed_cards
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def margin
|
|
115
|
-
# NOTE: There's a baseline of 0.25mm that we can 100% make sure that we
|
|
116
|
-
# can overlap really thin lines on the PDF
|
|
117
|
-
crop_line_width = [
|
|
118
|
-
Args::UnitConversion.parse(@template_hash['crop_line']['width'], @dpi, @cell_px),
|
|
119
|
-
Args::UnitConversion.parse('0.25mm', @dpi, @cell_px)
|
|
120
|
-
].max
|
|
121
|
-
|
|
122
|
-
parsed_cards = cards
|
|
123
|
-
left, right = parsed_cards.minmax { |a, b| a['x'] <=> b['x'] }
|
|
124
|
-
top, bottom = parsed_cards.minmax { |a, b| a['y'] <=> b['y'] }
|
|
125
|
-
|
|
126
|
-
{
|
|
127
|
-
left: left['x'] - crop_line_width,
|
|
128
|
-
right: right['x'] + card_width + crop_line_width,
|
|
129
|
-
top: top['y'] - crop_line_width,
|
|
130
|
-
bottom: bottom['y'] + card_height + crop_line_width
|
|
131
|
-
}
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
# Warn unrecognized options in the template sheet
|
|
135
|
-
def self.warn_unrecognized(yaml)
|
|
136
|
-
unrec = yaml.keys - DEFAULTS.keys
|
|
137
|
-
return unless unrec.any?
|
|
138
|
-
|
|
139
|
-
Squib.logger.warn(
|
|
140
|
-
"Unrecognized configuration option(s): #{unrec.join(',')}"
|
|
141
|
-
)
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
private
|
|
145
|
-
|
|
146
|
-
# Return path for built-in sheet templates
|
|
147
|
-
def self.builtin(file)
|
|
148
|
-
"#{File.dirname(__FILE__)}/../builtin/sprues/#{file}"
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
# Parse crop line definitions from template.
|
|
152
|
-
def parse_crop_line(line)
|
|
153
|
-
new_line = @crop_line_default.merge line
|
|
154
|
-
new_line['width'] = Args::UnitConversion.parse(new_line['width'], @dpi, @cell_px)
|
|
155
|
-
new_line['color'] = colorify new_line['color']
|
|
156
|
-
new_line['style_desc'] = new_line['style']
|
|
157
|
-
new_line['style'] = Sprues::CropLineDash.new(new_line['style'], @dpi, @cell_px)
|
|
158
|
-
new_line['line'] = Sprues::CropLine.new(
|
|
159
|
-
new_line['type'], new_line['position'], sheet_width, sheet_height, @dpi, @cell_px
|
|
160
|
-
)
|
|
161
|
-
new_line
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
# Parse card definitions from template.
|
|
165
|
-
def parse_card(card)
|
|
166
|
-
new_card = card.clone
|
|
167
|
-
|
|
168
|
-
x = Args::UnitConversion.parse(card['x'], @dpi, @cell_px)
|
|
169
|
-
y = Args::UnitConversion.parse(card['y'], @dpi, @cell_px)
|
|
170
|
-
if @template_hash['position_reference'] == :center
|
|
171
|
-
# Normalize it to a top-left positional reference
|
|
172
|
-
x -= card_width / 2
|
|
173
|
-
y -= card_height / 2
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
new_card['x'] = x
|
|
177
|
-
new_card['y'] = y
|
|
178
|
-
new_card['rotate'] = parse_rotate_param(
|
|
179
|
-
card['rotate'] ? card['rotate'] : @template_hash['rotate'])
|
|
180
|
-
new_card['flip_vertical'] = card['flip_vertical'] == true
|
|
181
|
-
new_card['flip_horizontal'] = card['flip_horizontal'] == true
|
|
182
|
-
new_card
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
def parse_rotate_param(val)
|
|
186
|
-
if val == :clockwise
|
|
187
|
-
0.5 * Math::PI
|
|
188
|
-
elsif val == :counterclockwise
|
|
189
|
-
1.5 * Math::PI
|
|
190
|
-
elsif val == :turnaround
|
|
191
|
-
Math::PI
|
|
192
|
-
elsif val.is_a? String
|
|
193
|
-
if val.end_with? 'deg'
|
|
194
|
-
val.gsub(/deg$/, '').to_f / 180 * Math::PI
|
|
195
|
-
elsif val.end_with? 'rad'
|
|
196
|
-
val.gsub(/rad$/, '').to_f
|
|
197
|
-
else
|
|
198
|
-
val.to_f
|
|
199
|
-
end
|
|
200
|
-
elsif val.nil?
|
|
201
|
-
0.0
|
|
202
|
-
else
|
|
203
|
-
val.to_f
|
|
204
|
-
end
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
end
|
|
208
|
-
end
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require 'classy_hash'
|
|
3
|
+
require_relative '../args/color_validator'
|
|
4
|
+
require_relative '../args/unit_conversion'
|
|
5
|
+
require_relative 'crop_line'
|
|
6
|
+
require_relative 'crop_line_dash'
|
|
7
|
+
require_relative 'invalid_sprue_definition'
|
|
8
|
+
require_relative 'sprue_schema'
|
|
9
|
+
|
|
10
|
+
module Squib
|
|
11
|
+
class Sprue
|
|
12
|
+
include Args::ColorValidator
|
|
13
|
+
|
|
14
|
+
# Defaults are set for poker sized deck on a A4 sheet, with no cards
|
|
15
|
+
DEFAULTS = {
|
|
16
|
+
'sheet_width' => nil,
|
|
17
|
+
'sheet_height' => nil,
|
|
18
|
+
'card_width' => nil,
|
|
19
|
+
'card_height' => nil,
|
|
20
|
+
'dpi' => 300,
|
|
21
|
+
'cell_px' => 37.5,
|
|
22
|
+
'position_reference' => :topleft,
|
|
23
|
+
'rotate' => 0.0,
|
|
24
|
+
'crop_line' => {
|
|
25
|
+
'style' => :solid,
|
|
26
|
+
'width' => '0.02mm',
|
|
27
|
+
'color' => :black,
|
|
28
|
+
'overlay' => :on_margin,
|
|
29
|
+
'lines' => []
|
|
30
|
+
},
|
|
31
|
+
'cards' => []
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
34
|
+
attr_reader :dpi
|
|
35
|
+
|
|
36
|
+
def initialize(template_hash, dpi, cell_px)
|
|
37
|
+
@template_hash = template_hash
|
|
38
|
+
@dpi = dpi
|
|
39
|
+
@cell_px = cell_px
|
|
40
|
+
@crop_line_default = @template_hash['crop_line'].select do |k, _|
|
|
41
|
+
%w[style width color].include? k
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Load the template definition file
|
|
46
|
+
def self.load(file, dpi, cell_px)
|
|
47
|
+
yaml = {}
|
|
48
|
+
thefile = file if File.exist?(file) # use custom first
|
|
49
|
+
thefile = builtin(file) if File.exist?(builtin(file)) # then builtin
|
|
50
|
+
unless File.exist?(thefile)
|
|
51
|
+
Squib::logger.error("Sprue not found: #{file}. Falling back to defaults.")
|
|
52
|
+
end
|
|
53
|
+
yaml = YAML.load_file(thefile) || {} if File.exist? thefile
|
|
54
|
+
# Bake the default values into our sprue
|
|
55
|
+
new_hash = DEFAULTS.merge(yaml)
|
|
56
|
+
new_hash['crop_line'] = DEFAULTS['crop_line'].
|
|
57
|
+
merge(new_hash['crop_line'])
|
|
58
|
+
warn_unrecognized(yaml)
|
|
59
|
+
|
|
60
|
+
# Validate
|
|
61
|
+
begin
|
|
62
|
+
require 'benchmark'
|
|
63
|
+
ClassyHash.validate(new_hash, Sprues::SCHEMA)
|
|
64
|
+
rescue ClassyHash::SchemaViolationError => e
|
|
65
|
+
raise Sprues::InvalidSprueDefinition.new(thefile, e)
|
|
66
|
+
end
|
|
67
|
+
Sprue.new new_hash, dpi, cell_px
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def sheet_width
|
|
71
|
+
Args::UnitConversion.parse @template_hash['sheet_width'], @dpi, @cell_px
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def sheet_height
|
|
75
|
+
Args::UnitConversion.parse @template_hash['sheet_height'], @dpi, @cell_px
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def card_width
|
|
79
|
+
Args::UnitConversion.parse @template_hash['card_width'], @dpi, @cell_px
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def card_height
|
|
83
|
+
Args::UnitConversion.parse @template_hash['card_height'], @dpi, @cell_px
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def card_default_rotation
|
|
87
|
+
parse_rotate_param @template_hash['rotate']
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def crop_line_overlay
|
|
91
|
+
@template_hash['crop_line']['overlay']
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def crop_lines
|
|
95
|
+
lines = @template_hash['crop_line']['lines'].map(
|
|
96
|
+
&method(:parse_crop_line)
|
|
97
|
+
)
|
|
98
|
+
if block_given?
|
|
99
|
+
lines.each { |v| yield v }
|
|
100
|
+
else
|
|
101
|
+
lines
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def cards
|
|
106
|
+
parsed_cards = @template_hash['cards'].map(&method(:parse_card))
|
|
107
|
+
if block_given?
|
|
108
|
+
parsed_cards.each { |v| yield v }
|
|
109
|
+
else
|
|
110
|
+
parsed_cards
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def margin
|
|
115
|
+
# NOTE: There's a baseline of 0.25mm that we can 100% make sure that we
|
|
116
|
+
# can overlap really thin lines on the PDF
|
|
117
|
+
crop_line_width = [
|
|
118
|
+
Args::UnitConversion.parse(@template_hash['crop_line']['width'], @dpi, @cell_px),
|
|
119
|
+
Args::UnitConversion.parse('0.25mm', @dpi, @cell_px)
|
|
120
|
+
].max
|
|
121
|
+
|
|
122
|
+
parsed_cards = cards
|
|
123
|
+
left, right = parsed_cards.minmax { |a, b| a['x'] <=> b['x'] }
|
|
124
|
+
top, bottom = parsed_cards.minmax { |a, b| a['y'] <=> b['y'] }
|
|
125
|
+
|
|
126
|
+
{
|
|
127
|
+
left: left['x'] - crop_line_width,
|
|
128
|
+
right: right['x'] + card_width + crop_line_width,
|
|
129
|
+
top: top['y'] - crop_line_width,
|
|
130
|
+
bottom: bottom['y'] + card_height + crop_line_width
|
|
131
|
+
}
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Warn unrecognized options in the template sheet
|
|
135
|
+
def self.warn_unrecognized(yaml)
|
|
136
|
+
unrec = yaml.keys - DEFAULTS.keys
|
|
137
|
+
return unless unrec.any?
|
|
138
|
+
|
|
139
|
+
Squib.logger.warn(
|
|
140
|
+
"Unrecognized configuration option(s): #{unrec.join(',')}"
|
|
141
|
+
)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
private
|
|
145
|
+
|
|
146
|
+
# Return path for built-in sheet templates
|
|
147
|
+
def self.builtin(file)
|
|
148
|
+
"#{File.dirname(__FILE__)}/../builtin/sprues/#{file}"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Parse crop line definitions from template.
|
|
152
|
+
def parse_crop_line(line)
|
|
153
|
+
new_line = @crop_line_default.merge line
|
|
154
|
+
new_line['width'] = Args::UnitConversion.parse(new_line['width'], @dpi, @cell_px)
|
|
155
|
+
new_line['color'] = colorify new_line['color']
|
|
156
|
+
new_line['style_desc'] = new_line['style']
|
|
157
|
+
new_line['style'] = Sprues::CropLineDash.new(new_line['style'], @dpi, @cell_px)
|
|
158
|
+
new_line['line'] = Sprues::CropLine.new(
|
|
159
|
+
new_line['type'], new_line['position'], sheet_width, sheet_height, @dpi, @cell_px
|
|
160
|
+
)
|
|
161
|
+
new_line
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Parse card definitions from template.
|
|
165
|
+
def parse_card(card)
|
|
166
|
+
new_card = card.clone
|
|
167
|
+
|
|
168
|
+
x = Args::UnitConversion.parse(card['x'], @dpi, @cell_px)
|
|
169
|
+
y = Args::UnitConversion.parse(card['y'], @dpi, @cell_px)
|
|
170
|
+
if @template_hash['position_reference'] == :center
|
|
171
|
+
# Normalize it to a top-left positional reference
|
|
172
|
+
x -= card_width / 2
|
|
173
|
+
y -= card_height / 2
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
new_card['x'] = x
|
|
177
|
+
new_card['y'] = y
|
|
178
|
+
new_card['rotate'] = parse_rotate_param(
|
|
179
|
+
card['rotate'] ? card['rotate'] : @template_hash['rotate'])
|
|
180
|
+
new_card['flip_vertical'] = card['flip_vertical'] == true
|
|
181
|
+
new_card['flip_horizontal'] = card['flip_horizontal'] == true
|
|
182
|
+
new_card
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def parse_rotate_param(val)
|
|
186
|
+
if val == :clockwise
|
|
187
|
+
0.5 * Math::PI
|
|
188
|
+
elsif val == :counterclockwise
|
|
189
|
+
1.5 * Math::PI
|
|
190
|
+
elsif val == :turnaround
|
|
191
|
+
Math::PI
|
|
192
|
+
elsif val.is_a? String
|
|
193
|
+
if val.end_with? 'deg'
|
|
194
|
+
val.gsub(/deg$/, '').to_f / 180 * Math::PI
|
|
195
|
+
elsif val.end_with? 'rad'
|
|
196
|
+
val.gsub(/rad$/, '').to_f
|
|
197
|
+
else
|
|
198
|
+
val.to_f
|
|
199
|
+
end
|
|
200
|
+
elsif val.nil?
|
|
201
|
+
0.0
|
|
202
|
+
else
|
|
203
|
+
val.to_f
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
end
|
|
208
|
+
end
|
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
module Squib
|
|
2
|
-
module Sprues
|
|
3
|
-
UNIT_REGEX = /^(\d*[.])?\d+(in|cm|mm)$/
|
|
4
|
-
COORD_REGEX = /^\-?(\d*[.])?\d+(in|cm|mm)$/
|
|
5
|
-
ROTATE_REGEX = /^\-?(\d*[.])?\d+(deg|rad)?$/
|
|
6
|
-
SCHEMA = {
|
|
7
|
-
'sheet_width' => UNIT_REGEX,
|
|
8
|
-
'sheet_height' => UNIT_REGEX,
|
|
9
|
-
'card_width' => UNIT_REGEX,
|
|
10
|
-
'card_height' => UNIT_REGEX,
|
|
11
|
-
'position_reference' => ClassyHash::G.enum(:topleft, :center),
|
|
12
|
-
'rotate' => [
|
|
13
|
-
:optional, Numeric,
|
|
14
|
-
ClassyHash::G.enum(:clockwise, :counterclockwise, :turnaround),
|
|
15
|
-
ROTATE_REGEX
|
|
16
|
-
],
|
|
17
|
-
'crop_line' => {
|
|
18
|
-
'style' => [
|
|
19
|
-
ClassyHash::G.enum(:solid, :dotted, :dashed),
|
|
20
|
-
Sprues::CropLineDash::VALIDATION_REGEX
|
|
21
|
-
],
|
|
22
|
-
'width' => UNIT_REGEX,
|
|
23
|
-
'color' => [String, Symbol],
|
|
24
|
-
'overlay' => ClassyHash::G.enum(
|
|
25
|
-
:on_margin, :overlay_on_cards, :beneath_cards
|
|
26
|
-
),
|
|
27
|
-
'lines' => [[{
|
|
28
|
-
'type' => ClassyHash::G.enum(:horizontal, :vertical),
|
|
29
|
-
'position' => UNIT_REGEX,
|
|
30
|
-
'style' => [
|
|
31
|
-
:optional, ClassyHash::G.enum(:solid, :dotted, :dashed)
|
|
32
|
-
],
|
|
33
|
-
'width' => [:optional, UNIT_REGEX],
|
|
34
|
-
'color' => [:optional, String, Symbol],
|
|
35
|
-
'overlay_on_cards' => [:optional, TrueClass]
|
|
36
|
-
}]]
|
|
37
|
-
},
|
|
38
|
-
'cards' => [[{
|
|
39
|
-
'x' => COORD_REGEX,
|
|
40
|
-
'y' => COORD_REGEX,
|
|
41
|
-
'rotate' => [
|
|
42
|
-
:optional, Numeric,
|
|
43
|
-
ClassyHash::G.enum(:clockwise, :counterclockwise, :turnaround),
|
|
44
|
-
ROTATE_REGEX
|
|
45
|
-
],
|
|
46
|
-
'flip_vertical' => [ :optional, FalseClass ],
|
|
47
|
-
'flip_horizontal' => [ :optional, FalseClass ],
|
|
48
|
-
}]]
|
|
49
|
-
}.freeze
|
|
50
|
-
end
|
|
51
|
-
end
|
|
1
|
+
module Squib
|
|
2
|
+
module Sprues
|
|
3
|
+
UNIT_REGEX = /^(\d*[.])?\d+(in|cm|mm)$/
|
|
4
|
+
COORD_REGEX = /^\-?(\d*[.])?\d+(in|cm|mm)$/
|
|
5
|
+
ROTATE_REGEX = /^\-?(\d*[.])?\d+(deg|rad)?$/
|
|
6
|
+
SCHEMA = {
|
|
7
|
+
'sheet_width' => UNIT_REGEX,
|
|
8
|
+
'sheet_height' => UNIT_REGEX,
|
|
9
|
+
'card_width' => UNIT_REGEX,
|
|
10
|
+
'card_height' => UNIT_REGEX,
|
|
11
|
+
'position_reference' => ClassyHash::G.enum(:topleft, :center),
|
|
12
|
+
'rotate' => [
|
|
13
|
+
:optional, Numeric,
|
|
14
|
+
ClassyHash::G.enum(:clockwise, :counterclockwise, :turnaround),
|
|
15
|
+
ROTATE_REGEX
|
|
16
|
+
],
|
|
17
|
+
'crop_line' => {
|
|
18
|
+
'style' => [
|
|
19
|
+
ClassyHash::G.enum(:solid, :dotted, :dashed),
|
|
20
|
+
Sprues::CropLineDash::VALIDATION_REGEX
|
|
21
|
+
],
|
|
22
|
+
'width' => UNIT_REGEX,
|
|
23
|
+
'color' => [String, Symbol],
|
|
24
|
+
'overlay' => ClassyHash::G.enum(
|
|
25
|
+
:on_margin, :overlay_on_cards, :beneath_cards
|
|
26
|
+
),
|
|
27
|
+
'lines' => [[{
|
|
28
|
+
'type' => ClassyHash::G.enum(:horizontal, :vertical),
|
|
29
|
+
'position' => UNIT_REGEX,
|
|
30
|
+
'style' => [
|
|
31
|
+
:optional, ClassyHash::G.enum(:solid, :dotted, :dashed)
|
|
32
|
+
],
|
|
33
|
+
'width' => [:optional, UNIT_REGEX],
|
|
34
|
+
'color' => [:optional, String, Symbol],
|
|
35
|
+
'overlay_on_cards' => [:optional, TrueClass]
|
|
36
|
+
}]]
|
|
37
|
+
},
|
|
38
|
+
'cards' => [[{
|
|
39
|
+
'x' => COORD_REGEX,
|
|
40
|
+
'y' => COORD_REGEX,
|
|
41
|
+
'rotate' => [
|
|
42
|
+
:optional, Numeric,
|
|
43
|
+
ClassyHash::G.enum(:clockwise, :counterclockwise, :turnaround),
|
|
44
|
+
ROTATE_REGEX
|
|
45
|
+
],
|
|
46
|
+
'flip_vertical' => [ :optional, FalseClass ],
|
|
47
|
+
'flip_horizontal' => [ :optional, FalseClass ],
|
|
48
|
+
}]]
|
|
49
|
+
}.freeze
|
|
50
|
+
end
|
|
51
|
+
end
|
data/lib/squib/system_fonts.rb
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
require 'pango'
|
|
2
|
-
module Squib
|
|
3
|
-
|
|
4
|
-
# List all system fonts that Cairo/Pango can see
|
|
5
|
-
# Wow this call was convoluted...
|
|
6
|
-
# Returns array of strings with the names of fonts
|
|
7
|
-
module_function def system_fonts
|
|
8
|
-
cc = Cairo::Context.new(Cairo::ImageSurface.new(0,0)) # empty image
|
|
9
|
-
cc.create_pango_layout.context.families.map {|f| f.name }
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
# Prints out the system fonts in sorted order
|
|
13
|
-
module_function def print_system_fonts
|
|
14
|
-
puts "== DEBUG: Squib knows about these fonts =="
|
|
15
|
-
puts system_fonts.sort
|
|
16
|
-
end
|
|
1
|
+
require 'pango'
|
|
2
|
+
module Squib
|
|
3
|
+
|
|
4
|
+
# List all system fonts that Cairo/Pango can see
|
|
5
|
+
# Wow this call was convoluted...
|
|
6
|
+
# Returns array of strings with the names of fonts
|
|
7
|
+
module_function def system_fonts
|
|
8
|
+
cc = Cairo::Context.new(Cairo::ImageSurface.new(0,0)) # empty image
|
|
9
|
+
cc.create_pango_layout.context.families.map {|f| f.name }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Prints out the system fonts in sorted order
|
|
13
|
+
module_function def print_system_fonts
|
|
14
|
+
puts "== DEBUG: Squib knows about these fonts =="
|
|
15
|
+
puts system_fonts.sort
|
|
16
|
+
end
|
|
17
17
|
end
|
data/lib/squib/version.rb
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
module Squib
|
|
2
|
-
|
|
3
|
-
# The next version to be released.
|
|
4
|
-
# Uses semantic versioning: http://semver.org/
|
|
5
|
-
#
|
|
6
|
-
# Most of the time this is in the alpha of the next release.
|
|
7
|
-
# e.g. v0.0.5a is on its way to becoming v0.0.5
|
|
8
|
-
#
|
|
9
|
-
VERSION = '0.
|
|
10
|
-
end
|
|
11
|
-
|
|
1
|
+
module Squib
|
|
2
|
+
|
|
3
|
+
# The next version to be released.
|
|
4
|
+
# Uses semantic versioning: http://semver.org/
|
|
5
|
+
#
|
|
6
|
+
# Most of the time this is in the alpha of the next release.
|
|
7
|
+
# e.g. v0.0.5a is on its way to becoming v0.0.5
|
|
8
|
+
#
|
|
9
|
+
VERSION = '0.19.0a'
|
|
10
|
+
end
|
|
11
|
+
|
data/lib/squib.rb
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
autoload :Cairo, 'cairo'
|
|
2
|
-
autoload :Pango, 'pango'
|
|
3
|
-
autoload :Rsvg, 'rsvg2'
|
|
4
|
-
require 'logger'
|
|
5
|
-
require 'rainbow/refinement'
|
|
6
|
-
require_relative 'squib/version'
|
|
7
|
-
require_relative 'squib/commands/cli'
|
|
8
|
-
require_relative 'squib/deck'
|
|
9
|
-
require_relative 'squib/card'
|
|
10
|
-
require_relative 'squib/system_fonts'
|
|
11
|
-
|
|
12
|
-
module Squib
|
|
13
|
-
using Rainbow # we can colorize strings now!
|
|
14
|
-
|
|
15
|
-
# Access the internal logger that Squib uses. By default, Squib configure the logger to the WARN level
|
|
16
|
-
# Use this to suppress or increase output levels.
|
|
17
|
-
# @example
|
|
18
|
-
# Squib.logger.level = Logger::DEBUG #show waaaay more information than you probably need, unless you're a dev
|
|
19
|
-
# Squib.logger.level = Logger::ERROR #basically turns it off
|
|
20
|
-
#
|
|
21
|
-
# @return [Logger] the ruby logger
|
|
22
|
-
# @api public
|
|
23
|
-
def logger
|
|
24
|
-
if @logger.nil?
|
|
25
|
-
@logger = Logger.new($stdout)
|
|
26
|
-
@logger.level = Logger::WARN
|
|
27
|
-
@logger.formatter = proc do |severity, datetime, m_progname, msg|
|
|
28
|
-
"[#{datetime.strftime('%F %H:%M:%S')} #{severity.red}] #{msg}\n"
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
@logger
|
|
32
|
-
end
|
|
33
|
-
module_function :logger
|
|
34
|
-
|
|
35
|
-
end
|
|
1
|
+
autoload :Cairo, 'cairo'
|
|
2
|
+
autoload :Pango, 'pango'
|
|
3
|
+
autoload :Rsvg, 'rsvg2'
|
|
4
|
+
require 'logger'
|
|
5
|
+
require 'rainbow/refinement'
|
|
6
|
+
require_relative 'squib/version'
|
|
7
|
+
require_relative 'squib/commands/cli'
|
|
8
|
+
require_relative 'squib/deck'
|
|
9
|
+
require_relative 'squib/card'
|
|
10
|
+
require_relative 'squib/system_fonts'
|
|
11
|
+
|
|
12
|
+
module Squib
|
|
13
|
+
using Rainbow # we can colorize strings now!
|
|
14
|
+
|
|
15
|
+
# Access the internal logger that Squib uses. By default, Squib configure the logger to the WARN level
|
|
16
|
+
# Use this to suppress or increase output levels.
|
|
17
|
+
# @example
|
|
18
|
+
# Squib.logger.level = Logger::DEBUG #show waaaay more information than you probably need, unless you're a dev
|
|
19
|
+
# Squib.logger.level = Logger::ERROR #basically turns it off
|
|
20
|
+
#
|
|
21
|
+
# @return [Logger] the ruby logger
|
|
22
|
+
# @api public
|
|
23
|
+
def logger
|
|
24
|
+
if @logger.nil?
|
|
25
|
+
@logger = Logger.new($stdout)
|
|
26
|
+
@logger.level = Logger::WARN
|
|
27
|
+
@logger.formatter = proc do |severity, datetime, m_progname, msg|
|
|
28
|
+
"[#{datetime.strftime('%F %H:%M:%S')} #{severity.red}] #{msg}\n"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
@logger
|
|
32
|
+
end
|
|
33
|
+
module_function :logger
|
|
34
|
+
|
|
35
|
+
end
|