squib 0.15.3 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/tests.yml +22 -0
  3. data/CHANGELOG.md +35 -1
  4. data/Dockerfile +18 -16
  5. data/Guardfile +8 -0
  6. data/README.md +3 -9
  7. data/RELEASE TODO.md +4 -2
  8. data/Rakefile +3 -0
  9. data/lib/squib.rb +5 -1
  10. data/lib/squib/args/arg_loader.rb +109 -106
  11. data/lib/squib/args/box.rb +52 -48
  12. data/lib/squib/args/card_range.rb +26 -24
  13. data/lib/squib/args/color_validator.rb +4 -9
  14. data/lib/squib/args/coords.rb +39 -25
  15. data/lib/squib/args/csv_opts.rb +13 -16
  16. data/lib/squib/args/dir_validator.rb +7 -12
  17. data/lib/squib/args/draw.rb +69 -68
  18. data/lib/squib/args/drop_shadow.rb +39 -0
  19. data/lib/squib/args/embed_adjust.rb +12 -15
  20. data/lib/squib/args/embed_key.rb +6 -11
  21. data/lib/squib/args/hand_special.rb +25 -25
  22. data/lib/squib/args/import.rb +54 -27
  23. data/lib/squib/args/input_file.rb +22 -26
  24. data/lib/squib/args/paint.rb +30 -31
  25. data/lib/squib/args/paragraph.rb +95 -93
  26. data/lib/squib/args/save_batch.rb +50 -48
  27. data/lib/squib/args/scale_box.rb +43 -39
  28. data/lib/squib/args/sheet.rb +142 -149
  29. data/lib/squib/args/showcase_special.rb +32 -32
  30. data/lib/squib/args/sprue_file.rb +30 -30
  31. data/lib/squib/args/svg_special.rb +26 -26
  32. data/lib/squib/args/transform.rb +48 -54
  33. data/lib/squib/args/typographer.rb +88 -92
  34. data/lib/squib/args/unit_conversion.rb +6 -8
  35. data/lib/squib/args/xywh_shorthands.rb +51 -0
  36. data/lib/squib/builtin/projects/advanced/config.yml +4 -3
  37. data/lib/squib/builtin/projects/basic/config.yml +4 -3
  38. data/lib/squib/conf.rb +5 -0
  39. data/lib/squib/deck.rb +34 -12
  40. data/lib/squib/dsl/background.rb +35 -0
  41. data/lib/squib/dsl/circle.rb +39 -0
  42. data/lib/squib/dsl/csv.rb +42 -0
  43. data/lib/squib/dsl/curve.rb +35 -0
  44. data/lib/squib/dsl/cut_zone.rb +47 -0
  45. data/lib/squib/dsl/ellipse.rb +37 -0
  46. data/lib/squib/dsl/grid.rb +35 -0
  47. data/lib/squib/{api → dsl}/groups.rb +0 -0
  48. data/lib/squib/dsl/hand.rb +42 -0
  49. data/lib/squib/dsl/line.rb +35 -0
  50. data/lib/squib/dsl/png.rb +56 -0
  51. data/lib/squib/dsl/polygon.rb +36 -0
  52. data/lib/squib/dsl/rect.rb +37 -0
  53. data/lib/squib/dsl/safe_zone.rb +48 -0
  54. data/lib/squib/dsl/save.rb +21 -0
  55. data/lib/squib/dsl/save_pdf.rb +50 -0
  56. data/lib/squib/dsl/save_png.rb +48 -0
  57. data/lib/squib/dsl/save_sheet.rb +53 -0
  58. data/lib/squib/dsl/showcase.rb +43 -0
  59. data/lib/squib/dsl/star.rb +37 -0
  60. data/lib/squib/dsl/svg.rb +62 -0
  61. data/lib/squib/dsl/text.rb +54 -0
  62. data/lib/squib/dsl/text_embed.rb +78 -0
  63. data/lib/squib/dsl/triangle.rb +35 -0
  64. data/lib/squib/{api → dsl}/units.rb +10 -0
  65. data/lib/squib/dsl/xlsx.rb +40 -0
  66. data/lib/squib/dsl/yaml.rb +40 -0
  67. data/lib/squib/errors_warnings/warn_unexpected_params.rb +14 -0
  68. data/lib/squib/graphics/cairo_context_wrapper.rb +8 -6
  69. data/lib/squib/graphics/save_images.rb +54 -15
  70. data/lib/squib/graphics/save_sprue.rb +14 -2
  71. data/lib/squib/graphics/text.rb +37 -9
  72. data/lib/squib/import/csv_importer.rb +45 -0
  73. data/lib/squib/import/quantity_exploder.rb +18 -0
  74. data/lib/squib/import/xlsx_importer.rb +28 -0
  75. data/lib/squib/import/yaml_importer.rb +30 -0
  76. data/lib/squib/layout_parser.rb +24 -7
  77. data/lib/squib/sprues/crop_line.rb +6 -6
  78. data/lib/squib/sprues/crop_line_dash.rb +6 -6
  79. data/lib/squib/sprues/sprue.rb +16 -14
  80. data/lib/squib/system_fonts.rb +17 -0
  81. data/lib/squib/version.rb +2 -1
  82. data/samples/autoscale_font/_autoscale_font.rb +77 -8
  83. data/samples/colors/_switch_color.rb +2 -2
  84. data/samples/data/_excel.rb +1 -1
  85. data/samples/ranges/_ranges.rb +1 -1
  86. data/samples/saves/_save_filenames.rb +4 -0
  87. data/samples/saves/_save_pdf.rb +1 -1
  88. data/samples/saves/_saves.rb +12 -1
  89. data/samples/shadows/_shadow.rb +72 -0
  90. data/samples/shapes/_draw_shapes.rb +2 -2
  91. data/samples/sprues/_builtin_sprues.rb +1 -0
  92. data/samples/sprues/_fold_sheet.rb +4 -1
  93. data/samples/system_font_debug/_list_fonts.rb +14 -0
  94. data/samples/text/_text.rb +6 -1
  95. data/samples/text/_text_options.rb +2 -1
  96. data/samples/units/_cells.rb +51 -0
  97. data/samples/units/_shorthands.rb +48 -0
  98. data/samples/units/_units.rb +7 -0
  99. data/squib.gemspec +14 -8
  100. metadata +134 -39
  101. data/.travis.yml +0 -17
  102. data/appveyor.yml +0 -25
  103. data/lib/squib/api/background.rb +0 -15
  104. data/lib/squib/api/data.rb +0 -137
  105. data/lib/squib/api/image.rb +0 -49
  106. data/lib/squib/api/save.rb +0 -83
  107. data/lib/squib/api/shapes.rb +0 -124
  108. data/lib/squib/api/text.rb +0 -25
  109. data/lib/squib/api/text_embed.rb +0 -71
