squib 0.14.2 → 0.14.3.pre1
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 +41 -41
- data/.travis.yml +19 -19
- data/CHANGELOG.md +366 -357
- data/CONTRIBUTING.md +40 -40
- data/Gemfile +2 -2
- data/LICENSE.txt +22 -22
- data/README.md +121 -121
- data/RELEASE TODO.md +21 -21
- data/Rakefile +48 -48
- data/appveyor.yml +24 -24
- data/bin/squib +4 -4
- data/lib/squib.rb +32 -32
- data/lib/squib/api/background.rb +15 -15
- data/lib/squib/api/data.rb +137 -137
- data/lib/squib/api/groups.rb +54 -54
- data/lib/squib/api/image.rb +49 -49
- data/lib/squib/api/save.rb +83 -83
- data/lib/squib/api/settings.rb +21 -21
- data/lib/squib/api/shapes.rb +124 -124
- data/lib/squib/api/text.rb +25 -25
- data/lib/squib/api/text_embed.rb +71 -71
- data/lib/squib/api/units.rb +27 -27
- data/lib/squib/args/arg_loader.rb +126 -126
- data/lib/squib/args/box.rb +55 -55
- data/lib/squib/args/card_range.rb +32 -32
- data/lib/squib/args/color_validator.rb +12 -12
- data/lib/squib/args/coords.rb +35 -35
- data/lib/squib/args/csv_opts.rb +25 -25
- data/lib/squib/args/dir_validator.rb +16 -16
- data/lib/squib/args/draw.rb +92 -92
- data/lib/squib/args/embed_adjust.rb +25 -25
- data/lib/squib/args/embed_key.rb +17 -17
- data/lib/squib/args/hand_special.rb +37 -37
- data/lib/squib/args/import.rb +40 -40
- data/lib/squib/args/input_file.rb +37 -37
- data/lib/squib/args/paint.rb +44 -44
- data/lib/squib/args/paragraph.rb +116 -116
- data/lib/squib/args/save_batch.rb +63 -63
- data/lib/squib/args/scale_box.rb +53 -53
- data/lib/squib/args/sheet.rb +172 -172
- data/lib/squib/args/showcase_special.rb +38 -38
- data/lib/squib/args/sprue_file.rb +44 -44
- data/lib/squib/args/svg_special.rb +37 -37
- data/lib/squib/args/transform.rb +61 -61
- data/lib/squib/args/typographer.rb +119 -119
- data/lib/squib/args/unit_conversion.rb +29 -29
- data/lib/squib/builtin/layouts/economy.yml +85 -85
- data/lib/squib/builtin/layouts/fantasy.yml +101 -101
- data/lib/squib/builtin/layouts/hand.yml +62 -62
- data/lib/squib/builtin/layouts/party.yml +94 -94
- data/lib/squib/builtin/layouts/playing-card.yml +35 -35
- data/lib/squib/builtin/layouts/tuck_box.yml +46 -46
- data/lib/squib/builtin/projects/advanced/.gitignore +4 -4
- data/lib/squib/builtin/projects/advanced/ABOUT.md +19 -19
- data/lib/squib/builtin/projects/advanced/Gemfile +11 -11
- data/lib/squib/builtin/projects/advanced/Guardfile +21 -21
- data/lib/squib/builtin/projects/advanced/IDEAS.md +22 -22
- data/lib/squib/builtin/projects/advanced/PLAYTESTING.md +26 -26
- data/lib/squib/builtin/projects/advanced/Rakefile +27 -27
- data/lib/squib/builtin/projects/advanced/config.yml +53 -49
- data/lib/squib/builtin/projects/advanced/docs/PNP NOTES.md +3 -3
- data/lib/squib/builtin/projects/advanced/docs/RULES.md +21 -21
- data/lib/squib/builtin/projects/advanced/img/example.svg +60 -60
- data/lib/squib/builtin/projects/advanced/layouts/deck.yml +27 -27
- data/lib/squib/builtin/projects/advanced/src/deck.rb +34 -34
- data/lib/squib/builtin/projects/advanced/src/version.rb +3 -3
- data/lib/squib/builtin/projects/basic/.gitignore +4 -4
- data/lib/squib/builtin/projects/basic/ABOUT.md +19 -19
- data/lib/squib/builtin/projects/basic/Gemfile +3 -3
- data/lib/squib/builtin/projects/basic/IDEAS.md +22 -22
- data/lib/squib/builtin/projects/basic/PLAYTESTING.md +26 -26
- data/lib/squib/builtin/projects/basic/PNP NOTES.md +3 -3
- data/lib/squib/builtin/projects/basic/RULES.md +21 -21
- data/lib/squib/builtin/projects/basic/Rakefile +7 -7
- data/lib/squib/builtin/projects/basic/config.yml +53 -49
- data/lib/squib/builtin/projects/basic/deck.rb +6 -6
- data/lib/squib/builtin/sprues/a4_euro_card.yml +42 -42
- data/lib/squib/builtin/sprues/a4_poker_card_8up.yml +40 -40
- data/lib/squib/builtin/sprues/a4_poker_card_9up.yml +42 -42
- data/lib/squib/builtin/sprues/a4_usa_card.yml +42 -42
- data/lib/squib/builtin/sprues/drivethrucards_1up.yml +10 -10
- data/lib/squib/builtin/sprues/letter_poker_card_9up.yml +25 -25
- data/lib/squib/builtin/sprues/letter_poker_foldable_8up.yml +52 -52
- data/lib/squib/builtin/sprues/printplaygames_18up.yml +68 -68
- data/lib/squib/card.rb +75 -75
- data/lib/squib/commands/cli.rb +39 -39
- data/lib/squib/commands/data/template_option.rb +109 -109
- data/lib/squib/commands/make_sprue.rb +275 -275
- data/lib/squib/commands/new.rb +77 -77
- data/lib/squib/conf.rb +144 -139
- data/lib/squib/constants.rb +17 -17
- data/lib/squib/deck.rb +116 -116
- data/lib/squib/graphics/background.rb +14 -14
- data/lib/squib/graphics/cairo_context_wrapper.rb +113 -113
- data/lib/squib/graphics/embedding_utils.rb +28 -28
- data/lib/squib/graphics/gradient_regex.rb +47 -47
- data/lib/squib/graphics/hand.rb +42 -42
- data/lib/squib/graphics/image.rb +129 -108
- data/lib/squib/graphics/save_doc.rb +61 -61
- data/lib/squib/graphics/save_images.rb +52 -52
- data/lib/squib/graphics/save_pdf.rb +90 -90
- data/lib/squib/graphics/save_sprue.rb +204 -204
- data/lib/squib/graphics/shapes.rb +143 -143
- data/lib/squib/graphics/showcase.rb +85 -85
- data/lib/squib/graphics/text.rb +174 -174
- data/lib/squib/import/data_frame.rb +108 -108
- data/lib/squib/layout_parser.rb +138 -138
- data/lib/squib/progress.rb +38 -38
- data/lib/squib/sample_helpers.rb +34 -34
- data/lib/squib/sprues/crop_line.rb +28 -28
- data/lib/squib/sprues/crop_line_dash.rb +35 -35
- data/lib/squib/sprues/invalid_sprue_definition.rb +9 -9
- data/lib/squib/sprues/sprue.rb +203 -203
- data/lib/squib/sprues/sprue_schema.rb +48 -48
- data/lib/squib/version.rb +10 -10
- data/samples/autoscale_font/_autoscale_font.rb +29 -29
- data/samples/backend/_backend.rb +26 -26
- data/samples/basic.rb +19 -19
- data/samples/bug256/_bug256.rb +13 -0
- data/samples/build_groups/build_groups.rb +36 -36
- data/samples/colors/_colors.rb +38 -38
- data/samples/colors/_gradients.rb +34 -34
- data/samples/config/config_text_markup.rb +20 -20
- data/samples/config/custom_config.rb +18 -18
- data/samples/data/_csv.rb +33 -33
- data/samples/data/_excel.rb +55 -55
- data/samples/data/_yaml.rb +12 -12
- data/samples/hello_world.rb +6 -6
- data/samples/images/_cairo_access.rb +39 -39
- data/samples/images/_images.rb +104 -104
- data/samples/images/_more_load_images.rb +102 -102
- data/samples/intro/01_hello.rb +8 -8
- data/samples/intro/02_options.rb +14 -14
- data/samples/intro/03_layout.rb +11 -11
- data/samples/intro/04_arrays.rb +15 -15
- data/samples/intro/05_excel.rb +14 -14
- data/samples/layouts/builtin_layouts.rb +97 -97
- data/samples/layouts/layouts.rb +71 -71
- data/samples/project/src/characters.rb +8 -8
- data/samples/project/src/skills.rb +7 -7
- data/samples/proofs/_tgc_proofs.rb +16 -16
- data/samples/ranges/_ranges.rb +64 -64
- data/samples/saves/_hand.rb +23 -23
- data/samples/saves/_portrait_landscape.rb +23 -23
- data/samples/saves/_save_pdf.rb +29 -29
- data/samples/saves/_saves.rb +51 -51
- data/samples/saves/_showcase.rb +25 -25
- data/samples/shapes/_draw_shapes.rb +60 -60
- data/samples/shapes/_proofs.rb +22 -22
- data/samples/sprues/_advanced_sprues.rb +24 -24
- data/samples/sprues/_builtin_sprues.rb +21 -21
- data/samples/sprues/_fold_sheet.rb +27 -27
- data/samples/sprues/_hex_tiles.rb +15 -15
- data/samples/sprues/_mints.rb +11 -11
- data/samples/sprues/_sprue_example.rb +11 -11
- data/samples/text/_embed_text.rb +128 -128
- data/samples/text/_text.rb +47 -47
- data/samples/text/_text_options.rb +102 -102
- data/samples/text/bug134.rb +14 -14
- data/samples/units/_units.rb +32 -32
- data/squib.gemspec +52 -52
- metadata +51 -48
data/lib/squib/graphics/text.rb
CHANGED
|
@@ -1,174 +1,174 @@
|
|
|
1
|
-
require 'pango'
|
|
2
|
-
require_relative '../args/typographer'
|
|
3
|
-
require_relative 'embedding_utils'
|
|
4
|
-
|
|
5
|
-
module Squib
|
|
6
|
-
class Card
|
|
7
|
-
|
|
8
|
-
# :nodoc:
|
|
9
|
-
# @api private
|
|
10
|
-
def draw_text_hint(cc, x, y, layout, color)
|
|
11
|
-
color = @deck.text_hint if color.to_s.eql? 'off' and not @deck.text_hint.to_s.eql? 'off'
|
|
12
|
-
return if color.to_s.eql? 'off' or color.nil?
|
|
13
|
-
# when w,h < 0, it was never set. extents[1] are ink extents
|
|
14
|
-
w = layout.width / Pango::SCALE
|
|
15
|
-
w = layout.extents[1].width / Pango::SCALE if w < 0
|
|
16
|
-
h = layout.height / Pango::SCALE
|
|
17
|
-
h = layout.extents[1].height / Pango::SCALE if h < 0
|
|
18
|
-
cc.rounded_rectangle(0, 0, w, h, 0, 0)
|
|
19
|
-
cc.set_source_color(color)
|
|
20
|
-
cc.set_line_width(2.0)
|
|
21
|
-
cc.stroke
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# :nodoc:
|
|
25
|
-
# @api private
|
|
26
|
-
def compute_valign(layout, valign, embed_h)
|
|
27
|
-
return 0 unless layout.height > 0
|
|
28
|
-
ink_extents = layout.extents[1]
|
|
29
|
-
ink_extents.height = embed_h * Pango::SCALE if ink_extents.height == 0 # JUST embed, bug #134
|
|
30
|
-
case valign.to_s.downcase
|
|
31
|
-
when 'middle'
|
|
32
|
-
Pango.pixels((layout.height - ink_extents.height) / 2)
|
|
33
|
-
when 'bottom'
|
|
34
|
-
Pango.pixels(layout.height - ink_extents.height)
|
|
35
|
-
else
|
|
36
|
-
0
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def set_font_rendering_opts!(layout)
|
|
41
|
-
font_options = Cairo::FontOptions.new
|
|
42
|
-
font_options.antialias = Conf::ANTIALIAS_OPTS[(@deck.antialias || 'gray').downcase]
|
|
43
|
-
font_options.hint_metrics = 'on' # TODO make this configurable
|
|
44
|
-
font_options.hint_style = 'full' # TODO make this configurable
|
|
45
|
-
layout.context.font_options = font_options
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# :nodoc:
|
|
49
|
-
# @api private
|
|
50
|
-
def set_wh!(layout, width, height)
|
|
51
|
-
layout.width = width * Pango::SCALE unless width.nil? || width == :auto
|
|
52
|
-
layout.height = height * Pango::SCALE unless height.nil? || height == :auto
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Compute the width of the carve that we need
|
|
56
|
-
def compute_carve(rule, range)
|
|
57
|
-
w = rule[:box].width[@index]
|
|
58
|
-
if w == :native
|
|
59
|
-
file = rule[:file][@index].file
|
|
60
|
-
case rule[:type]
|
|
61
|
-
when :png
|
|
62
|
-
Squib.cache_load_image(file).width.to_f / (range.size - 1)
|
|
63
|
-
when :svg
|
|
64
|
-
svg_data = rule[:svg_args].data[@index]
|
|
65
|
-
unless file.to_s.empty? || svg_data.to_s.empty?
|
|
66
|
-
Squib.logger.warn 'Both an SVG file and SVG data were specified'
|
|
67
|
-
end
|
|
68
|
-
return 0 if (file.nil? or file.eql? '') and svg_data.nil?
|
|
69
|
-
svg_data = File.read(file) if svg_data.to_s.empty?
|
|
70
|
-
RSVG::Handle.new_from_data(svg_data).width
|
|
71
|
-
end
|
|
72
|
-
else
|
|
73
|
-
rule[:box].width[@index] * Pango::SCALE / (range.size - 1)
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# # :nodoc:
|
|
78
|
-
# # @api private
|
|
79
|
-
def embed_images!(embed, str, layout, valign)
|
|
80
|
-
return [] unless embed.rules.any?
|
|
81
|
-
layout.markup = str
|
|
82
|
-
clean_str = layout.text
|
|
83
|
-
attrs = layout.attributes || Pango::AttrList.new
|
|
84
|
-
EmbeddingUtils.indices(clean_str, embed.rules.keys).each do |key, ranges|
|
|
85
|
-
rule = embed.rules[key]
|
|
86
|
-
ranges.each do |range|
|
|
87
|
-
carve = Pango::Rectangle.new(0, 0, compute_carve(rule, range), 0)
|
|
88
|
-
att = Pango::AttrShape.new(carve, carve, rule)
|
|
89
|
-
att.start_index = range.first
|
|
90
|
-
att.end_index = range.last
|
|
91
|
-
attrs.insert(att)
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
layout.attributes = attrs
|
|
95
|
-
layout.context.set_shape_renderer do |cxt, att, do_path|
|
|
96
|
-
unless do_path # when stroking the text
|
|
97
|
-
rule = att.data
|
|
98
|
-
x = Pango.pixels(layout.index_to_pos(att.start_index).x) +
|
|
99
|
-
rule[:adjust].dx[@index]
|
|
100
|
-
y = Pango.pixels(layout.index_to_pos(att.start_index).y) +
|
|
101
|
-
rule[:adjust].dy[@index] +
|
|
102
|
-
compute_valign(layout, valign, rule[:box].height[@index])
|
|
103
|
-
rule[:draw].call(self, x, y)
|
|
104
|
-
cxt.reset_clip
|
|
105
|
-
[cxt, att, do_path]
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def stroke_outline!(cc, layout, draw)
|
|
111
|
-
if draw.stroke_width > 0
|
|
112
|
-
cc.pango_layout_path(layout)
|
|
113
|
-
cc.fancy_stroke draw
|
|
114
|
-
cc.set_source_squibcolor(draw.color)
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def warn_if_ellipsized(layout)
|
|
119
|
-
if @deck.conf.warn_ellipsize? && layout.ellipsized?
|
|
120
|
-
Squib.logger.warn { "Ellipsized (too much text). Card \##{@index}. Text: \"#{layout.text}\". \n (To disable this warning, set warn_ellipsize: false in config.yml)" }
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
# :nodoc:
|
|
125
|
-
# @api private
|
|
126
|
-
def text(embed, para, box, trans, draw, dpi)
|
|
127
|
-
Squib.logger.debug {"Rendering text with: \n#{para} \nat:\n #{box} \ndraw:\n #{draw} \ntransform: #{trans}"}
|
|
128
|
-
extents = nil
|
|
129
|
-
use_cairo do |cc|
|
|
130
|
-
cc.set_source_squibcolor(draw.color)
|
|
131
|
-
cc.translate(box.x, box.y)
|
|
132
|
-
cc.rotate(trans.angle)
|
|
133
|
-
cc.move_to(0, 0)
|
|
134
|
-
|
|
135
|
-
font_desc = Pango::FontDescription.new(para.font)
|
|
136
|
-
font_desc.size = para.font_size * Pango::SCALE unless para.font_size.nil?
|
|
137
|
-
layout = cc.create_pango_layout
|
|
138
|
-
layout.font_description = font_desc
|
|
139
|
-
layout.text = para.str.to_s
|
|
140
|
-
layout.context.resolution = dpi
|
|
141
|
-
if para.markup
|
|
142
|
-
para.str = @deck.typographer.process(layout.text)
|
|
143
|
-
layout.markup = para.str.to_s
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
set_font_rendering_opts!(layout)
|
|
147
|
-
set_wh!(layout, box.width, box.height)
|
|
148
|
-
layout.wrap = para.wrap
|
|
149
|
-
layout.ellipsize = para.ellipsize
|
|
150
|
-
layout.alignment = para.align
|
|
151
|
-
|
|
152
|
-
layout.justify = para.justify unless para.justify.nil?
|
|
153
|
-
layout.spacing = para.spacing unless para.spacing.nil?
|
|
154
|
-
|
|
155
|
-
embed_images!(embed, para.str, layout, para.valign)
|
|
156
|
-
|
|
157
|
-
vertical_start = compute_valign(layout, para.valign, 0)
|
|
158
|
-
cc.move_to(0, vertical_start)
|
|
159
|
-
|
|
160
|
-
stroke_outline!(cc, layout, draw) if draw.stroke_strategy == :stroke_first
|
|
161
|
-
cc.move_to(0, vertical_start)
|
|
162
|
-
|
|
163
|
-
cc.show_pango_layout(layout)
|
|
164
|
-
stroke_outline!(cc, layout, draw) if draw.stroke_strategy == :fill_first
|
|
165
|
-
draw_text_hint(cc, box.x, box.y, layout, para.hint)
|
|
166
|
-
extents = { width: layout.extents[1].width / Pango::SCALE,
|
|
167
|
-
height: layout.extents[1].height / Pango::SCALE }
|
|
168
|
-
warn_if_ellipsized layout
|
|
169
|
-
end
|
|
170
|
-
return extents
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
end
|
|
174
|
-
end
|
|
1
|
+
require 'pango'
|
|
2
|
+
require_relative '../args/typographer'
|
|
3
|
+
require_relative 'embedding_utils'
|
|
4
|
+
|
|
5
|
+
module Squib
|
|
6
|
+
class Card
|
|
7
|
+
|
|
8
|
+
# :nodoc:
|
|
9
|
+
# @api private
|
|
10
|
+
def draw_text_hint(cc, x, y, layout, color)
|
|
11
|
+
color = @deck.text_hint if color.to_s.eql? 'off' and not @deck.text_hint.to_s.eql? 'off'
|
|
12
|
+
return if color.to_s.eql? 'off' or color.nil?
|
|
13
|
+
# when w,h < 0, it was never set. extents[1] are ink extents
|
|
14
|
+
w = layout.width / Pango::SCALE
|
|
15
|
+
w = layout.extents[1].width / Pango::SCALE if w < 0
|
|
16
|
+
h = layout.height / Pango::SCALE
|
|
17
|
+
h = layout.extents[1].height / Pango::SCALE if h < 0
|
|
18
|
+
cc.rounded_rectangle(0, 0, w, h, 0, 0)
|
|
19
|
+
cc.set_source_color(color)
|
|
20
|
+
cc.set_line_width(2.0)
|
|
21
|
+
cc.stroke
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# :nodoc:
|
|
25
|
+
# @api private
|
|
26
|
+
def compute_valign(layout, valign, embed_h)
|
|
27
|
+
return 0 unless layout.height > 0
|
|
28
|
+
ink_extents = layout.extents[1]
|
|
29
|
+
ink_extents.height = embed_h * Pango::SCALE if ink_extents.height == 0 # JUST embed, bug #134
|
|
30
|
+
case valign.to_s.downcase
|
|
31
|
+
when 'middle'
|
|
32
|
+
Pango.pixels((layout.height - ink_extents.height) / 2)
|
|
33
|
+
when 'bottom'
|
|
34
|
+
Pango.pixels(layout.height - ink_extents.height)
|
|
35
|
+
else
|
|
36
|
+
0
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def set_font_rendering_opts!(layout)
|
|
41
|
+
font_options = Cairo::FontOptions.new
|
|
42
|
+
font_options.antialias = Conf::ANTIALIAS_OPTS[(@deck.antialias || 'gray').downcase]
|
|
43
|
+
font_options.hint_metrics = 'on' # TODO make this configurable
|
|
44
|
+
font_options.hint_style = 'full' # TODO make this configurable
|
|
45
|
+
layout.context.font_options = font_options
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# :nodoc:
|
|
49
|
+
# @api private
|
|
50
|
+
def set_wh!(layout, width, height)
|
|
51
|
+
layout.width = width * Pango::SCALE unless width.nil? || width == :auto
|
|
52
|
+
layout.height = height * Pango::SCALE unless height.nil? || height == :auto
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Compute the width of the carve that we need
|
|
56
|
+
def compute_carve(rule, range)
|
|
57
|
+
w = rule[:box].width[@index]
|
|
58
|
+
if w == :native
|
|
59
|
+
file = rule[:file][@index].file
|
|
60
|
+
case rule[:type]
|
|
61
|
+
when :png
|
|
62
|
+
Squib.cache_load_image(file).width.to_f / (range.size - 1)
|
|
63
|
+
when :svg
|
|
64
|
+
svg_data = rule[:svg_args].data[@index]
|
|
65
|
+
unless file.to_s.empty? || svg_data.to_s.empty?
|
|
66
|
+
Squib.logger.warn 'Both an SVG file and SVG data were specified'
|
|
67
|
+
end
|
|
68
|
+
return 0 if (file.nil? or file.eql? '') and svg_data.nil?
|
|
69
|
+
svg_data = File.read(file) if svg_data.to_s.empty?
|
|
70
|
+
RSVG::Handle.new_from_data(svg_data).width
|
|
71
|
+
end
|
|
72
|
+
else
|
|
73
|
+
rule[:box].width[@index] * Pango::SCALE / (range.size - 1)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# # :nodoc:
|
|
78
|
+
# # @api private
|
|
79
|
+
def embed_images!(embed, str, layout, valign)
|
|
80
|
+
return [] unless embed.rules.any?
|
|
81
|
+
layout.markup = str
|
|
82
|
+
clean_str = layout.text
|
|
83
|
+
attrs = layout.attributes || Pango::AttrList.new
|
|
84
|
+
EmbeddingUtils.indices(clean_str, embed.rules.keys).each do |key, ranges|
|
|
85
|
+
rule = embed.rules[key]
|
|
86
|
+
ranges.each do |range|
|
|
87
|
+
carve = Pango::Rectangle.new(0, 0, compute_carve(rule, range), 0)
|
|
88
|
+
att = Pango::AttrShape.new(carve, carve, rule)
|
|
89
|
+
att.start_index = range.first
|
|
90
|
+
att.end_index = range.last
|
|
91
|
+
attrs.insert(att)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
layout.attributes = attrs
|
|
95
|
+
layout.context.set_shape_renderer do |cxt, att, do_path|
|
|
96
|
+
unless do_path # when stroking the text
|
|
97
|
+
rule = att.data
|
|
98
|
+
x = Pango.pixels(layout.index_to_pos(att.start_index).x) +
|
|
99
|
+
rule[:adjust].dx[@index]
|
|
100
|
+
y = Pango.pixels(layout.index_to_pos(att.start_index).y) +
|
|
101
|
+
rule[:adjust].dy[@index] +
|
|
102
|
+
compute_valign(layout, valign, rule[:box].height[@index])
|
|
103
|
+
rule[:draw].call(self, x, y)
|
|
104
|
+
cxt.reset_clip
|
|
105
|
+
[cxt, att, do_path]
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def stroke_outline!(cc, layout, draw)
|
|
111
|
+
if draw.stroke_width > 0
|
|
112
|
+
cc.pango_layout_path(layout)
|
|
113
|
+
cc.fancy_stroke draw
|
|
114
|
+
cc.set_source_squibcolor(draw.color)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def warn_if_ellipsized(layout)
|
|
119
|
+
if @deck.conf.warn_ellipsize? && layout.ellipsized?
|
|
120
|
+
Squib.logger.warn { "Ellipsized (too much text). Card \##{@index}. Text: \"#{layout.text}\". \n (To disable this warning, set warn_ellipsize: false in config.yml)" }
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# :nodoc:
|
|
125
|
+
# @api private
|
|
126
|
+
def text(embed, para, box, trans, draw, dpi)
|
|
127
|
+
Squib.logger.debug {"Rendering text with: \n#{para} \nat:\n #{box} \ndraw:\n #{draw} \ntransform: #{trans}"}
|
|
128
|
+
extents = nil
|
|
129
|
+
use_cairo do |cc|
|
|
130
|
+
cc.set_source_squibcolor(draw.color)
|
|
131
|
+
cc.translate(box.x, box.y)
|
|
132
|
+
cc.rotate(trans.angle)
|
|
133
|
+
cc.move_to(0, 0)
|
|
134
|
+
|
|
135
|
+
font_desc = Pango::FontDescription.new(para.font)
|
|
136
|
+
font_desc.size = para.font_size * Pango::SCALE unless para.font_size.nil?
|
|
137
|
+
layout = cc.create_pango_layout
|
|
138
|
+
layout.font_description = font_desc
|
|
139
|
+
layout.text = para.str.to_s
|
|
140
|
+
layout.context.resolution = dpi
|
|
141
|
+
if para.markup
|
|
142
|
+
para.str = @deck.typographer.process(layout.text)
|
|
143
|
+
layout.markup = para.str.to_s
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
set_font_rendering_opts!(layout)
|
|
147
|
+
set_wh!(layout, box.width, box.height)
|
|
148
|
+
layout.wrap = para.wrap
|
|
149
|
+
layout.ellipsize = para.ellipsize
|
|
150
|
+
layout.alignment = para.align
|
|
151
|
+
|
|
152
|
+
layout.justify = para.justify unless para.justify.nil?
|
|
153
|
+
layout.spacing = para.spacing unless para.spacing.nil?
|
|
154
|
+
|
|
155
|
+
embed_images!(embed, para.str, layout, para.valign)
|
|
156
|
+
|
|
157
|
+
vertical_start = compute_valign(layout, para.valign, 0)
|
|
158
|
+
cc.move_to(0, vertical_start)
|
|
159
|
+
|
|
160
|
+
stroke_outline!(cc, layout, draw) if draw.stroke_strategy == :stroke_first
|
|
161
|
+
cc.move_to(0, vertical_start)
|
|
162
|
+
|
|
163
|
+
cc.show_pango_layout(layout)
|
|
164
|
+
stroke_outline!(cc, layout, draw) if draw.stroke_strategy == :fill_first
|
|
165
|
+
draw_text_hint(cc, box.x, box.y, layout, para.hint)
|
|
166
|
+
extents = { width: layout.extents[1].width / Pango::SCALE,
|
|
167
|
+
height: layout.extents[1].height / Pango::SCALE }
|
|
168
|
+
warn_if_ellipsized layout
|
|
169
|
+
end
|
|
170
|
+
return extents
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
end
|
|
174
|
+
end
|
|
@@ -1,108 +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
|
-
new_str = str.to_s + ' ' # handle nil & empty strings
|
|
90
|
-
new_str = new_str.
|
|
91
|
-
scan(/.{1,34}/). # break down
|
|
92
|
-
map { |s| (' ' * max_col) + " | " + s.ljust(34) }.
|
|
93
|
-
join(" |\n").
|
|
94
|
-
lstrip # initially no whitespace next to key
|
|
95
|
-
return new_str + " |\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
|
|
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
|
+
new_str = str.to_s + ' ' # handle nil & empty strings
|
|
90
|
+
new_str = new_str.
|
|
91
|
+
scan(/.{1,34}/). # break down
|
|
92
|
+
map { |s| (' ' * max_col) + " | " + s.ljust(34) }.
|
|
93
|
+
join(" |\n").
|
|
94
|
+
lstrip # initially no whitespace next to key
|
|
95
|
+
return new_str + " |\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
|