squib 0.14.1 → 0.14.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +41 -41
  3. data/.travis.yml +19 -19
  4. data/CHANGELOG.md +357 -340
  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 +49 -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 +49 -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 -0
  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 -0
  86. data/lib/squib/card.rb +75 -74
  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 +139 -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 +108 -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 -205
  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 -126
  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/build_groups/build_groups.rb +36 -36
  121. data/samples/colors/_colors.rb +38 -38
  122. data/samples/colors/_gradients.rb +34 -34
  123. data/samples/config/config_text_markup.rb +20 -20
  124. data/samples/config/custom_config.rb +18 -18
  125. data/samples/data/_csv.rb +33 -33
  126. data/samples/data/_excel.rb +55 -55
  127. data/samples/data/_yaml.rb +12 -12
  128. data/samples/hello_world.rb +6 -6
  129. data/samples/images/_cairo_access.rb +39 -39
  130. data/samples/images/_images.rb +104 -104
  131. data/samples/images/_more_load_images.rb +102 -102
  132. data/samples/intro/01_hello.rb +8 -8
  133. data/samples/intro/02_options.rb +14 -14
  134. data/samples/intro/03_layout.rb +11 -11
  135. data/samples/intro/04_arrays.rb +15 -15
  136. data/samples/intro/05_excel.rb +14 -14
  137. data/samples/layouts/builtin_layouts.rb +97 -97
  138. data/samples/layouts/layouts.rb +71 -71
  139. data/samples/project/src/characters.rb +8 -8
  140. data/samples/project/src/skills.rb +7 -7
  141. data/samples/proofs/_tgc_proofs.rb +16 -16
  142. data/samples/ranges/_ranges.rb +64 -64
  143. data/samples/saves/_hand.rb +23 -23
  144. data/samples/saves/_portrait_landscape.rb +23 -23
  145. data/samples/saves/_save_pdf.rb +29 -29
  146. data/samples/saves/_saves.rb +51 -51
  147. data/samples/saves/_showcase.rb +25 -25
  148. data/samples/shapes/_draw_shapes.rb +60 -58
  149. data/samples/shapes/_proofs.rb +22 -22
  150. data/samples/sprues/_advanced_sprues.rb +24 -24
  151. data/samples/sprues/_builtin_sprues.rb +21 -19
  152. data/samples/sprues/_fold_sheet.rb +27 -27
  153. data/samples/sprues/_hex_tiles.rb +15 -15
  154. data/samples/sprues/_mints.rb +11 -11
  155. data/samples/sprues/_sprue_example.rb +11 -11
  156. data/samples/text/_embed_text.rb +128 -128
  157. data/samples/text/_text.rb +47 -47
  158. data/samples/text/_text_options.rb +102 -102
  159. data/samples/text/bug134.rb +14 -14
  160. data/samples/units/_units.rb +32 -32
  161. data/squib.gemspec +52 -51
  162. metadata +66 -50
@@ -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