@@ -1,22 +1,24 @@
1
+ require_relative 'cairo_context_wrapper'
2
+
1
3
  module Squib
2
4
  class Card
3
5
 
4
6
  # :nodoc:
5
7
  # @api private
6
- def save_png(batch)
7
- surface = if preprocess_save?(batch)
8
+ def save_png(batch, shadow)
9
+ surface = if preprocess_save?(batch, shadow)
8
10
  w, h = compute_dimensions(batch.rotate, batch.trim)
9
- preprocessed_save(w, h, batch)
11
+ preprocessed_save(w, h, batch, shadow)
10
12
  else
11
13
  @cairo_surface
12
14
  end
13
- write_png(surface, index, batch.dir, batch.prefix, batch.count_format)
15
+ write_png(surface, index, batch)
14
16
  end
15
17
 
16
18
  # :nodoc:
17
19
  # @api private
18
- def preprocess_save?(batch)
19
- batch.rotate != false || batch.trim > 0
20
+ def preprocess_save?(batch, shadow)
21
+ batch.rotate != false || batch.trim > 0 || !(shadow.shadow_radius.nil?)
20
22
  end
21
23
 
22
24
  def compute_dimensions(rotate, trim)
@@ -27,25 +29,62 @@ module Squib
27
29
  end
28
30
  end
