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,35 +1,35 @@
1
- module Squib
2
- module Sprues
3
- class CropLineDash
4
- VALIDATION_REGEX = /%r{
5
- ^(\d*[.])?\d+(in|cm|mm)
6
- \s+
7
- (\d*[.])?\d+(in|cm|mm)$
8
- }x/
9
-
10
- attr_reader :pattern
11
-
12
- def initialize(value, dpi)
13
- if value == :solid
14
- @pattern = nil
15
- elsif value == :dotted
16
- @pattern = [
17
- Args::UnitConversion.parse('0.2mm', dpi),
18
- Args::UnitConversion.parse('0.5mm', dpi)
19
- ]
20
- elsif value == :dashed
21
- @pattern = [
22
- Args::UnitConversion.parse('2mm', dpi),
23
- Args::UnitConversion.parse('2mm', dpi)
24
- ]
25
- elsif value.is_a? String
26
- @pattern = value.split(' ').map do |val|
27
- Args::UnitConversion.parse val, dpi
28
- end
29
- else
30
- raise ArgumentError, 'Unsupported dash style'
31
- end
32
- end
33
- end
34
- end
35
- end
1
+ module Squib
2
+ module Sprues
3
+ class CropLineDash
4
+ VALIDATION_REGEX = /%r{
5
+ ^(\d*[.])?\d+(in|cm|mm)
6
+ \s+
7
+ (\d*[.])?\d+(in|cm|mm)$
8
+ }x/
9
+
10
+ attr_reader :pattern
11
+
12
+ def initialize(value, dpi)
13
+ if value == :solid
14
+ @pattern = nil
15
+ elsif value == :dotted
16
+ @pattern = [
17
+ Args::UnitConversion.parse('0.2mm', dpi),
18
+ Args::UnitConversion.parse('0.5mm', dpi)
19
+ ]
20
+ elsif value == :dashed
21
+ @pattern = [
22
+ Args::UnitConversion.parse('2mm', dpi),
23
+ Args::UnitConversion.parse('2mm', dpi)
24
+ ]
25
+ elsif value.is_a? String
26
+ @pattern = value.split(' ').map do |val|
27
+ Args::UnitConversion.parse val, dpi
28
+ end
29
+ else
30
+ raise ArgumentError, 'Unsupported dash style'
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,9 +1,9 @@
1
- module Squib
2
- module Sprues
3
- class InvalidSprueDefinition < StandardError
4
- def initialize(file, error)
5
- super("Invalid sprue definition in file: #{file}. #{error.message}")
6
- end
7
- end
8
- end
9
- end
1
+ module Squib
2
+ module Sprues
3
+ class InvalidSprueDefinition < StandardError
4
+ def initialize(file, error)
5
+ super("Invalid sprue definition in file: #{file}. #{error.message}")
6
+ end
7
+ end
8
+ end
9
+ end
@@ -1,203 +1,203 @@
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
- 'position_reference' => :topleft,
22
- 'rotate' => 0.0,
23
- 'crop_line' => {
24
- 'style' => :solid,
25
- 'width' => '0.02mm',
26
- 'color' => :black,
27
- 'overlay' => :on_margin,
28
- 'lines' => []
29
- },
30
- 'cards' => []
31
- }.freeze
32
-
33
- attr_reader :dpi
34
-
35
- def initialize(template_hash, dpi)
36
- @template_hash = template_hash
37
- @dpi = dpi
38
- @crop_line_default = @template_hash['crop_line'].select do |k, _|
39
- %w[style width color].include? k
40
- end
41
- end
42
-
43
- # Load the template definition file
44
- def self.load(file, dpi)
45
- yaml = {}
46
- thefile = file if File.exist?(file) # use custom first
47
- thefile = builtin(file) if File.exist?(builtin(file)) # then builtin
48
- unless File.exist?(thefile)
49
- Squib::logger.error("Sprue not found: #{file}. Falling back to defaults.")
50
- end
51
- yaml = YAML.load_file(thefile) || {} if File.exist? thefile
52
- # Bake the default values into our sprue
53
- new_hash = DEFAULTS.merge(yaml)
54
- new_hash['crop_line'] = DEFAULTS['crop_line'].
55
- merge(new_hash['crop_line'])
56
- warn_unrecognized(yaml)
57
-
58
- # Validate
59
- begin
60
- require 'benchmark'
61
- ClassyHash.validate(new_hash, Sprues::SCHEMA)
62
- rescue ClassyHash::SchemaViolationError => e
63
- raise Sprues::InvalidSprueDefinition.new(thefile, e)
64
- end
65
- Sprue.new new_hash, dpi
66
- end
67
-
68
- def sheet_width
69
- Args::UnitConversion.parse @template_hash['sheet_width'], @dpi
70
- end
71
-
72
- def sheet_height
73
- Args::UnitConversion.parse @template_hash['sheet_height'], @dpi
74
- end
75
-
76
- def card_width
77
- Args::UnitConversion.parse @template_hash['card_width'], @dpi
78
- end
79
-
80
- def card_height
81
- Args::UnitConversion.parse @template_hash['card_height'], @dpi
82
- end
83
-
84
- def card_default_rotation
85
- parse_rotate_param @template_hash['rotate']
86
- end
87
-
88
- def crop_line_overlay
89
- @template_hash['crop_line']['overlay']
90
- end
91
-
92
- def crop_lines
93
- lines = @template_hash['crop_line']['lines'].map(
94
- &method(:parse_crop_line)
95
- )
96
- if block_given?
97
- lines.each { |v| yield v }
98
- else
99
- lines
100
- end
101
- end
102
-
103
- def cards
104
- parsed_cards = @template_hash['cards'].map(&method(:parse_card))
105
- if block_given?
106
- parsed_cards.each { |v| yield v }
107
- else
108
- parsed_cards
109
- end
110
- end
111
-
112
- def margin
113
- # NOTE: There's a baseline of 0.25mm that we can 100% make sure that we
114
- # can overlap really thin lines on the PDF
115
- crop_line_width = [
116
- Args::UnitConversion.parse(@template_hash['crop_line']['width'], @dpi),
117
- Args::UnitConversion.parse('0.25mm', @dpi)
118
- ].max
119
-
120
- parsed_cards = cards
121
- left, right = parsed_cards.minmax { |a, b| a['x'] <=> b['x'] }
122
- top, bottom = parsed_cards.minmax { |a, b| a['y'] <=> b['y'] }
123
-
124
- {
125
- left: left['x'] - crop_line_width,
126
- right: right['x'] + card_width + crop_line_width,
127
- top: top['y'] - crop_line_width,
128
- bottom: bottom['y'] + card_height + crop_line_width
129
- }
130
- end
131
-
132
- # Warn unrecognized options in the template sheet
133
- def self.warn_unrecognized(yaml)
134
- unrec = yaml.keys - DEFAULTS.keys
135
- return unless unrec.any?
136
-
137
- Squib.logger.warn(
138
- "Unrecognized configuration option(s): #{unrec.join(',')}"
139
- )
140
- end
141
-
142
- private
143
-
144
- # Return path for built-in sheet templates
145
- def self.builtin(file)
146
- "#{File.dirname(__FILE__)}/../builtin/sprues/#{file}"
147
- end
148
-
149
- # Parse crop line definitions from template.
150
- def parse_crop_line(line)
151
- new_line = @crop_line_default.merge line
152
- new_line['width'] = Args::UnitConversion.parse(new_line['width'], @dpi)
153
- new_line['color'] = colorify new_line['color']
154
- new_line['style_desc'] = new_line['style']
155
- new_line['style'] = Sprues::CropLineDash.new(new_line['style'], @dpi)
156
- new_line['line'] = Sprues::CropLine.new(
157
- new_line['type'], new_line['position'], sheet_width, sheet_height, @dpi
158
- )
159
- new_line
160
- end
161
-
162
- # Parse card definitions from template.
163
- def parse_card(card)
164
- new_card = card.clone
165
-
166
- x = Args::UnitConversion.parse(card['x'], @dpi)
167
- y = Args::UnitConversion.parse(card['y'], @dpi)
168
- if @template_hash['position_reference'] == :center
169
- # Normalize it to a top-left positional reference
170
- x -= card_width / 2
171
- y -= card_height / 2
172
- end
173
-
174
- new_card['x'] = x
175
- new_card['y'] = y
176
- new_card['rotate'] = parse_rotate_param(
177
- card['rotate'] ? card['rotate'] : @template_hash['rotate'])
178
- new_card
179
- end
180
-
181
- def parse_rotate_param(val)
182
- if val == :clockwise
183
- 0.5 * Math::PI
184
- elsif val == :counterclockwise
185
- 1.5 * Math::PI
186
- elsif val == :turnaround
187
- Math::PI
188
- elsif val.is_a? String
189
- if val.end_with? 'deg'
190
- val.gsub(/deg$/, '').to_f / 180 * Math::PI
191
- elsif val.end_with? 'rad'
192
- val.gsub(/rad$/, '').to_f
193
- else
194
- val.to_f
195
- end
196
- elsif val.nil?
197
- 0.0
198
- else
199
- val.to_f
200
- end
201
- end
202
- end
203
- 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
+ 'position_reference' => :topleft,
22
+ 'rotate' => 0.0,
23
+ 'crop_line' => {
24
+ 'style' => :solid,
25
+ 'width' => '0.02mm',
26
+ 'color' => :black,
27
+ 'overlay' => :on_margin,
28
+ 'lines' => []
29
+ },
30
+ 'cards' => []
31
+ }.freeze
32
+
33
+ attr_reader :dpi
34
+
35
+ def initialize(template_hash, dpi)
36
+ @template_hash = template_hash
37
+ @dpi = dpi
38
+ @crop_line_default = @template_hash['crop_line'].select do |k, _|
39
+ %w[style width color].include? k
40
+ end
41
+ end
42
+
43
+ # Load the template definition file
44
+ def self.load(file, dpi)
45
+ yaml = {}
46
+ thefile = file if File.exist?(file) # use custom first
47
+ thefile = builtin(file) if File.exist?(builtin(file)) # then builtin
48
+ unless File.exist?(thefile)
49
+ Squib::logger.error("Sprue not found: #{file}. Falling back to defaults.")
50
+ end
51
+ yaml = YAML.load_file(thefile) || {} if File.exist? thefile
52
+ # Bake the default values into our sprue
53
+ new_hash = DEFAULTS.merge(yaml)
54
+ new_hash['crop_line'] = DEFAULTS['crop_line'].
55
+ merge(new_hash['crop_line'])
56
+ warn_unrecognized(yaml)
57
+
58
+ # Validate
59
+ begin
60
+ require 'benchmark'
61
+ ClassyHash.validate(new_hash, Sprues::SCHEMA)
62
+ rescue ClassyHash::SchemaViolationError => e
63
+ raise Sprues::InvalidSprueDefinition.new(thefile, e)
64
+ end
65
+ Sprue.new new_hash, dpi
66
+ end
67
+
68
+ def sheet_width
69
+ Args::UnitConversion.parse @template_hash['sheet_width'], @dpi
70
+ end
71
+
72
+ def sheet_height
73
+ Args::UnitConversion.parse @template_hash['sheet_height'], @dpi
74
+ end
75
+
76
+ def card_width
77
+ Args::UnitConversion.parse @template_hash['card_width'], @dpi
78
+ end
79
+
80
+ def card_height
81
+ Args::UnitConversion.parse @template_hash['card_height'], @dpi
82
+ end
83
+
84
+ def card_default_rotation
85
+ parse_rotate_param @template_hash['rotate']
86
+ end
87
+
88
+ def crop_line_overlay
89
+ @template_hash['crop_line']['overlay']
90
+ end
91
+
92
+ def crop_lines
93
+ lines = @template_hash['crop_line']['lines'].map(
94
+ &method(:parse_crop_line)
95
+ )
96
+ if block_given?
97
+ lines.each { |v| yield v }
98
+ else
99
+ lines
100
+ end
101
+ end
102
+
103
+ def cards
104
+ parsed_cards = @template_hash['cards'].map(&method(:parse_card))
105
+ if block_given?
106
+ parsed_cards.each { |v| yield v }
107
+ else
108
+ parsed_cards
109
+ end
110
+ end
111
+
112
+ def margin
113
+ # NOTE: There's a baseline of 0.25mm that we can 100% make sure that we
114
+ # can overlap really thin lines on the PDF
115
+ crop_line_width = [
116
+ Args::UnitConversion.parse(@template_hash['crop_line']['width'], @dpi),
117
+ Args::UnitConversion.parse('0.25mm', @dpi)
118
+ ].max
119
+
120
+ parsed_cards = cards
121
+ left, right = parsed_cards.minmax { |a, b| a['x'] <=> b['x'] }
122
+ top, bottom = parsed_cards.minmax { |a, b| a['y'] <=> b['y'] }
123
+
124
+ {
125
+ left: left['x'] - crop_line_width,
126
+ right: right['x'] + card_width + crop_line_width,
127
+ top: top['y'] - crop_line_width,
128
+ bottom: bottom['y'] + card_height + crop_line_width
129
+ }
130
+ end
131
+
132
+ # Warn unrecognized options in the template sheet
133
+ def self.warn_unrecognized(yaml)
134
+ unrec = yaml.keys - DEFAULTS.keys
135
+ return unless unrec.any?
136
+
137
+ Squib.logger.warn(
138
+ "Unrecognized configuration option(s): #{unrec.join(',')}"
139
+ )
140
+ end
141
+
142
+ private
143
+
144
+ # Return path for built-in sheet templates
145
+ def self.builtin(file)
146
+ "#{File.dirname(__FILE__)}/../builtin/sprues/#{file}"
147
+ end
148
+
149
+ # Parse crop line definitions from template.
150
+ def parse_crop_line(line)
151
+ new_line = @crop_line_default.merge line
152
+ new_line['width'] = Args::UnitConversion.parse(new_line['width'], @dpi)
153
+ new_line['color'] = colorify new_line['color']
154
+ new_line['style_desc'] = new_line['style']
155
+ new_line['style'] = Sprues::CropLineDash.new(new_line['style'], @dpi)
156
+ new_line['line'] = Sprues::CropLine.new(
157
+ new_line['type'], new_line['position'], sheet_width, sheet_height, @dpi
158
+ )
159
+ new_line
160
+ end
161
+
162
+ # Parse card definitions from template.
163
+ def parse_card(card)
164
+ new_card = card.clone
165
+
166
+ x = Args::UnitConversion.parse(card['x'], @dpi)
167
+ y = Args::UnitConversion.parse(card['y'], @dpi)
168
+ if @template_hash['position_reference'] == :center
169
+ # Normalize it to a top-left positional reference
170
+ x -= card_width / 2
171
+ y -= card_height / 2
172
+ end
173
+
174
+ new_card['x'] = x
175
+ new_card['y'] = y
176
+ new_card['rotate'] = parse_rotate_param(
177
+ card['rotate'] ? card['rotate'] : @template_hash['rotate'])
178
+ new_card
179
+ end
180
+
181
+ def parse_rotate_param(val)
182
+ if val == :clockwise
183
+ 0.5 * Math::PI
184
+ elsif val == :counterclockwise
185
+ 1.5 * Math::PI
186
+ elsif val == :turnaround
187
+ Math::PI
188
+ elsif val.is_a? String
189
+ if val.end_with? 'deg'
190
+ val.gsub(/deg$/, '').to_f / 180 * Math::PI
191
+ elsif val.end_with? 'rad'
192
+ val.gsub(/rad$/, '').to_f
193
+ else
194
+ val.to_f
195
+ end
196
+ elsif val.nil?
197
+ 0.0
198
+ else
199
+ val.to_f
200
+ end
201
+ end
202
+ end
203
+ end