squib 0.11.0 → 0.12.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/.gitignore +3 -0
- data/.travis.yml +5 -3
- data/CHANGELOG.md +24 -2
- data/README.md +2 -4
- data/RELEASE TODO.md +1 -0
- data/Rakefile +1 -1
- data/docs/arrays.rst +5 -2
- data/docs/build_groups.rst +15 -17
- data/docs/colors.rst +29 -3
- data/docs/conf.py +3 -3
- data/docs/config.rst +14 -1
- data/docs/data.rst +8 -4
- data/docs/dsl/configure.rst +18 -0
- data/docs/dsl/csv.rst +19 -0
- data/docs/dsl/data_frame.rst +85 -0
- data/docs/dsl/disable_build.rst +1 -4
- data/docs/dsl/disable_build_globally.rst +46 -0
- data/docs/dsl/enable_build.rst +1 -1
- data/docs/dsl/enable_build_globally.rst +44 -0
- data/docs/dsl/save_pdf.rst +44 -3
- data/docs/dsl/showcase.rst +11 -0
- data/docs/dsl/text.rst +3 -5
- data/docs/dsl/xlsx.rst +6 -0
- data/docs/guides/game_icons.rst +124 -4
- data/docs/guides/getting-started/part_1_zero_to_game.rst +4 -3
- data/docs/guides/getting-started/part_2_iconography.rst +92 -16
- data/docs/guides/getting-started/part_3_workflows.rst +43 -1
- data/docs/guides/getting-started/part_4_ruby_power.rst +18 -0
- data/docs/guides/git.rst +2 -1
- data/docs/guides/guard.rst +84 -0
- data/docs/guides/hello_world.rst +63 -4
- data/docs/guides/projects.rst +35 -0
- data/docs/install.rst +3 -1
- data/docs/layouts.rst +3 -3
- data/docs/learning.rst +3 -1
- data/docs/parameters.rst +14 -6
- data/docs/text_feature.rst +42 -13
- data/docs/units.rst +9 -1
- data/lib/squib/api/data.rb +6 -5
- data/lib/squib/api/groups.rb +14 -7
- data/lib/squib/api/save.rb +2 -1
- data/lib/squib/args/sheet.rb +57 -2
- data/lib/squib/card.rb +8 -0
- data/lib/squib/conf.rb +9 -1
- data/lib/squib/deck.rb +2 -1
- data/lib/squib/graphics/save_doc.rb +0 -45
- data/lib/squib/graphics/save_pdf.rb +85 -0
- data/lib/squib/graphics/showcase.rb +1 -1
- data/lib/squib/import/data_frame.rb +108 -0
- data/lib/squib/version.rb +1 -1
- data/samples/autoscale_font/.gitignore +2 -0
- data/samples/autoscale_font/card_00_expected.png +0 -0
- data/samples/backend/.gitignore +1 -0
- data/samples/backend/_backend-config.yml +5 -0
- data/samples/backend/_backend.rb +2 -9
- data/samples/backend/backend_00_expected.png +0 -0
- data/samples/backend/backend_01_expected.png +0 -0
- data/samples/backend/backend_vectorized_expected.pdf +0 -0
- data/samples/backend/backend_vectors_00_expected.svg +84 -0
- data/samples/backend/backend_vectors_01_expected.svg +84 -0
- data/samples/backend/shiny-purse.png +0 -0
- data/samples/backend/showcase_expected.png +0 -0
- data/samples/backend/spanner.svg +91 -0
- data/samples/build_groups/.gitignore +1 -0
- data/samples/build_groups/Rakefile +25 -0
- data/samples/colors/.gitignore +1 -0
- data/samples/colors/color_constants_00_expected.png +0 -0
- data/samples/colors/colors_00_expected.png +0 -0
- data/samples/colors/gradient_00_expected.png +0 -0
- data/samples/data/.gitignore +1 -0
- data/samples/data/explode_quantities.xlsx +0 -0
- data/samples/data/quantity_explosion.csv +3 -0
- data/samples/data/sample.csv +3 -0
- data/samples/data/sample.xlsx +0 -0
- data/samples/data/sample_csv_00_expected.png +0 -0
- data/samples/data/sample_csv_01_expected.png +0 -0
- data/samples/data/sample_csv_qty_00_expected.png +0 -0
- data/samples/data/sample_excel_00_expected.png +0 -0
- data/samples/data/sample_excel_01_expected.png +0 -0
- data/samples/data/sample_excel_02_expected.png +0 -0
- data/samples/data/sample_excel_resources_00_expected.png +0 -0
- data/samples/data/sample_excel_resources_01_expected.png +0 -0
- data/samples/data/sample_xlsx_qty_00_expected.png +0 -0
- data/samples/images/.gitignore +8 -0
- data/samples/images/_images_00_expected.png +0 -0
- data/samples/images/angler-fish.png +0 -0
- data/samples/images/ball.png +0 -0
- data/samples/images/glass-heart.svg +52 -0
- data/samples/images/grit.png +0 -0
- data/samples/images/offset.svg +85 -0
- data/samples/images/robot-golem.svg +1 -0
- data/samples/images/shiny-purse.png +0 -0
- data/samples/images/spanner.svg +91 -0
- data/samples/images/sprites.png +0 -0
- data/samples/images/with-alpha.png +0 -0
- data/samples/intro/.gitignore +2 -0
- data/samples/intro/auto-repair.svg +1 -0
- data/samples/intro/crawling.svg +1 -0
- data/samples/intro/data.xlsx +0 -0
- data/samples/intro/humans.svg +1 -0
- data/samples/intro/ninja-mask.svg +1 -0
- data/samples/intro/part1_00_expected.png +0 -0
- data/samples/intro/part2_00_expected.png +0 -0
- data/samples/intro/part3_00_expected.png +0 -0
- data/samples/intro/part3_layout.yml +34 -0
- data/samples/intro/part4_00_expected.png +0 -0
- data/samples/intro/part4_01_expected.png +0 -0
- data/samples/intro/part5_00_expected.png +0 -0
- data/samples/intro/part5_01_expected.png +0 -0
- data/samples/intro/part5_02_expected.png +0 -0
- data/samples/intro/part5_03_expected.png +0 -0
- data/samples/intro/part5_hand_expected.png +0 -0
- data/samples/intro/part5_showcase_expected.png +0 -0
- data/samples/intro/pirate-skull.svg +1 -0
- data/samples/intro/robot-golem.svg +1 -0
- data/samples/project/Gemfile +6 -0
- data/samples/project/Guardfile +18 -0
- data/samples/project/Rakefile +25 -0
- data/samples/project/bw/robot-golem.svg +53 -0
- data/samples/project/color/robot-golem.svg +1 -0
- data/samples/project/config.yml +2 -0
- data/samples/project/layouts/characters.yml +3 -0
- data/samples/project/layouts/skills.yml +3 -0
- data/samples/project/src/characters.rb +8 -0
- data/samples/project/src/skills.rb +7 -0
- data/samples/{ranges.rb → ranges/_ranges.rb} +0 -0
- data/samples/ranges/glass-heart.svg +52 -0
- data/samples/ranges/ranges_00_expected.png +0 -0
- data/samples/saves/.gitignore +1 -0
- data/samples/saves/_save_pdf.rb +18 -0
- data/samples/saves/hand_expected.png +0 -0
- data/samples/saves/hand_pretty_expected.png +0 -0
- data/samples/saves/save-pdf-small_expected.pdf +0 -0
- data/samples/saves/save-pdf_expected.pdf +0 -0
- data/samples/saves/save_png_00_expected.png +0 -0
- data/samples/saves/save_png_trimmed_00_expected.png +0 -0
- data/samples/saves/save_sheet_00_expected.png +0 -0
- data/samples/saves/save_sheet_01_expected.png +0 -0
- data/samples/saves/save_sheet_range_00_expected.png +0 -0
- data/samples/saves/save_sheet_range_01_expected.png +0 -0
- data/samples/saves/save_single_sheet_00_expected.png +0 -0
- data/samples/saves/saves_notrim_01_expected.png +0 -0
- data/samples/saves/showcase2_expected.png +0 -0
- data/samples/saves/showcase_expected.png +0 -0
- data/samples/saves/showcase_individual_00_expected.png +0 -0
- data/samples/saves/showcase_individual_01_expected.png +0 -0
- data/samples/saves/showcase_individual_02_expected.png +0 -0
- data/samples/saves/showcase_individual_03_expected.png +0 -0
- data/samples/saves/spanner.svg +91 -0
- data/samples/shapes/.gitignore +1 -0
- data/samples/shapes/shape_00_expected.png +0 -0
- data/samples/text/.gitignore +2 -0
- data/samples/text/README.md +1 -0
- data/samples/text/_text_00_expected.png +0 -0
- data/samples/text/config.yml +2 -0
- data/samples/units/_units.rb +32 -0
- data/samples/units/units_00_expected.png +0 -0
- data/samples/units/using_units.yml +10 -0
- data/spec/api/api_data_spec.rb +18 -18
- data/spec/api/api_groups_spec.rb +49 -0
- data/spec/args/sheet_spec.rb +24 -2
- data/spec/conf_spec.rb +8 -0
- data/spec/data/conf/basic.yml +1 -0
- data/spec/data/samples/autoscale_font/_autoscale_font.rb.txt +3 -0
- data/spec/data/samples/basic.rb.txt +3 -0
- data/spec/data/samples/cairo_access.rb.txt +2 -0
- data/spec/data/samples/colors/_gradients.rb.txt +1 -0
- data/spec/data/samples/config_text_markup.rb.txt +2 -0
- data/spec/data/samples/custom_config.rb.txt +1 -0
- data/spec/data/samples/data/_csv.rb.txt +6 -0
- data/spec/data/samples/data/_excel.rb.txt +13 -0
- data/spec/data/samples/embed_text.rb.txt +4 -0
- data/spec/data/samples/hello_world.rb.txt +2 -0
- data/spec/data/samples/images/_more_load_images.rb.txt +1 -0
- data/spec/data/samples/{ranges.rb.txt → ranges/_ranges.rb.txt} +3 -0
- data/spec/data/samples/saves/_hand.rb.txt +8 -0
- data/spec/data/samples/saves/_portrait_landscape.rb.txt +2 -0
- data/spec/data/samples/saves/_save_pdf.rb.txt +1505 -449
- data/spec/data/samples/saves/_saves.rb.txt +16 -0
- data/spec/data/samples/saves/_showcase.rb.txt +4 -0
- data/spec/data/samples/shapes/_draw_shapes.rb.txt +1 -0
- data/spec/data/samples/text_options.rb.txt +3 -0
- data/spec/data/samples/tgc_proofs.rb.txt +1 -0
- data/spec/data/samples/{units.rb.txt → units/_units.rb.txt} +20 -15
- data/spec/deck_spec.rb +7 -0
- data/spec/graphics/graphics_save_doc_spec.rb +2 -1
- data/spec/import/data_frame_spec.rb +251 -0
- data/spec/samples/run_samples_spec.rb +1 -1
- data/spec/samples/samples_regression_spec.rb +3 -2
- data/squib.gemspec +6 -6
- metadata +144 -24
- data/.gitmodules +0 -36
- data/samples/units.rb +0 -28
data/lib/squib/api/data.rb
CHANGED
|
@@ -3,6 +3,7 @@ require 'csv'
|
|
|
3
3
|
require_relative '../args/input_file'
|
|
4
4
|
require_relative '../args/import'
|
|
5
5
|
require_relative '../args/csv_opts'
|
|
6
|
+
require_relative '../import/data_frame'
|
|
6
7
|
|
|
7
8
|
module Squib
|
|
8
9
|
|
|
@@ -12,7 +13,7 @@ module Squib
|
|
|
12
13
|
import = Args::Import.new.load!(opts)
|
|
13
14
|
s = Roo::Excelx.new(input.file[0])
|
|
14
15
|
s.default_sheet = s.sheets[input.sheet[0]]
|
|
15
|
-
data =
|
|
16
|
+
data = Squib::DataFrame.new
|
|
16
17
|
s.first_column.upto(s.last_column) do |col|
|
|
17
18
|
header = s.cell(s.first_row, col).to_s
|
|
18
19
|
header.strip! if import.strip?
|
|
@@ -39,14 +40,14 @@ module Squib
|
|
|
39
40
|
csv_opts = Args::CSV_Opts.new(opts)
|
|
40
41
|
table = CSV.parse(data, csv_opts.to_hash)
|
|
41
42
|
check_duplicate_csv_headers(table)
|
|
42
|
-
hash =
|
|
43
|
+
hash = Squib::DataFrame.new
|
|
43
44
|
table.headers.each do |header|
|
|
44
45
|
new_header = header.to_s
|
|
45
46
|
new_header = new_header.strip if import.strip?
|
|
46
47
|
hash[new_header] ||= table[header]
|
|
47
48
|
end
|
|
48
49
|
if import.strip?
|
|
49
|
-
new_hash =
|
|
50
|
+
new_hash = Squib::DataFrame.new
|
|
50
51
|
hash.each do |header, col|
|
|
51
52
|
new_hash[header] = col.map do |str|
|
|
52
53
|
str = str.strip if str.respond_to?(:strip)
|
|
@@ -78,9 +79,9 @@ module Squib
|
|
|
78
79
|
|
|
79
80
|
# @api private
|
|
80
81
|
def explode_quantities(data, qty)
|
|
81
|
-
return data unless data.
|
|
82
|
+
return data unless data.col? qty.to_s.strip
|
|
82
83
|
qtys = data[qty]
|
|
83
|
-
new_data =
|
|
84
|
+
new_data = Squib::DataFrame.new
|
|
84
85
|
data.each do |col, arr|
|
|
85
86
|
new_data[col] = []
|
|
86
87
|
qtys.each_with_index do |qty, index|
|
data/lib/squib/api/groups.rb
CHANGED
|
@@ -2,13 +2,20 @@ require 'set'
|
|
|
2
2
|
|
|
3
3
|
module Squib
|
|
4
4
|
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
# DSL method. See http://squib.readthedocs.io
|
|
6
|
+
def enable_build_globally group
|
|
7
|
+
groups = (ENV['SQUIB_BUILD'] ||= '').split(',')
|
|
8
|
+
ENV['SQUIB_BUILD'] = (groups << group).uniq.join(',')
|
|
9
|
+
end
|
|
10
|
+
module_function :enable_build_globally
|
|
11
|
+
|
|
12
|
+
# DSL method. See http://squib.readthedocs.io
|
|
13
|
+
def disable_build_globally group
|
|
14
|
+
groups = (ENV['SQUIB_BUILD'] ||= '').split(',')
|
|
15
|
+
groups.delete(group.to_s)
|
|
16
|
+
ENV['SQUIB_BUILD'] = groups.uniq.join(',')
|
|
17
|
+
end
|
|
18
|
+
module_function :disable_build_globally
|
|
12
19
|
|
|
13
20
|
class Deck
|
|
14
21
|
|
data/lib/squib/api/save.rb
CHANGED
|
@@ -3,6 +3,7 @@ require_relative '../args/hand_special'
|
|
|
3
3
|
require_relative '../args/save_batch'
|
|
4
4
|
require_relative '../args/sheet'
|
|
5
5
|
require_relative '../args/showcase_special'
|
|
6
|
+
require_relative '../graphics/save_pdf'
|
|
6
7
|
|
|
7
8
|
module Squib
|
|
8
9
|
class Deck
|
|
@@ -18,7 +19,7 @@ module Squib
|
|
|
18
19
|
def save_pdf(opts = {})
|
|
19
20
|
range = Args::CardRange.new(opts[:range], deck_size: size)
|
|
20
21
|
sheet = Args::Sheet.new(custom_colors, { file: 'output.pdf' }).load!(opts, expand_by: size, layout: layout, dpi: dpi)
|
|
21
|
-
render_pdf(range, sheet)
|
|
22
|
+
Graphics::SavePDF.new(self).render_pdf(range, sheet)
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
# DSL method. See http://squib.readthedocs.io
|
data/lib/squib/args/sheet.rb
CHANGED
|
@@ -12,14 +12,23 @@ module Squib
|
|
|
12
12
|
include ColorValidator
|
|
13
13
|
include DirValidator
|
|
14
14
|
|
|
15
|
-
def initialize(custom_colors = {}, dsl_method_defaults = {}, deck_size = 1)
|
|
15
|
+
def initialize(custom_colors = {}, dsl_method_defaults = {}, deck_size = 1, dpi = 300)
|
|
16
16
|
@custom_colors = custom_colors
|
|
17
17
|
@dsl_method_defaults = dsl_method_defaults
|
|
18
18
|
@deck_size = deck_size
|
|
19
|
+
@dpi = dpi
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
def self.parameters
|
|
22
23
|
{
|
|
24
|
+
crop_margin_bottom: 0,
|
|
25
|
+
crop_margin_left: 0,
|
|
26
|
+
crop_margin_right: 0,
|
|
27
|
+
crop_margin_top: 0,
|
|
28
|
+
crop_marks: false,
|
|
29
|
+
crop_stroke_color: :black,
|
|
30
|
+
crop_stroke_dash: '',
|
|
31
|
+
crop_stroke_width: 1.5,
|
|
23
32
|
dir: '_output',
|
|
24
33
|
file: 'sheet.png',
|
|
25
34
|
fill_color: :white,
|
|
@@ -39,7 +48,16 @@ module Squib
|
|
|
39
48
|
end
|
|
40
49
|
|
|
41
50
|
def self.params_with_units
|
|
42
|
-
[ :
|
|
51
|
+
[ :crop_margin_bottom, :crop_margin_left, :crop_margin_right,
|
|
52
|
+
:crop_margin_top, :gap, :height, :margin, :trim_radius, :trim,
|
|
53
|
+
:width
|
|
54
|
+
]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def validate_crop_stroke_dash(arg)
|
|
58
|
+
arg.to_s.split.collect do |x|
|
|
59
|
+
UnitConversion.parse(x, @dpi).to_f
|
|
60
|
+
end
|
|
43
61
|
end
|
|
44
62
|
|
|
45
63
|
def validate_fill_color(arg)
|
|
@@ -66,6 +84,43 @@ module Squib
|
|
|
66
84
|
"#{dir}/#{file}"
|
|
67
85
|
end
|
|
68
86
|
|
|
87
|
+
def crop_coords(x, y, deck_w, deck_h)
|
|
88
|
+
[
|
|
89
|
+
{ # Vertical, Upper-left
|
|
90
|
+
x1: x + trim + crop_margin_left, y1: 0,
|
|
91
|
+
x2: x + trim + crop_margin_left, y2: margin - 1
|
|
92
|
+
},
|
|
93
|
+
{ # Vertical , Upper-right
|
|
94
|
+
x1: x + deck_w - trim - crop_margin_right, y1: 0,
|
|
95
|
+
x2: x + deck_w - trim - crop_margin_right, y2: margin - 1
|
|
96
|
+
},
|
|
97
|
+
{ # Vertical , Lower-left
|
|
98
|
+
x1: x + trim + crop_margin_left, y1: height,
|
|
99
|
+
x2: x + trim + crop_margin_left, y2: height - margin + 1
|
|
100
|
+
},
|
|
101
|
+
{ # Vertical , Lower-right
|
|
102
|
+
x1: x + deck_w - trim - crop_margin_right, y1: height,
|
|
103
|
+
x2: x + deck_w - trim - crop_margin_right, y2: height - margin + 1
|
|
104
|
+
},
|
|
105
|
+
{ # Horizont al, Upper-left
|
|
106
|
+
x1: 0 , y1: y + trim + crop_margin_top,
|
|
107
|
+
x2: margin - 1, y2: y + trim + crop_margin_top
|
|
108
|
+
},
|
|
109
|
+
{ # Horizontal, Upper-Right
|
|
110
|
+
x1: width , y1: y + trim + crop_margin_top,
|
|
111
|
+
x2: width - margin + 1, y2: y + trim + crop_margin_top
|
|
112
|
+
},
|
|
113
|
+
{ # Horizontal, Lower-Left
|
|
114
|
+
x1: 0 , y1: y + deck_h - trim - crop_margin_bottom,
|
|
115
|
+
x2: margin - 1, y2: y + deck_h - trim - crop_margin_bottom
|
|
116
|
+
},
|
|
117
|
+
{ # Horizontal, Lower-Right
|
|
118
|
+
x1: width, y1: y + deck_h - trim - crop_margin_bottom,
|
|
119
|
+
x2: width - margin + 1, y2: y + deck_h - trim - crop_margin_bottom
|
|
120
|
+
},
|
|
121
|
+
]
|
|
122
|
+
end
|
|
123
|
+
|
|
69
124
|
end
|
|
70
125
|
|
|
71
126
|
end
|
data/lib/squib/card.rb
CHANGED
|
@@ -51,6 +51,14 @@ module Squib
|
|
|
51
51
|
@cairo_context.restore
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
def finish!
|
|
55
|
+
begin
|
|
56
|
+
@cairo_surface.finish
|
|
57
|
+
rescue Cairo::SurfaceFinishedError
|
|
58
|
+
# do nothin - if it's already finished that's fine
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
54
62
|
########################
|
|
55
63
|
### BACKEND GRAPHICS ###
|
|
56
64
|
########################
|
data/lib/squib/conf.rb
CHANGED
|
@@ -3,6 +3,14 @@ require 'yaml'
|
|
|
3
3
|
require_relative 'args/typographer'
|
|
4
4
|
|
|
5
5
|
module Squib
|
|
6
|
+
USER_CONFIG = {}
|
|
7
|
+
|
|
8
|
+
def configure(opts)
|
|
9
|
+
str_hash = opts.inject({}) { |h, (k, v)| h[k.to_s] = v; h }
|
|
10
|
+
USER_CONFIG.merge! str_hash
|
|
11
|
+
end
|
|
12
|
+
module_function :configure
|
|
13
|
+
|
|
6
14
|
# @api private
|
|
7
15
|
class Conf
|
|
8
16
|
|
|
@@ -40,7 +48,7 @@ module Squib
|
|
|
40
48
|
}
|
|
41
49
|
|
|
42
50
|
def initialize(config_hash = DEFAULTS)
|
|
43
|
-
@config_hash = config_hash
|
|
51
|
+
@config_hash = config_hash.merge USER_CONFIG # programmatic overrides yml
|
|
44
52
|
@typographer = Args::Typographer.new(config_hash)
|
|
45
53
|
normalize_antialias
|
|
46
54
|
end
|
data/lib/squib/deck.rb
CHANGED
|
@@ -26,7 +26,7 @@ module Squib
|
|
|
26
26
|
# Attributes for the width, height (in pixels) and number of cards
|
|
27
27
|
# These are expected to be immuatble for the life of Deck
|
|
28
28
|
# @api private
|
|
29
|
-
attr_reader :width, :height, :cards
|
|
29
|
+
attr_reader :width, :height, :cards, :progress_bar
|
|
30
30
|
|
|
31
31
|
# Delegate these configuration options to the Squib::Conf object
|
|
32
32
|
def_delegators :conf, :antialias, :backend, :count_format, :custom_colors, :dir,
|
|
@@ -73,6 +73,7 @@ module Squib
|
|
|
73
73
|
if block_given?
|
|
74
74
|
instance_eval(&block) # here we go. wheeeee!
|
|
75
75
|
end
|
|
76
|
+
@cards.each { |c| c.finish! }
|
|
76
77
|
end
|
|
77
78
|
|
|
78
79
|
# Directly accesses the array of cards in the deck
|
|
@@ -1,51 +1,6 @@
|
|
|
1
1
|
module Squib
|
|
2
2
|
class Deck
|
|
3
3
|
|
|
4
|
-
# :nodoc:
|
|
5
|
-
# @api private
|
|
6
|
-
def render_pdf(range, sheet)
|
|
7
|
-
file = "#{sheet.dir}/#{sheet.file}"
|
|
8
|
-
cc = Cairo::Context.new(Cairo::PDFSurface.new(file, sheet.width * 72.0 / @dpi, sheet.height * 72.0 / @dpi))
|
|
9
|
-
cc.scale(72.0 / @dpi, 72.0 / @dpi) # for bug #62
|
|
10
|
-
x, y = sheet.margin, sheet.margin
|
|
11
|
-
card_width = @width - 2 * sheet.trim
|
|
12
|
-
card_height = @height - 2 * sheet.trim
|
|
13
|
-
@progress_bar.start("Saving PDF to #{file}", range.size) do |bar|
|
|
14
|
-
range.each do |i|
|
|
15
|
-
card = @cards[i]
|
|
16
|
-
cc.translate(x, y)
|
|
17
|
-
cc.rectangle(sheet.trim, sheet.trim, card_width, card_height)
|
|
18
|
-
cc.clip
|
|
19
|
-
case card.backend.downcase.to_sym
|
|
20
|
-
when :memory
|
|
21
|
-
cc.set_source(card.cairo_surface, 0, 0)
|
|
22
|
-
cc.paint
|
|
23
|
-
when :svg
|
|
24
|
-
card.cairo_surface.finish
|
|
25
|
-
cc.save
|
|
26
|
-
cc.scale(0.8, 0.8) # I really don't know why I needed to do this at all. But 0.8 is the magic number to get this to scale right
|
|
27
|
-
cc.render_rsvg_handle(RSVG::Handle.new_from_file(card.svgfile), nil)
|
|
28
|
-
cc.restore
|
|
29
|
-
else
|
|
30
|
-
abort "No such back end supported for save_pdf: #{backend}"
|
|
31
|
-
end
|
|
32
|
-
bar.increment
|
|
33
|
-
cc.reset_clip
|
|
34
|
-
cc.translate(-x, -y)
|
|
35
|
-
x += card.width + sheet.gap - 2 * sheet.trim
|
|
36
|
-
if x > (sheet.width - card_width - sheet.margin)
|
|
37
|
-
x = sheet.margin
|
|
38
|
-
y += card.height + sheet.gap - 2 * sheet.trim
|
|
39
|
-
if y > (sheet.height - card_height - sheet.margin)
|
|
40
|
-
cc.show_page # next page
|
|
41
|
-
x, y = sheet.margin, sheet.margin
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
cc.target.finish
|
|
47
|
-
end
|
|
48
|
-
|
|
49
4
|
# :nodoc:
|
|
50
5
|
# @api private
|
|
51
6
|
def render_sheet(range, batch, sheet)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
require_relative '../args/sheet'
|
|
2
|
+
|
|
3
|
+
module Squib
|
|
4
|
+
module Graphics
|
|
5
|
+
class SavePDF
|
|
6
|
+
|
|
7
|
+
def initialize(deck)
|
|
8
|
+
@deck = deck
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# :nodoc:
|
|
12
|
+
# @api private
|
|
13
|
+
def render_pdf(range, sheet)
|
|
14
|
+
cc = init_cc(sheet)
|
|
15
|
+
cc.scale(72.0 / @deck.dpi, 72.0 / @deck.dpi) # for bug #62
|
|
16
|
+
x, y = sheet.margin, sheet.margin
|
|
17
|
+
card_width = @deck.width - 2 * sheet.trim
|
|
18
|
+
card_height = @deck.height - 2 * sheet.trim
|
|
19
|
+
track_progress(range, sheet) do |bar|
|
|
20
|
+
range.each do |i|
|
|
21
|
+
card = @deck.cards[i]
|
|
22
|
+
cc.translate(x, y)
|
|
23
|
+
cc.rectangle(sheet.trim, sheet.trim, card_width, card_height)
|
|
24
|
+
cc.clip
|
|
25
|
+
case card.backend.downcase.to_sym
|
|
26
|
+
when :memory
|
|
27
|
+
cc.set_source(card.cairo_surface, 0, 0)
|
|
28
|
+
cc.paint
|
|
29
|
+
when :svg
|
|
30
|
+
card.cairo_surface.finish
|
|
31
|
+
cc.save
|
|
32
|
+
cc.scale(0.8, 0.8) # I really don't know why I needed to do this at all. But 0.8 is the magic number to get this to scale right
|
|
33
|
+
cc.render_rsvg_handle(RSVG::Handle.new_from_file(card.svgfile), nil)
|
|
34
|
+
cc.restore
|
|
35
|
+
else
|
|
36
|
+
abort "No such back end supported for save_pdf: #{backend}"
|
|
37
|
+
end
|
|
38
|
+
bar.increment
|
|
39
|
+
cc.reset_clip
|
|
40
|
+
cc.translate(-x, -y)
|
|
41
|
+
draw_crop_marks(cc, x, y, sheet) if sheet.crop_marks
|
|
42
|
+
x += card.width + sheet.gap - 2 * sheet.trim
|
|
43
|
+
if x > (sheet.width - card_width - sheet.margin)
|
|
44
|
+
x = sheet.margin
|
|
45
|
+
y += card.height + sheet.gap - 2 * sheet.trim
|
|
46
|
+
if y > (sheet.height - card_height - sheet.margin)
|
|
47
|
+
cc.show_page # next page
|
|
48
|
+
x, y = sheet.margin, sheet.margin
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
cc.target.finish
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
# Initialize the Cairo Context
|
|
59
|
+
def init_cc(sheet)
|
|
60
|
+
Cairo::Context.new(Cairo::PDFSurface.new(
|
|
61
|
+
"#{sheet.dir}/#{sheet.file}",
|
|
62
|
+
sheet.width * 72.0 / @deck.dpi, #PDF thinks in 72 DPI "points"
|
|
63
|
+
sheet.height * 72.0 / @deck.dpi)
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def track_progress(range, sheet)
|
|
68
|
+
msg = "Saving PDF to #{sheet.full_filename}"
|
|
69
|
+
@deck.progress_bar.start(msg, range.size) { |bar| yield(bar) }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def draw_crop_marks(cc, x, y, sheet)
|
|
73
|
+
sheet.crop_coords(x, y, @deck.width, @deck.height).each do |coord|
|
|
74
|
+
cc.move_to(coord[:x1], coord[:y1])
|
|
75
|
+
cc.line_to(coord[:x2], coord[:y2])
|
|
76
|
+
cc.set_source_color(sheet.crop_stroke_color)
|
|
77
|
+
cc.set_dash(sheet.crop_stroke_dash)
|
|
78
|
+
cc.set_line_width(sheet.crop_stroke_width)
|
|
79
|
+
cc.stroke
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -33,7 +33,7 @@ module Squib
|
|
|
33
33
|
# :nodoc:
|
|
34
34
|
# @api private
|
|
35
35
|
def trim_rounded(src, trim, radius)
|
|
36
|
-
trim_cc = Cairo::Context.new(Cairo::ImageSurface.new(
|
|
36
|
+
trim_cc = Cairo::Context.new(Cairo::ImageSurface.new(@width - 2.0 * trim, @height - 2.0 * trim))
|
|
37
37
|
trim_cc.rounded_rectangle(0, 0, trim_cc.target.width, trim_cc.target.height, radius, radius)
|
|
38
38
|
trim_cc.set_source(src, -1 * trim, -1 * trim)
|
|
39
39
|
trim_cc.clip
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'forwardable'
|
|
5
|
+
|
|
6
|
+
module Squib
|
|
7
|
+
class DataFrame
|
|
8
|
+
include Enumerable
|
|
9
|
+
|
|
10
|
+
def initialize(hash = {}, def_columns = true)
|
|
11
|
+
@hash = hash
|
|
12
|
+
columns.each { |col| def_column(col) } if def_columns
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def each(&block)
|
|
16
|
+
@hash.each(&block)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def [](i)
|
|
20
|
+
@hash[i]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def []=(col, v)
|
|
24
|
+
@hash[col] = v
|
|
25
|
+
def_column(col)
|
|
26
|
+
return v
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def columns
|
|
30
|
+
@hash.keys
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def ncolumns
|
|
34
|
+
@hash.keys.size
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def col?(col)
|
|
38
|
+
@hash.key? col
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def row(i)
|
|
42
|
+
@hash.inject(Hash.new) { |ret, (name, arr)| ret[name] = arr[i]; ret }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def nrows
|
|
46
|
+
@hash.inject(0) { |max, (_n, col)| col.size > max ? col.size : max }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def to_json
|
|
50
|
+
@hash.to_json
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def to_pretty_json
|
|
54
|
+
JSON.pretty_generate(@hash)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def to_h
|
|
58
|
+
@hash
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def to_pretty_text
|
|
62
|
+
max_col = columns.inject(0) { |max, c | c.length > max ? c.length : max }
|
|
63
|
+
top = " ╭#{'-' * 36}╮\n"
|
|
64
|
+
bottom = " ╰#{'-' * 36}╯\n"
|
|
65
|
+
str = ''
|
|
66
|
+
0.upto(nrows - 1) do | i |
|
|
67
|
+
str += (' ' * max_col) + top
|
|
68
|
+
row(i).each do |col, data|
|
|
69
|
+
str += "#{col.rjust(max_col)} #{wrap_n_pad(data, max_col)}"
|
|
70
|
+
end
|
|
71
|
+
str += (' ' * max_col) + bottom
|
|
72
|
+
end
|
|
73
|
+
return str
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def snake_case(str)
|
|
79
|
+
str.to_s.
|
|
80
|
+
strip.
|
|
81
|
+
gsub(/\s+/,'_').
|
|
82
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
|
83
|
+
gsub(/([a-z]+)([A-Z])/,'\1_\2').
|
|
84
|
+
downcase.
|
|
85
|
+
to_sym
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def wrap_n_pad(str, max_col)
|
|
89
|
+
str.to_s.
|
|
90
|
+
concat(' '). # handle nil & empty strings
|
|
91
|
+
scan(/.{1,34}/).
|
|
92
|
+
map { |s| (' ' * max_col) + " | " + s.ljust(34) }.
|
|
93
|
+
join(" |\n").
|
|
94
|
+
lstrip. # initially no whitespace next to key
|
|
95
|
+
concat(" |\n")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def def_column(col)
|
|
99
|
+
raise "Column #{col} - does not exist" unless @hash.key? col
|
|
100
|
+
method_name = snake_case(col)
|
|
101
|
+
return if self.class.method_defined?(method_name) #warn people? or skip?
|
|
102
|
+
define_singleton_method method_name do
|
|
103
|
+
@hash[col]
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
end
|
|
108
|
+
end
|