29
31
 
30
- def preprocessed_save(width, height, batch)
31
- new_cc = Cairo::Context.new(Cairo::ImageSurface.new(width, height))
32
+ def preprocessed_save(w, h, batch, shadow)
33
+ new_cc = Cairo::Context.new(Cairo::ImageSurface.new(w, h))
32
34
  trim_radius = batch.trim_radius
33
35
  if batch.rotate != false
34
- new_cc.translate(width * 0.5, height * 0.5)
35
- new_cc.rotate(batch.angle)
36
- new_cc.translate(height * -0.5, width * -0.5)
37
- new_cc.rounded_rectangle(0, 0, height, width, trim_radius, trim_radius)
36
+ new_cc.translate w * 0.5, h * 0.5
37
+ new_cc.rotate batch.angle
38
+ new_cc.translate h * -0.5, w * -0.5
39
+ new_cc.rounded_rectangle(0, 0, h, w, trim_radius, trim_radius)
38
40
  else
39
- new_cc.rounded_rectangle(0, 0, width, height, trim_radius, trim_radius)
41
+ new_cc.rounded_rectangle(0, 0, w, h, trim_radius, trim_radius)
40
42
  end
41
43
  new_cc.clip
42
44
  new_cc.set_source(@cairo_surface, -batch.trim, -batch.trim)
43
45
  new_cc.paint
46
+ new_cc.reset_clip
47
+ new_cc = drop_shadow(new_cc, shadow, batch) unless shadow.shadow_radius.nil?
44
48
  return new_cc.target
45
49
  end
46
50
 
47
- def write_png(surface, i, dir, prefix, count_format)
48
- surface.write_to_png("#{dir}/#{prefix}#{count_format % i}.png")
51
+ # pseudo-blur behave weirdly with a radius of 0 - wrapping
52
+ def blur(cc, r, &block)
53
+ if r == 0
54
+ yield(block)
55
+ else
56
+ cc.pseudo_blur(r, &block)
57
+ end
58
+ end
59
+
60
+ def drop_shadow(cc, s, batch)
61
+ off_x = s.shadow_offset_x
62
+ off_y = s.shadow_offset_y
63
+ s_trim = s.shadow_trim
64
+ s_rad = s.shadow_radius
65
+ new_w = cc.target.width + off_x + 3 * s_rad - (2 * s_trim)
66
+ new_h = cc.target.height + off_y + 3 * s_rad - (2 * s_trim)
67
+ new_cc = Squib::Graphics::CairoContextWrapper.new(
68
+ Cairo::Context.new(Cairo::ImageSurface.new(new_w, new_h)))
69
+ blur(new_cc, s_rad) do
70
+ # fill in with shadow color
71
+ new_cc.set_source_squibcolor s.shadow_color
72
+ new_cc.rectangle 0, 0, new_cc.target.width, new_cc.target.height
73
+ new_cc.fill
74
+ # then, paint but blend with :dest_in to get a shadow-shaped drawing
75
+ new_cc.set_source cc.target, s_rad + off_x, s_rad + off_y
76
+ new_cc.operator = :dest_in # see https://www.cairographics.org/operators/
77
+ new_cc.paint
78
+ end
79
+ new_cc.set_source cc.target, s_rad, s_rad
80
+ new_cc.operator = :over
81
+ new_cc.paint
82
+ return new_cc
83
+ end
84
+
85
+ def write_png(surface, i, b)
86
+ filename = "#{b.dir}/#{b.prefix}#{b.count_format % i}#{b.suffix}.png"
87
+ surface.write_to_png filename
49
88
  end
50
89
 
51
90
  end
@@ -5,7 +5,7 @@ module Squib
5
5
  def initialize(deck, tmpl, sheet_args)
