squib 0.15.3 → 0.17.0
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/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
|