squib 0.15.3 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/tests.yml +22 -0
- data/CHANGELOG.md +35 -1
- data/Dockerfile +18 -16
- data/Guardfile +8 -0
- data/README.md +3 -9
- data/RELEASE TODO.md +4 -2
- data/Rakefile +3 -0
- data/lib/squib.rb +5 -1
- data/lib/squib/args/arg_loader.rb +109 -106
- data/lib/squib/args/box.rb +52 -48
- data/lib/squib/args/card_range.rb +26 -24
- data/lib/squib/args/color_validator.rb +4 -9
- data/lib/squib/args/coords.rb +39 -25
- data/lib/squib/args/csv_opts.rb +13 -16
- data/lib/squib/args/dir_validator.rb +7 -12
- data/lib/squib/args/draw.rb +69 -68
- data/lib/squib/args/drop_shadow.rb +39 -0
- data/lib/squib/args/embed_adjust.rb +12 -15
- data/lib/squib/args/embed_key.rb +6 -11
- data/lib/squib/args/hand_special.rb +25 -25
- data/lib/squib/args/import.rb +54 -27
- data/lib/squib/args/input_file.rb +22 -26
- data/lib/squib/args/paint.rb +30 -31
- data/lib/squib/args/paragraph.rb +95 -93
- data/lib/squib/args/save_batch.rb +50 -48
- data/lib/squib/args/scale_box.rb +43 -39
- data/lib/squib/args/sheet.rb +142 -149
- data/lib/squib/args/showcase_special.rb +32 -32
- data/lib/squib/args/sprue_file.rb +30 -30
- data/lib/squib/args/svg_special.rb +26 -26
- data/lib/squib/args/transform.rb +48 -54
- data/lib/squib/args/typographer.rb +88 -92
- data/lib/squib/args/unit_conversion.rb +6 -8
- data/lib/squib/args/xywh_shorthands.rb +51 -0
- data/lib/squib/builtin/projects/advanced/config.yml +4 -3
- data/lib/squib/builtin/projects/basic/config.yml +4 -3
- data/lib/squib/conf.rb +5 -0
- data/lib/squib/deck.rb +34 -12
- data/lib/squib/dsl/background.rb +35 -0
- data/lib/squib/dsl/circle.rb +39 -0
- data/lib/squib/dsl/csv.rb +42 -0
- data/lib/squib/dsl/curve.rb +35 -0
- data/lib/squib/dsl/cut_zone.rb +47 -0
- data/lib/squib/dsl/ellipse.rb +37 -0
- data/lib/squib/dsl/grid.rb +35 -0
- data/lib/squib/{api → dsl}/groups.rb +0 -0
- data/lib/squib/dsl/hand.rb +42 -0
- data/lib/squib/dsl/line.rb +35 -0
- data/lib/squib/dsl/png.rb +56 -0
- data/lib/squib/dsl/polygon.rb +36 -0
- data/lib/squib/dsl/rect.rb +37 -0
- data/lib/squib/dsl/safe_zone.rb +48 -0
- data/lib/squib/dsl/save.rb +21 -0
- data/lib/squib/dsl/save_pdf.rb +50 -0
- data/lib/squib/dsl/save_png.rb +48 -0
- data/lib/squib/dsl/save_sheet.rb +53 -0
- data/lib/squib/dsl/showcase.rb +43 -0
- data/lib/squib/dsl/star.rb +37 -0
- data/lib/squib/dsl/svg.rb +62 -0
- data/lib/squib/dsl/text.rb +54 -0
- data/lib/squib/dsl/text_embed.rb +78 -0
- data/lib/squib/dsl/triangle.rb +35 -0
- data/lib/squib/{api → dsl}/units.rb +10 -0
- data/lib/squib/dsl/xlsx.rb +40 -0
- data/lib/squib/dsl/yaml.rb +40 -0
- data/lib/squib/errors_warnings/warn_unexpected_params.rb +14 -0
- data/lib/squib/graphics/cairo_context_wrapper.rb +8 -6
- data/lib/squib/graphics/save_images.rb +54 -15
- data/lib/squib/graphics/save_sprue.rb +14 -2
- data/lib/squib/graphics/text.rb +37 -9
- data/lib/squib/import/csv_importer.rb +45 -0
- data/lib/squib/import/quantity_exploder.rb +18 -0
- data/lib/squib/import/xlsx_importer.rb +28 -0
- data/lib/squib/import/yaml_importer.rb +30 -0
- data/lib/squib/layout_parser.rb +24 -7
- data/lib/squib/sprues/crop_line.rb +6 -6
- data/lib/squib/sprues/crop_line_dash.rb +6 -6
- data/lib/squib/sprues/sprue.rb +16 -14
- data/lib/squib/system_fonts.rb +17 -0
- data/lib/squib/version.rb +2 -1
- data/samples/autoscale_font/_autoscale_font.rb +77 -8
- data/samples/colors/_switch_color.rb +2 -2
- data/samples/data/_excel.rb +1 -1
- data/samples/ranges/_ranges.rb +1 -1
- data/samples/saves/_save_filenames.rb +4 -0
- data/samples/saves/_save_pdf.rb +1 -1
- data/samples/saves/_saves.rb +12 -1
- data/samples/shadows/_shadow.rb +72 -0
- data/samples/shapes/_draw_shapes.rb +2 -2
- data/samples/sprues/_builtin_sprues.rb +1 -0
- data/samples/sprues/_fold_sheet.rb +4 -1
- data/samples/system_font_debug/_list_fonts.rb +14 -0
- data/samples/text/_text.rb +6 -1
- data/samples/text/_text_options.rb +2 -1
- data/samples/units/_cells.rb +51 -0
- data/samples/units/_shorthands.rb +48 -0
- data/samples/units/_units.rb +7 -0
- data/squib.gemspec +14 -8
- metadata +134 -39
- data/.travis.yml +0 -17
- data/appveyor.yml +0 -25
- data/lib/squib/api/background.rb +0 -15
- data/lib/squib/api/data.rb +0 -137
- data/lib/squib/api/image.rb +0 -49
- data/lib/squib/api/save.rb +0 -83
- data/lib/squib/api/shapes.rb +0 -124
- data/lib/squib/api/text.rb +0 -25
- 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
|
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(
|
31
|
-
new_cc = Cairo::Context.new(Cairo::ImageSurface.new(
|
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
|
35
|
-
new_cc.rotate
|
36
|
-
new_cc.translate
|
37
|
-
new_cc.rounded_rectangle(0, 0,
|
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,
|
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
|
-
|
48
|
-
|
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 =
|
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
|
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
|
data/lib/squib/graphics/text.rb
CHANGED
@@ -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
|
-
|
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
|
+
|
data/lib/squib/layout_parser.rb
CHANGED
@@ -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
|