6
6
  @deck = deck
7
7
  @tmpl = tmpl
8
- @page_number = 1
8
+ @page_number = 0
9
9
  @sheet_args = sheet_args # might be Args::Sheet or Args::SaveBatch
10
10
  @overlay_lines = @tmpl.crop_lines.select do |line|
11
11
  line['overlay_on_cards']
@@ -39,7 +39,7 @@ module Squib
39
39
  end
40
40
 
41
41
  draw_overlay_above_cards cc
42
- cc.target.finish
42
+ draw_final_page cc # See bug #320
43
43
  end
44
44
  end
45
45
 
@@ -190,6 +190,11 @@ module Squib
190
190
  cc
191
191
  end
192
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
+
193
198
  def full_filename
194
199
  @sheet_args.full_filename
195
200
  end
@@ -211,6 +216,13 @@ module Squib
211
216
  cc
212
217
  end
213
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
+
214
226
  def full_filename
215
227
  @sheet_args.full_filename @page_number
216
228
  end
@@ -76,7 +76,7 @@ module Squib
76
76
 
77
77
  # # :nodoc:
78
78
  # # @api private
79
- def embed_images!(embed, str, layout, valign)
79
+ def embed_images!(embed, str, layout, valign, scale)
80
80
  return [] unless embed.rules.any?
81
81
  layout.markup = str
82
82
  clean_str = layout.text
@@ -84,7 +84,7 @@ module Squib
84
84
  EmbeddingUtils.indices(clean_str, embed.rules.keys).each do |key, ranges|
85
85
  rule = embed.rules[key]
86
86
  ranges.each do |range|
87
- carve = Pango::Rectangle.new(0, 0, compute_carve(rule, range), 0)
87
+ carve = Pango::Rectangle.new(0, 0, compute_carve(rule, range) * scale, 0)
88
88
  att = Pango::AttrShape.new(carve, carve, rule)
89
89
  att.start_index = range.first
90
90
  att.end_index = range.last
@@ -100,7 +100,7 @@ module Squib
100
100
  y = Pango.pixels(layout.index_to_pos(att.start_index).y) +
101
101
  rule[:adjust].dy[@index] +
102
102
  compute_valign(layout, valign, rule[:box].height[@index])
103
- rule[:draw].call(self, x, y)
103
+ rule[:draw].call(self, x, y, scale)
104
104
  cxt.reset_clip
105
105
  [cxt, att, do_path]
106
106
  end
@@ -121,19 +121,46 @@ module Squib
121
121
  end
122
122
  end
123
123
 
124
- # :nodoc:
125
124
  # @api private
126
125
  def text(embed, para, box, trans, draw, dpi)
126
+ font_desc = Pango::FontDescription.new(para.font)
127
+ font_desc.size = para.font_size * Pango::SCALE if para.font_size.is_a? Numeric
128
+ orig_font_size = font_desc.size
129
+
130
+ # If text autoscaling is enabled, find the largest text size (smaller or equal to the set text size) that fits
131
+ if para.ellipsize == :autoscale
132
+ para.ellipsize = Pango::EllipsizeMode::END
133
+ sizes = sizes = (1 .. font_desc.size).to_a.reverse
134
+
135
+ # Dummy render to an area outside the card with decreasing font sizes until text no longer ellipsizes
136
+ max_fitting_size = sizes.bsearch{ |sz|
137
+ font_desc.size = sz
138
+ extents = render_text(embed, para, box, trans, draw, dpi, font_desc, orig_font_size, true)
139
+ !extents[:ellipsized]
140
+ }
141
+
142
+ if max_fitting_size.nil?
143
+ max_fitting_size = sizes.last
144
+ Squib.logger.warn{"Could not autosize for Card \##{@index} as minimum specified size #{max_fitting_size} still ellipsizes."}
145
+ end
146
+ font_desc.size = max_fitting_size
147
+ end
148
+
149
+ render_text(embed, para, box, trans, draw, dpi, font_desc, orig_font_size, false)
150
+ end
151
+
152
+ # :nodoc:
153
+ # @api private
154
+ def render_text(embed, para, box, trans, draw, dpi, font_desc, orig_font_size, dummy_draw)
127
155
  Squib.logger.debug {"Rendering text with: \n#{para} \nat:\n #{box} \ndraw:\n #{draw} \ntransform: #{trans}"}
