squib 0.14.3.pre1 → 0.16.0.pre.preview1
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 +58 -2
- data/Dockerfile +27 -0
- data/Guardfile +8 -0
- data/README.md +24 -6
- data/RELEASE TODO.md +1 -0
- data/Rakefile +3 -0
- data/lib/squib.rb +3 -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/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 +147 -149
- data/lib/squib/args/showcase_special.rb +32 -29
- 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 +56 -0
- data/lib/squib/builtin/projects/advanced/config.yml +3 -6
- data/lib/squib/builtin/projects/basic/config.yml +3 -6
- data/lib/squib/commands/make_sprue.rb +2 -0
- data/lib/squib/conf.rb +5 -5
- 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 +47 -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 +2 -2
- data/lib/squib/graphics/image.rb +0 -6
- data/lib/squib/graphics/save_images.rb +3 -3
- data/lib/squib/graphics/save_sprue.rb +39 -12
- data/lib/squib/graphics/showcase.rb +1 -1
- 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 +19 -14
- data/lib/squib/sprues/sprue_schema.rb +4 -2
- data/lib/squib/version.rb +1 -1
- data/samples/autoscale_font/_autoscale_font.rb +77 -8
- data/samples/colors/_colors.rb +11 -5
- data/samples/colors/_switch_color.rb +33 -0
- data/samples/data/_excel.rb +1 -1
- data/samples/images/_more_load_images.rb +1 -1
- data/samples/ranges/_ranges.rb +1 -1
- data/samples/saves/_save_filenames.rb +28 -0
- data/samples/saves/_save_pdf.rb +1 -1
- data/samples/saves/_saves.rb +2 -1
- data/samples/shapes/_draw_shapes.rb +2 -2
- data/samples/sprues/_advanced_sprues.rb +4 -3
- data/samples/sprues/_builtin_sprues.rb +1 -0
- data/samples/sprues/_fold_sheet.rb +4 -1
- 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 +49 -0
- data/samples/units/_units.rb +7 -0
- data/squib.gemspec +19 -13
- metadata +151 -61
- data/.travis.yml +0 -19
- data/appveyor.yml +0 -24
- 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
- data/samples/bug256/_bug256.rb +0 -13
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rainbow/refinement'
|
2
|
+
|
3
|
+
module Squib::WarnUnexpectedParams
|
4
|
+
using Rainbow # we can colorize strings now!
|
5
|
+
|
6
|
+
def warn_if_unexpected(opts, uplevel: 5)
|
7
|
+
accepted_params = self.class.accepted_params
|
8
|
+
unexpected = opts.keys - accepted_params
|
9
|
+
unexpected.each do |key|
|
10
|
+
warn "Unexpected parameter '#{key.to_s.yellow}:' to #{dsl_method.to_s.cyan}(). Accepted parameters: #{accepted_params}",
|
11
|
+
uplevel: uplevel
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -20,11 +20,11 @@ module Squib
|
|
20
20
|
|
21
21
|
def_delegators :cairo_cxt, :save, :set_source_color, :paint, :restore,
|
22
22
|
:translate, :rotate, :move_to, :update_pango_layout, :width, :height,
|
23
|
-
:show_pango_layout, :rounded_rectangle, :set_line_width, :stroke, :fill,
|
23
|
+
:show_pango_layout, :rectangle, :rounded_rectangle, :set_line_width, :stroke, :fill,
|
24
24
|
:set_source, :scale, :render_rsvg_handle, :circle, :triangle, :line_to,
|
25
25
|
:operator=, :show_page, :clip, :transform, :mask, :create_pango_layout,
|
26
26
|
:antialias=, :curve_to, :matrix, :matrix=, :identity_matrix, :pango_layout_path,
|
27
|
-
:stroke_preserve, :target, :new_path, :fill_preserve, :close_path,
|
27
|
+
:stroke_preserve, :target, :new_path, :new_sub_path, :reset_clip, :fill_preserve, :close_path,
|
28
28
|
:set_line_join, :set_line_cap, :set_dash, :arc, :arc_negative
|
29
29
|
|
30
30
|
# :nodoc:
|
data/lib/squib/graphics/image.rb
CHANGED
@@ -81,12 +81,6 @@ module Squib
|
|
81
81
|
Squib.logger.debug {"Rendering: #{file}, id: #{id} @#{x},#{y} #{width}x#{height}, alpha: #{alpha}, blend: #{blend}, angle: #{angle}, mask: #{mask}"}
|
82
82
|
Squib.logger.warn 'Both an SVG file and SVG data were specified' unless file.to_s.empty? || svg_args.data.to_s.empty?
|
83
83
|
return if (file.nil? or file.eql? '') and svg_args.data.nil? # nothing specified TODO Move this out to arg validator
|
84
|
-
if(box.width == 0 || box.height == 0)
|
85
|
-
if @deck.conf.warn_zero_size_image?
|
86
|
-
Squib.logger.warn "svg: zero-sized width or height detected for #{file}. SVG not drawn."
|
87
|
-
end
|
88
|
-
return
|
89
|
-
end
|
90
84
|
svg_args.data = File.read(file) if svg_args.data.to_s.empty?
|
91
85
|
begin
|
92
86
|
svg = Rsvg::Handle.new_from_data(svg_args.data)
|
@@ -10,7 +10,7 @@ module Squib
|
|
10
10
|
else
|
11
11
|
@cairo_surface
|
12
12
|
end
|
13
|
-
write_png(surface, index, batch
|
13
|
+
write_png(surface, index, batch)
|
14
14
|
end
|
15
15
|
|
16
16
|
# :nodoc:
|
@@ -44,8 +44,8 @@ module Squib
|
|
44
44
|
return new_cc.target
|
45
45
|
end
|
46
46
|
|
47
|
-
def write_png(surface, i,
|
48
|
-
surface.write_to_png("#{dir}/#{prefix}#{count_format % i}.png")
|
47
|
+
def write_png(surface, i, b)
|
48
|
+
surface.write_to_png("#{b.dir}/#{b.prefix}#{b.count_format % i}#{b.suffix}.png")
|
49
49
|
end
|
50
50
|
|
51
51
|
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']
|
@@ -30,15 +30,16 @@ module Squib
|
|
30
30
|
slot = slots[i % per_sheet]
|
31
31
|
|
32
32
|
draw_card cc, card,
|
33
|
-
slot['x']
|
33
|
+
slot['x'] - @sheet_args.trim,
|
34
|
+
slot['y'] - @sheet_args.trim,
|
34
35
|
slot['rotate'],
|
36
|
+
slot['flip_vertical'], slot['flip_horizontal'],
|
35
37
|
@sheet_args.trim, @sheet_args.trim_radius
|
36
|
-
|
37
38
|
bar.increment
|
38
39
|
end
|
39
40
|
|
40
41
|
draw_overlay_above_cards cc
|
41
|
-
cc
|
42
|
+
draw_final_page cc # See bug #320
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
@@ -128,7 +129,7 @@ module Squib
|
|
128
129
|
(@deck.height - 2.0 * @sheet_args.trim) > @tmpl.card_height
|
129
130
|
end
|
130
131
|
|
131
|
-
def draw_card(cc, card, x, y, angle, trim, trim_radius)
|
132
|
+
def draw_card(cc, card, x, y, angle, flip_v, flip_h, trim, trim_radius)
|
132
133
|
# Compute the true size of the card after trimming
|
133
134
|
w = @deck.width - 2.0 * trim
|
134
135
|
h = @deck.height - 2.0 * trim
|
@@ -142,6 +143,7 @@ module Squib
|
|
142
143
|
mat = cc.matrix # Save the transformation matrix to revert later
|
143
144
|
cc.translate x, y
|
144
145
|
cc.translate @deck.width / 2.0, @deck.height / 2.0
|
146
|
+
cc.flip(flip_v, flip_h, 0, 0)
|
145
147
|
cc.rotate angle
|
146
148
|
cc.translate -@deck.width / 2.0, -@deck.height / 2.0
|
147
149
|
cc.rounded_rectangle(trim, trim, w, h, trim_radius, trim_radius) # clip
|
@@ -158,13 +160,25 @@ module Squib
|
|
158
160
|
def init_cc
|
159
161
|
ratio = 72.0 / @deck.dpi
|
160
162
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
163
|
+
slots = @tmpl.cards
|
164
|
+
per_sheet = slots.size
|
165
|
+
|
166
|
+
surface = if per_sheet == 1
|
167
|
+
Cairo::PDFSurface.new(
|
168
|
+
full_filename,
|
169
|
+
(@tmpl.sheet_width - 2 * @sheet_args.trim) * ratio,
|
170
|
+
(@tmpl.sheet_height - 2 *@sheet_args.trim) * ratio
|
171
|
+
)
|
172
|
+
else
|
173
|
+
Cairo::PDFSurface.new(
|
174
|
+
full_filename,
|
175
|
+
@tmpl.sheet_width * ratio,
|
176
|
+
@tmpl.sheet_height * ratio
|
177
|
+
)
|
178
|
+
end
|
166
179
|
|
167
|
-
cc = Cairo::Context.new(surface)
|
180
|
+
cc = CairoContextWrapper.new(Cairo::Context.new(surface))
|
181
|
+
# cc = Cairo::Context.new(surface)
|
168
182
|
cc.scale(72.0 / @deck.dpi, 72.0 / @deck.dpi) # make it like pixels
|
169
183
|
cc
|
170
184
|
end
|
@@ -176,6 +190,11 @@ module Squib
|
|
176
190
|
cc
|
177
191
|
end
|
178
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
|
+
|
179
198
|
def full_filename
|
180
199
|
@sheet_args.full_filename
|
181
200
|
end
|
@@ -185,7 +204,8 @@ module Squib
|
|
185
204
|
class SaveSpruePNG < SaveSprue
|
186
205
|
def init_cc
|
187
206
|
surface = Cairo::ImageSurface.new @tmpl.sheet_width, @tmpl.sheet_height
|
188
|
-
Cairo::Context.new(surface)
|
207
|
+
CairoContextWrapper.new(Cairo::Context.new(surface))
|
208
|
+
# Cairo::Context.new(surface)
|
189
209
|
end
|
190
210
|
|
191
211
|
def draw_page(cc)
|
@@ -196,6 +216,13 @@ module Squib
|
|
196
216
|
cc
|
197
217
|
end
|
198
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
|
+
|
199
226
|
def full_filename
|
200
227
|
@sheet_args.full_filename @page_number
|
201
228
|
end
|
@@ -21,7 +21,7 @@ module Squib
|
|
21
21
|
|
22
22
|
cards = range.collect { |i| @cards[i] }
|
23
23
|
cards.each_with_index do |card, i|
|
24
|
-
trimmed = trim_rounded(card.cairo_surface,
|
24
|
+
trimmed = trim_rounded(card.cairo_surface, showcase.trim, showcase.trim_radius)
|
25
25
|
reflected = reflect(trimmed, showcase.reflect_offset, showcase.reflect_percent, showcase.reflect_strength)
|
26
26
|
perspectived = perspective(reflected, showcase.scale, showcase.face_right?)
|
27
27
|
out_cc.set_source(perspectived, sheet.margin + i * perspectived.width * showcase.offset, sheet.margin)
|
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:
|