squib 0.14.3.pre1 → 0.16.0.pre.preview1
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 +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:
|