128
156
  extents = nil
129
157
  use_cairo do |cc|
130
158
  cc.set_source_squibcolor(draw.color)
131
159
  cc.translate(box.x, box.y)
160
+ cc.translate(-10000, -10000) if dummy_draw
132
161
  cc.rotate(trans.angle)
133
162
  cc.move_to(0, 0)
134
163
 
135
- font_desc = Pango::FontDescription.new(para.font)
136
- font_desc.size = para.font_size * Pango::SCALE unless para.font_size.nil?
137
164
  layout = cc.create_pango_layout
138
165
  layout.font_description = font_desc
139
166
  layout.text = para.str.to_s
@@ -152,7 +179,7 @@ module Squib
152
179
  layout.justify = para.justify unless para.justify.nil?
153
180
  layout.spacing = para.spacing unless para.spacing.nil?
154
181
 
155
- embed_images!(embed, para.str, layout, para.valign)
182
+ embed_images!(embed, para.str, layout, para.valign, font_desc.size / orig_font_size.to_f)
156
183
 
157
184
  vertical_start = compute_valign(layout, para.valign, 0)
158
185
  cc.move_to(0, vertical_start)
@@ -164,8 +191,9 @@ module Squib
164
191
  stroke_outline!(cc, layout, draw) if draw.stroke_strategy == :fill_first
165
192
  draw_text_hint(cc, box.x, box.y, layout, para.hint)
166
193
  extents = { width: layout.extents[1].width / Pango::SCALE,
167
- height: layout.extents[1].height / Pango::SCALE }
168
- warn_if_ellipsized layout
194
+ height: layout.extents[1].height / Pango::SCALE,
195
+ ellipsized: layout.ellipsized?}
196
+ warn_if_ellipsized layout unless dummy_draw
169
197
  end
170
198
  return extents
171
199
  end
@@ -0,0 +1,45 @@
1
+ require 'csv'
2
+ require_relative 'quantity_exploder'
3
+
4
+ module Squib::Import
5
+ class CsvImporter
6
+ include Squib::Import::QuantityExploder
7
+ def import_to_dataframe(import, csv_opts, &block)
8
+ data = import.data.nil? ? File.read(import.file) : import.data
9
+ table = CSV.parse(data, **csv_opts.to_hash)
10
+ check_duplicate_csv_headers(table)
11
+ hash = Squib::DataFrame.new
12
+ table.headers.each do |header|
13
+ new_header = header.to_s
14
+ new_header = new_header.strip if import.strip?
15
+ hash[new_header] ||= table[header]
16
+ end
17
+ if import.strip?
18
+ new_hash = Squib::DataFrame.new
19
+ hash.each do |header, col|
20
+ new_hash[header] = col.map do |str|
21
+ str = str.strip if str.respond_to?(:strip)
22
+ str
23
+ end
24
+ end
25
+ hash = new_hash
26
+ end
27
+ unless block.nil?
28
+ hash.each do |header, col|
29
+ col.map! do |val|
30
+ yield(header, val)
31
+ end
32
+ end
33
+ end
34
+ return explode_quantities(hash, import.explode)
35
+ end
36
+
37
+ def check_duplicate_csv_headers(table)
38
+ if table.headers.size != table.headers.uniq.size
39
+ dups = table.headers.select{|e| table.headers.count(e) > 1 }
40
+ Squib.logger.warn "CSV duplicated the following column keys: #{dups.join(',')}"
41
+ end
42
+ end
43
+ end
44
+ end
45
+
@@ -0,0 +1,18 @@
1
+ module Squib
2
+ module Import
3
+ module QuantityExploder
4
+ def explode_quantities(data, qty)
5
+ return data unless data.col? qty.to_s.strip
6
+ qtys = data[qty]
7
+ new_data = Squib::DataFrame.new
8
+ data.each do |col, arr|
9
+ new_data[col] = []
10
+ qtys.each_with_index do |qty, index|
11
+ qty.to_i.times { new_data[col] << arr[index] }
12
+ end
13
+ end
14
+ return new_data
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ require 'roo'
2
+ require_relative 'quantity_exploder'
3
+
4
+ module Squib::Import
5
+ class XlsxImporter
6
+ include Squib::Import::QuantityExploder
7
+ def import_to_dataframe(import, &block)
8
+ s = Roo::Excelx.new(import.file)
9
+ s.default_sheet = s.sheets[import.sheet]
10
+ data = Squib::DataFrame.new
11
+ s.first_column.upto(s.last_column) do |col|
12
+ header = s.cell(s.first_row, col).to_s
13
+ header.strip! if import.strip?
14
+ data[header] = []
15
+ (s.first_row + 1).upto(s.last_row) do |row|
16
+ cell = s.cell(row, col)
17
+ # Roo hack for avoiding unnecessary .0's on whole integers (https://github.com/roo-rb/roo/issues/139)
18
+ cell = s.excelx_value(row, col) if s.excelx_type(row, col) == [:numeric_or_formula, 'General']
19
+ cell.strip! if cell.respond_to?(:strip) && import.strip?
20
+ cell = block.yield(header, cell) unless block.nil?
21
+ data[header] << cell
22
+ end# row
23
+ end# col
24
+ explode_quantities(data, import.explode)
25
+ end
26
+ end
27
+ end
28
+
@@ -0,0 +1,30 @@
1
+ require 'yaml'
2
+ require_relative 'data_frame'
3
+ require_relative 'quantity_exploder'
4
+
5
+ module Squib::Import
6
+ class YamlImporter
7
+ include Squib::Import::QuantityExploder
8
+ def import_to_dataframe(import, &block)
9
+ data = import.data.nil? ? File.read(import.file) : import.data
10
+ yml = YAML.load(data)
11
+ data = Squib::DataFrame.new
12
+ # Get a universal list of keys to ensure everything is covered.
13
+ keys = yml.map { |c| c.keys}.flatten.uniq
14
+ keys.each { |k| data[k] = [] } #init arrays
15
+ yml.each do |card|
16
+ # nil value if key isn't set.
17
+ keys.each { |k| data[k] << card[k] }
18
+ end
19
+ unless block.nil?
20
+ data.each do |header, col|
21
+ col.map! do |val|
22
+ block.yield(header, val)
23
+ end
24
+ end
25
+ end
26
+ explode_quantities(data, import.explode)
27
+ end
28
+ end
29
+ end
30
+
@@ -1,12 +1,15 @@
1
1
  require 'yaml'
2
+ require_relative 'args/xywh_shorthands'
2
3
 
3
4
  module Squib
4
5
  # Internal class for handling layouts
5
6
  # @api private
6
7
  class LayoutParser
8
+ include Args::XYWHShorthands
7
9
 
8
- def initialize(dpi = 300)
10
+ def initialize(dpi = 300, cell_px = 37.5)
9
11
  @dpi = dpi
12
+ @cell_px = cell_px
10
13
  end
11
14
 
12
15
  # Load the layout file(s), if exists
@@ -67,6 +70,9 @@ module Squib
67
70
  end
68
71
 
69
72
  def handle_relative_operators(parent_val, child_val)
73
+ if uses_operator?(child_val) && !has_digits?(parent_val) && !has_digits?(child_val)
74
+ raise "Layout parse error: can't combine #{parent_val} and #{child_val}"
75
+ end
70
76
  if child_val.to_s.strip.start_with?('+=')
71
77
  add_parent_child(parent_val, child_val)
72
78
  elsif child_val.to_s.strip.start_with?('-=')
@@ -81,29 +87,40 @@ module Squib
81
87
  end
82
88
 
83
89
  def add_parent_child(parent, child)
84
- parent_pixels = Args::UnitConversion.parse(parent, @dpi).to_f
85
- child_pixels = Args::UnitConversion.parse(child.sub('+=', ''), @dpi).to_f
90
+ parent_pixels = Args::UnitConversion.parse(parent, @dpi, @cell_px).to_f
91
+ child_pixels = Args::UnitConversion.parse(child.sub('+=', ''), @dpi, @cell_px).to_f
86
92
  parent_pixels + child_pixels
87
93
  end
88
94
 
89
95
  def sub_parent_child(parent, child)
90
- parent_pixels = Args::UnitConversion.parse(parent, @dpi).to_f
91
- child_pixels = Args::UnitConversion.parse(child.sub('-=', ''), @dpi).to_f
96
+ parent_pixels = Args::UnitConversion.parse(parent, @dpi, @cell_px).to_f
97
+ child_pixels = Args::UnitConversion.parse(child.sub('-=', ''), @dpi, @cell_px).to_f
92
98
  parent_pixels - child_pixels
93
99
  end
94
100
 
95
101
  def mul_parent_child(parent, child)
96
- parent_pixels = Args::UnitConversion.parse(parent, @dpi).to_f
102
+ parent_pixels = Args::UnitConversion.parse(parent, @dpi, @cell_px).to_f
97
103
  child_float = child.sub('*=', '').to_f
98
104
  parent_pixels * child_float
99
105
  end
100
106
 
101
107
  def div_parent_child(parent, child)
102
- parent_pixels = Args::UnitConversion.parse(parent, @dpi).to_f
108
+ parent_pixels = Args::UnitConversion.parse(parent, @dpi, @cell_px).to_f
103
109
  child_float = child.sub('/=', '').to_f
104
110
  parent_pixels / child_float
105
111
  end
106
112
 
113
+ # For relative operators, it's difficult for us to handle
114
+ # some of the shorthands - so let's just freak out if you're trying to use
115
+ # relative operators with words, e.g. "middle += 0.5in"
116
+ def has_digits?(arg)
117
+ /.*\d.*/ === arg.to_s
118
+ end
119
+
120
+ def uses_operator?(arg)
121
+ /^[+=|\-=|\/=|*=].*/ === arg.to_s
122
+ end
123
+
107
124
  # Does this layout entry have an extends field?
108
125
  # i.e. is it a base-case or will it need recursion?
109
126
  # :nodoc:
@@ -3,21 +3,21 @@ module Squib
3
3
  class CropLine
4
4
  attr_reader :x1, :y1, :x2, :y2
5
5
 
6
- def initialize(type, position, sheet_width, sheet_height, dpi)
6
+ def initialize(type, position, sheet_width, sheet_height, dpi, cell_px)
7
7
  method = "parse_#{type}"
8
- send method, position, sheet_width, sheet_height, dpi
8
+ send method, position, sheet_width, sheet_height, dpi, cell_px
9
9
  end
10
10
 
11
- def parse_horizontal(position, sheet_width, _, dpi)
12
- position = Args::UnitConversion.parse(position, dpi)
11
+ def parse_horizontal(position, sheet_width, _, dpi, cell_px)
12
+ position = Args::UnitConversion.parse(position, dpi, cell_px)
13
13
  @x1 = 0
14
14
  @y1 = position
15
15
  @x2 = sheet_width
16
16
  @y2 = position
17
17
  end
18
18
 
19
- def parse_vertical(position, _, sheet_height, dpi)
20
- position = Args::UnitConversion.parse(position, dpi)
19
+ def parse_vertical(position, _, sheet_height, dpi, cell_px)
20
+ position = Args::UnitConversion.parse(position, dpi, cell_px)
21
21
  @x1 = position
22
22
  @y1 = 0
23
23
  @x2 = position