giga-fast-kit 0.0.1
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 +7 -0
- data/giga-fast-kit.gemspec +12 -0
- data/prawn-2.5.0/COPYING +2 -0
- data/prawn-2.5.0/GPLv2 +339 -0
- data/prawn-2.5.0/GPLv3 +674 -0
- data/prawn-2.5.0/LICENSE +56 -0
- data/prawn-2.5.0/data/fonts/Courier-Bold.afm +342 -0
- data/prawn-2.5.0/data/fonts/Courier-BoldOblique.afm +342 -0
- data/prawn-2.5.0/data/fonts/Courier-Oblique.afm +342 -0
- data/prawn-2.5.0/data/fonts/Courier.afm +342 -0
- data/prawn-2.5.0/data/fonts/Helvetica-Bold.afm +2827 -0
- data/prawn-2.5.0/data/fonts/Helvetica-BoldOblique.afm +2827 -0
- data/prawn-2.5.0/data/fonts/Helvetica-Oblique.afm +3051 -0
- data/prawn-2.5.0/data/fonts/Helvetica.afm +3051 -0
- data/prawn-2.5.0/data/fonts/MustRead.html +19 -0
- data/prawn-2.5.0/data/fonts/Symbol.afm +213 -0
- data/prawn-2.5.0/data/fonts/Times-Bold.afm +2588 -0
- data/prawn-2.5.0/data/fonts/Times-BoldItalic.afm +2384 -0
- data/prawn-2.5.0/data/fonts/Times-Italic.afm +2667 -0
- data/prawn-2.5.0/data/fonts/Times-Roman.afm +2419 -0
- data/prawn-2.5.0/data/fonts/ZapfDingbats.afm +225 -0
- data/prawn-2.5.0/lib/prawn/document/bounding_box.rb +625 -0
- data/prawn-2.5.0/lib/prawn/document/column_box.rb +177 -0
- data/prawn-2.5.0/lib/prawn/document/internals.rb +76 -0
- data/prawn-2.5.0/lib/prawn/document/span.rb +65 -0
- data/prawn-2.5.0/lib/prawn/document.rb +846 -0
- data/prawn-2.5.0/lib/prawn/encoding.rb +85 -0
- data/prawn-2.5.0/lib/prawn/errors.rb +73 -0
- data/prawn-2.5.0/lib/prawn/font.rb +567 -0
- data/prawn-2.5.0/lib/prawn/font_metric_cache.rb +50 -0
- data/prawn-2.5.0/lib/prawn/fonts/afm.rb +332 -0
- data/prawn-2.5.0/lib/prawn/fonts/dfont.rb +42 -0
- data/prawn-2.5.0/lib/prawn/fonts/otf.rb +14 -0
- data/prawn-2.5.0/lib/prawn/fonts/to_unicode_cmap.rb +151 -0
- data/prawn-2.5.0/lib/prawn/fonts/ttc.rb +41 -0
- data/prawn-2.5.0/lib/prawn/fonts/ttf.rb +615 -0
- data/prawn-2.5.0/lib/prawn/fonts.rb +14 -0
- data/prawn-2.5.0/lib/prawn/graphics/blend_mode.rb +62 -0
- data/prawn-2.5.0/lib/prawn/graphics/cap_style.rb +44 -0
- data/prawn-2.5.0/lib/prawn/graphics/color.rb +257 -0
- data/prawn-2.5.0/lib/prawn/graphics/dash.rb +123 -0
- data/prawn-2.5.0/lib/prawn/graphics/join_style.rb +60 -0
- data/prawn-2.5.0/lib/prawn/graphics/patterns.rb +389 -0
- data/prawn-2.5.0/lib/prawn/graphics/transformation.rb +172 -0
- data/prawn-2.5.0/lib/prawn/graphics/transparency.rb +79 -0
- data/prawn-2.5.0/lib/prawn/graphics.rb +791 -0
- data/prawn-2.5.0/lib/prawn/grid.rb +449 -0
- data/prawn-2.5.0/lib/prawn/image_handler.rb +61 -0
- data/prawn-2.5.0/lib/prawn/images/image.rb +48 -0
- data/prawn-2.5.0/lib/prawn/images/jpg.rb +121 -0
- data/prawn-2.5.0/lib/prawn/images/png.rb +383 -0
- data/prawn-2.5.0/lib/prawn/images.rb +195 -0
- data/prawn-2.5.0/lib/prawn/measurement_extensions.rb +82 -0
- data/prawn-2.5.0/lib/prawn/measurements.rb +129 -0
- data/prawn-2.5.0/lib/prawn/outline.rb +316 -0
- data/prawn-2.5.0/lib/prawn/repeater.rb +140 -0
- data/prawn-2.5.0/lib/prawn/security/arcfour.rb +54 -0
- data/prawn-2.5.0/lib/prawn/security.rb +308 -0
- data/prawn-2.5.0/lib/prawn/soft_mask.rb +105 -0
- data/prawn-2.5.0/lib/prawn/stamp.rb +147 -0
- data/prawn-2.5.0/lib/prawn/text/box.rb +192 -0
- data/prawn-2.5.0/lib/prawn/text/formatted/arranger.rb +396 -0
- data/prawn-2.5.0/lib/prawn/text/formatted/box.rb +725 -0
- data/prawn-2.5.0/lib/prawn/text/formatted/fragment.rb +373 -0
- data/prawn-2.5.0/lib/prawn/text/formatted/line_wrap.rb +335 -0
- data/prawn-2.5.0/lib/prawn/text/formatted/parser.rb +284 -0
- data/prawn-2.5.0/lib/prawn/text/formatted/wrap.rb +166 -0
- data/prawn-2.5.0/lib/prawn/text/formatted.rb +82 -0
- data/prawn-2.5.0/lib/prawn/text.rb +701 -0
- data/prawn-2.5.0/lib/prawn/transformation_stack.rb +63 -0
- data/prawn-2.5.0/lib/prawn/utilities.rb +34 -0
- data/prawn-2.5.0/lib/prawn/version.rb +6 -0
- data/prawn-2.5.0/lib/prawn/view.rb +116 -0
- data/prawn-2.5.0/lib/prawn.rb +83 -0
- metadata +114 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Prawn
|
|
4
|
+
module Text
|
|
5
|
+
module Formatted
|
|
6
|
+
# Implements a bi-directional parser between a subset of html and
|
|
7
|
+
# formatted text arrays.
|
|
8
|
+
class Parser
|
|
9
|
+
# @group Extension API
|
|
10
|
+
|
|
11
|
+
# Parser regular expression.
|
|
12
|
+
# @private
|
|
13
|
+
PARSER_REGEX =
|
|
14
|
+
begin
|
|
15
|
+
regex_string =
|
|
16
|
+
"\n|" \
|
|
17
|
+
'<b>|</b>|' \
|
|
18
|
+
'<i>|</i>|' \
|
|
19
|
+
'<u>|</u>|' \
|
|
20
|
+
'<strikethrough>|</strikethrough>|' \
|
|
21
|
+
'<sub>|</sub>|' \
|
|
22
|
+
'<sup>|</sup>|' \
|
|
23
|
+
'<link[^>]*>|</link>|' \
|
|
24
|
+
'<color[^>]*>|</color>|' \
|
|
25
|
+
'<font[^>]*>|</font>|' \
|
|
26
|
+
'<strong>|</strong>|' \
|
|
27
|
+
'<em>|</em>|' \
|
|
28
|
+
'<a[^>]*>|</a>|' \
|
|
29
|
+
"[^<\n]+"
|
|
30
|
+
Regexp.new(regex_string, Regexp::MULTILINE)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Escaped characters.
|
|
34
|
+
# @private
|
|
35
|
+
ESCAPE_CHARS = {
|
|
36
|
+
'&' => '&',
|
|
37
|
+
'>' => '>',
|
|
38
|
+
'<' => '<',
|
|
39
|
+
}.freeze
|
|
40
|
+
|
|
41
|
+
# @private
|
|
42
|
+
UNESCAPE_CHARS = ESCAPE_CHARS.invert.freeze
|
|
43
|
+
|
|
44
|
+
# Parse formatted string.
|
|
45
|
+
#
|
|
46
|
+
# @param string [String]
|
|
47
|
+
# @return [Array<Hash>] Text fragments.
|
|
48
|
+
def self.format(string, *_args)
|
|
49
|
+
tokens = string.gsub(%r{<br\s*/?>}, "\n").scan(PARSER_REGEX)
|
|
50
|
+
array_from_tokens(tokens)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Serialize text fragments to an inline format string.
|
|
54
|
+
#
|
|
55
|
+
# @param array [Array<Hash>]
|
|
56
|
+
# @return [String]
|
|
57
|
+
def self.to_string(array)
|
|
58
|
+
prefixes = {
|
|
59
|
+
bold: '<b>',
|
|
60
|
+
italic: '<i>',
|
|
61
|
+
underline: '<u>',
|
|
62
|
+
strikethrough: '<strikethrough>',
|
|
63
|
+
subscript: '<sub>',
|
|
64
|
+
superscript: '<sup>',
|
|
65
|
+
}
|
|
66
|
+
suffixes = {
|
|
67
|
+
bold: '</b>',
|
|
68
|
+
italic: '</i>',
|
|
69
|
+
underline: '</u>',
|
|
70
|
+
strikethrough: '</strikethrough>',
|
|
71
|
+
subscript: '</sub>',
|
|
72
|
+
superscript: '</sup>',
|
|
73
|
+
}
|
|
74
|
+
array
|
|
75
|
+
.map { |hash|
|
|
76
|
+
prefix = ''
|
|
77
|
+
suffix = ''
|
|
78
|
+
hash[:styles]&.each do |style|
|
|
79
|
+
prefix += prefixes[style]
|
|
80
|
+
suffix = suffixes[style] + suffix
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
font = hash[:font] ? " name='#{hash[:font]}'" : nil
|
|
84
|
+
size = hash[:size] ? " size='#{hash[:size]}'" : nil
|
|
85
|
+
character_spacing =
|
|
86
|
+
if hash[:character_spacing]
|
|
87
|
+
" character_spacing='#{hash[:character_spacing]}'"
|
|
88
|
+
end
|
|
89
|
+
if font || size || character_spacing
|
|
90
|
+
prefix += "<font#{font}#{size}#{character_spacing}>"
|
|
91
|
+
suffix = '</font>'
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
link = hash[:link] ? " href='#{hash[:link]}'" : nil
|
|
95
|
+
anchor = hash[:anchor] ? " anchor='#{hash[:anchor]}'" : nil
|
|
96
|
+
if link || anchor
|
|
97
|
+
prefix += "<link#{link}#{anchor}>"
|
|
98
|
+
suffix = '</link>'
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if hash[:color]
|
|
102
|
+
prefix +=
|
|
103
|
+
if hash[:color].is_a?(Array)
|
|
104
|
+
"<color c='#{hash[:color][0]}' " \
|
|
105
|
+
"m='#{hash[:color][1]}' " \
|
|
106
|
+
"y='#{hash[:color][2]}' " \
|
|
107
|
+
"k='#{hash[:color][3]}'>"
|
|
108
|
+
else
|
|
109
|
+
"<color rgb='#{hash[:color]}'>"
|
|
110
|
+
end
|
|
111
|
+
suffix = '</color>'
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
string = escape(hash[:text])
|
|
115
|
+
prefix + string + suffix
|
|
116
|
+
}
|
|
117
|
+
.join
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Break text into paragraphs.
|
|
121
|
+
#
|
|
122
|
+
# @private
|
|
123
|
+
# @param array [Array<Hash>] Text fragments.
|
|
124
|
+
# @return [Array<Array<Hash>>] Pragraphs of text fragments.
|
|
125
|
+
def self.array_paragraphs(array)
|
|
126
|
+
paragraphs = []
|
|
127
|
+
paragraph = []
|
|
128
|
+
previous_string = "\n"
|
|
129
|
+
scan_pattern = /[^\n]+|\n/
|
|
130
|
+
array.each do |hash|
|
|
131
|
+
hash[:text].scan(scan_pattern).each do |string|
|
|
132
|
+
if string == "\n"
|
|
133
|
+
if previous_string == "\n"
|
|
134
|
+
paragraph << hash.dup.merge(text: "\n")
|
|
135
|
+
end
|
|
136
|
+
paragraphs << paragraph unless paragraph.empty?
|
|
137
|
+
paragraph = []
|
|
138
|
+
else
|
|
139
|
+
paragraph << hash.dup.merge(text: string)
|
|
140
|
+
end
|
|
141
|
+
previous_string = string
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
paragraphs << paragraph unless paragraph.empty?
|
|
145
|
+
paragraphs
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# @private
|
|
149
|
+
# @param tokens [Array<String>]
|
|
150
|
+
# @return [Array<Hash>]
|
|
151
|
+
def self.array_from_tokens(tokens)
|
|
152
|
+
array = []
|
|
153
|
+
styles = []
|
|
154
|
+
colors = []
|
|
155
|
+
link = nil
|
|
156
|
+
anchor = nil
|
|
157
|
+
local = nil
|
|
158
|
+
fonts = []
|
|
159
|
+
sizes = []
|
|
160
|
+
character_spacings = []
|
|
161
|
+
|
|
162
|
+
tokens.each do |token|
|
|
163
|
+
case token
|
|
164
|
+
when '<b>', '<strong>'
|
|
165
|
+
styles << :bold
|
|
166
|
+
when '<i>', '<em>'
|
|
167
|
+
styles << :italic
|
|
168
|
+
when '<u>'
|
|
169
|
+
styles << :underline
|
|
170
|
+
when '<strikethrough>'
|
|
171
|
+
styles << :strikethrough
|
|
172
|
+
when '<sub>'
|
|
173
|
+
styles << :subscript
|
|
174
|
+
when '<sup>'
|
|
175
|
+
styles << :superscript
|
|
176
|
+
when '</b>', '</strong>'
|
|
177
|
+
styles.delete(:bold)
|
|
178
|
+
when '</i>', '</em>'
|
|
179
|
+
styles.delete(:italic)
|
|
180
|
+
when '</u>'
|
|
181
|
+
styles.delete(:underline)
|
|
182
|
+
when '</strikethrough>'
|
|
183
|
+
styles.delete(:strikethrough)
|
|
184
|
+
when '</sub>'
|
|
185
|
+
styles.delete(:subscript)
|
|
186
|
+
when '</sup>'
|
|
187
|
+
styles.delete(:superscript)
|
|
188
|
+
when '</link>', '</a>'
|
|
189
|
+
link = nil
|
|
190
|
+
anchor = nil
|
|
191
|
+
local = nil
|
|
192
|
+
when '</color>'
|
|
193
|
+
colors.pop
|
|
194
|
+
when '</font>'
|
|
195
|
+
fonts.pop
|
|
196
|
+
sizes.pop
|
|
197
|
+
character_spacings.pop
|
|
198
|
+
when /^<link[^>]*>$/, /^<a[^>]*>$/
|
|
199
|
+
matches = /href="([^"]*)"/.match(token) ||
|
|
200
|
+
/href='([^']*)'/.match(token)
|
|
201
|
+
link = matches[1] unless matches.nil?
|
|
202
|
+
|
|
203
|
+
matches = /anchor="([^"]*)"/.match(token) ||
|
|
204
|
+
/anchor='([^']*)'/.match(token)
|
|
205
|
+
anchor = matches[1] unless matches.nil?
|
|
206
|
+
|
|
207
|
+
matches = /local="([^"]*)"/.match(token) ||
|
|
208
|
+
/local='([^']*)'/.match(token)
|
|
209
|
+
local = matches[1] unless matches.nil?
|
|
210
|
+
when /^<color[^>]*>$/
|
|
211
|
+
matches = /rgb="#?([^"]*)"/.match(token) ||
|
|
212
|
+
/rgb='#?([^']*)'/.match(token)
|
|
213
|
+
colors << matches[1] if matches
|
|
214
|
+
|
|
215
|
+
match = /c="#?([^"]*)"/.match(token) ||
|
|
216
|
+
/c='#?([^']*)'/.match(token)
|
|
217
|
+
c = Integer(match[1], 10) unless match.nil?
|
|
218
|
+
match = /m="#?([^"]*)"/.match(token) ||
|
|
219
|
+
/m='#?([^']*)'/.match(token)
|
|
220
|
+
m = Integer(match[1], 10) unless match.nil?
|
|
221
|
+
match = /y="#?([^"]*)"/.match(token) ||
|
|
222
|
+
/y='#?([^']*)'/.match(token)
|
|
223
|
+
y = Integer(match[1], 10) unless match.nil?
|
|
224
|
+
match = /k="#?([^"]*)"/.match(token) ||
|
|
225
|
+
/k='#?([^']*)'/.match(token)
|
|
226
|
+
k = Integer(match[1], 10) unless match.nil?
|
|
227
|
+
colors << [c, m, y, k] if [c, m, y, k].all?
|
|
228
|
+
|
|
229
|
+
# intend to support rgb="#ffffff" or rgb='#ffffff',
|
|
230
|
+
# r="255" g="255" b="255" or r='255' g='255' b='255',
|
|
231
|
+
# and c="100" m="100" y="100" k="100" or
|
|
232
|
+
# c='100' m='100' y='100' k='100'
|
|
233
|
+
# color = { :rgb => "#ffffff" }
|
|
234
|
+
# color = { :r => 255, :g => 255, :b => 255 }
|
|
235
|
+
# color = { :c => 100, :m => 100, :y => 100, :k => 100 }
|
|
236
|
+
when /^<font[^>]*>$/
|
|
237
|
+
matches = /name="([^"]*)"/.match(token) ||
|
|
238
|
+
/name='([^']*)'/.match(token)
|
|
239
|
+
fonts << matches[1] unless matches.nil?
|
|
240
|
+
|
|
241
|
+
matches = /size="([^"]*)"/.match(token) ||
|
|
242
|
+
/size='([^']*)'/.match(token)
|
|
243
|
+
sizes << Float(matches[1]) unless matches.nil?
|
|
244
|
+
|
|
245
|
+
matches = /character_spacing="([^"]*)"/.match(token) ||
|
|
246
|
+
/character_spacing='([^']*)'/.match(token)
|
|
247
|
+
character_spacings << Float(matches[1]) unless matches.nil?
|
|
248
|
+
else
|
|
249
|
+
string = unescape(token)
|
|
250
|
+
array << {
|
|
251
|
+
text: string,
|
|
252
|
+
styles: styles.dup,
|
|
253
|
+
color: colors.last,
|
|
254
|
+
local: local,
|
|
255
|
+
link: link,
|
|
256
|
+
anchor: anchor,
|
|
257
|
+
font: fonts.last,
|
|
258
|
+
size: sizes.last,
|
|
259
|
+
character_spacing: character_spacings.last,
|
|
260
|
+
}
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
array
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Escape characters that can interfere with inline format parsing.
|
|
267
|
+
#
|
|
268
|
+
# @param text [String]
|
|
269
|
+
# @return [String]
|
|
270
|
+
def self.escape(text)
|
|
271
|
+
text.gsub(Regexp.union(ESCAPE_CHARS.keys), ESCAPE_CHARS)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Unescape characters that can interfere with inline format parsing.
|
|
275
|
+
#
|
|
276
|
+
# @param text [String]
|
|
277
|
+
# @return [String]
|
|
278
|
+
def self.unescape(text)
|
|
279
|
+
text.gsub(Regexp.union(UNESCAPE_CHARS.keys), UNESCAPE_CHARS)
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'line_wrap'
|
|
4
|
+
require_relative 'arranger'
|
|
5
|
+
|
|
6
|
+
module Prawn
|
|
7
|
+
module Text
|
|
8
|
+
module Formatted
|
|
9
|
+
# Handles text wrapping for for formatted text.
|
|
10
|
+
#
|
|
11
|
+
# @private
|
|
12
|
+
module Wrap
|
|
13
|
+
def initialize(_array, options)
|
|
14
|
+
@line_wrap = Prawn::Text::Formatted::LineWrap.new
|
|
15
|
+
@arranger = Prawn::Text::Formatted::Arranger.new(
|
|
16
|
+
@document,
|
|
17
|
+
kerning: options[:kerning],
|
|
18
|
+
)
|
|
19
|
+
@disable_wrap_by_char = options[:disable_wrap_by_char]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# See the developer documentation for PDF::Core::Text#wrap
|
|
23
|
+
#
|
|
24
|
+
# Formatted#wrap should set the following variables:
|
|
25
|
+
# <tt>@line_height</tt>::
|
|
26
|
+
# the height of the tallest fragment in the last printed line
|
|
27
|
+
# <tt>@descender</tt>::
|
|
28
|
+
# the descender height of the tallest fragment in the last
|
|
29
|
+
# printed line
|
|
30
|
+
# <tt>@ascender</tt>::
|
|
31
|
+
# the ascender heigth of the tallest fragment in the last
|
|
32
|
+
# printed line
|
|
33
|
+
# <tt>@baseline_y</tt>::
|
|
34
|
+
# the baseline of the current line
|
|
35
|
+
# <tt>@nothing_printed</tt>::
|
|
36
|
+
# set to true until something is printed, then false
|
|
37
|
+
# <tt>@everything_printed</tt>::
|
|
38
|
+
# set to false until everything printed, then true
|
|
39
|
+
#
|
|
40
|
+
# Returns any formatted text that was not printed
|
|
41
|
+
#
|
|
42
|
+
def wrap(array)
|
|
43
|
+
initialize_wrap(array)
|
|
44
|
+
|
|
45
|
+
stop = false
|
|
46
|
+
until stop
|
|
47
|
+
# wrap before testing if enough height for this line because the
|
|
48
|
+
# height of the highest fragment on this line will be used to
|
|
49
|
+
# determine the line height
|
|
50
|
+
@line_wrap.wrap_line(
|
|
51
|
+
document: @document,
|
|
52
|
+
kerning: @kerning,
|
|
53
|
+
width: available_width,
|
|
54
|
+
arranger: @arranger,
|
|
55
|
+
disable_wrap_by_char: @disable_wrap_by_char,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if enough_height_for_this_line?
|
|
59
|
+
move_baseline_down
|
|
60
|
+
print_line
|
|
61
|
+
else
|
|
62
|
+
stop = true
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
stop ||= @single_line || @arranger.finished?
|
|
66
|
+
end
|
|
67
|
+
@text = @printed_lines.join("\n")
|
|
68
|
+
@everything_printed = @arranger.finished?
|
|
69
|
+
@arranger.unconsumed
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def print_line
|
|
75
|
+
@nothing_printed = false
|
|
76
|
+
printed_fragments = []
|
|
77
|
+
fragments_this_line = []
|
|
78
|
+
|
|
79
|
+
word_spacing = word_spacing_for_this_line
|
|
80
|
+
@arranger.fragments.each do |fragment|
|
|
81
|
+
fragment.word_spacing = word_spacing
|
|
82
|
+
if fragment.text == "\n"
|
|
83
|
+
printed_fragments << "\n" if @printed_lines.last == ''
|
|
84
|
+
break
|
|
85
|
+
end
|
|
86
|
+
printed_fragments << fragment.text
|
|
87
|
+
fragments_this_line << fragment
|
|
88
|
+
end
|
|
89
|
+
@arranger.fragments.replace([])
|
|
90
|
+
|
|
91
|
+
accumulated_width = 0
|
|
92
|
+
fragments_this_line.reverse! if @direction == :rtl
|
|
93
|
+
fragments_this_line.each do |fragment_this_line|
|
|
94
|
+
fragment_this_line.default_direction = @direction
|
|
95
|
+
format_and_draw_fragment(
|
|
96
|
+
fragment_this_line,
|
|
97
|
+
accumulated_width,
|
|
98
|
+
@line_wrap.width,
|
|
99
|
+
word_spacing,
|
|
100
|
+
)
|
|
101
|
+
accumulated_width += fragment_this_line.width
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
@printed_lines << printed_fragments.map { |s|
|
|
105
|
+
s.dup.force_encoding(::Encoding::UTF_8)
|
|
106
|
+
}.join
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def word_spacing_for_this_line
|
|
110
|
+
if @align == :justify && @line_wrap.space_count.positive? &&
|
|
111
|
+
!@line_wrap.paragraph_finished?
|
|
112
|
+
(available_width - @line_wrap.width) / @line_wrap.space_count
|
|
113
|
+
else
|
|
114
|
+
0
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def enough_height_for_this_line?
|
|
119
|
+
@line_height = @arranger.max_line_height
|
|
120
|
+
@descender = @arranger.max_descender
|
|
121
|
+
@ascender = @arranger.max_ascender
|
|
122
|
+
diff =
|
|
123
|
+
if @baseline_y.zero?
|
|
124
|
+
@ascender + @descender
|
|
125
|
+
else
|
|
126
|
+
@descender + @line_height + @leading
|
|
127
|
+
end
|
|
128
|
+
require_relatived_total_height = @baseline_y.abs + diff
|
|
129
|
+
if require_relatived_total_height > @height + 0.0001
|
|
130
|
+
# no room for the full height of this line
|
|
131
|
+
@arranger.repack_unretrieved
|
|
132
|
+
false
|
|
133
|
+
else
|
|
134
|
+
true
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def initialize_wrap(array)
|
|
139
|
+
@text = nil
|
|
140
|
+
@arranger.format_array = array
|
|
141
|
+
|
|
142
|
+
# these values will depend on the maximum value within a given line
|
|
143
|
+
@line_height = 0
|
|
144
|
+
@descender = 0
|
|
145
|
+
@ascender = 0
|
|
146
|
+
@baseline_y = 0
|
|
147
|
+
|
|
148
|
+
@printed_lines = []
|
|
149
|
+
@nothing_printed = true
|
|
150
|
+
@everything_printed = false
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def format_and_draw_fragment(fragment, accumulated_width, line_width, word_spacing)
|
|
154
|
+
@arranger.apply_color_and_font_settings(fragment) do
|
|
155
|
+
draw_fragment(
|
|
156
|
+
fragment,
|
|
157
|
+
accumulated_width,
|
|
158
|
+
line_width,
|
|
159
|
+
word_spacing,
|
|
160
|
+
)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'formatted/wrap'
|
|
4
|
+
|
|
5
|
+
module Prawn
|
|
6
|
+
module Text
|
|
7
|
+
# Fancy pretty text.
|
|
8
|
+
module Formatted
|
|
9
|
+
# @group Stable API
|
|
10
|
+
|
|
11
|
+
# Draws the requested formatted text into a box.
|
|
12
|
+
#
|
|
13
|
+
# When the text overflows the rectangle shrink to fit or truncate the
|
|
14
|
+
# text. Text boxes are independent of the document y position.
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# formatted_text_box([{ :text => "hello" },
|
|
18
|
+
# { :text => "world",
|
|
19
|
+
# :size => 24,
|
|
20
|
+
# :styles => [:bold, :italic] }])
|
|
21
|
+
#
|
|
22
|
+
# @param array [Array<Hash{Symbol => any}>]
|
|
23
|
+
# Formatted text is an array of hashes, where each hash defines text and
|
|
24
|
+
# format information. The following hash options are supported:
|
|
25
|
+
#
|
|
26
|
+
# - `:text` --- the text to format according to the other hash options.
|
|
27
|
+
# - `:styles` --- an array of styles to apply to this text. Available
|
|
28
|
+
# styles include `:bold`, `:italic`, `:underline`, `:strikethrough`,
|
|
29
|
+
# `:subscript`, and `:superscript`.
|
|
30
|
+
# - `:size` ---a number denoting the font size to apply to this text.
|
|
31
|
+
# - `:character_spacing` --- a number denoting how much to increase or
|
|
32
|
+
# decrease the default spacing between characters.
|
|
33
|
+
# - `:font` --- the name of a font. The name must be an AFM font with
|
|
34
|
+
# the desired faces or must be a font that is already registered using
|
|
35
|
+
# {Prawn::Document#font_families}.
|
|
36
|
+
# - `:color` --- anything compatible with
|
|
37
|
+
# {Prawn::Graphics::Color#fill_color} and
|
|
38
|
+
# {Prawn::Graphics::Color#stroke_color}.
|
|
39
|
+
# - :link` --- a URL to which to create a link. A clickable link will be
|
|
40
|
+
# created to that URL. Note that you must explicitly underline and
|
|
41
|
+
# color using the appropriate tags if you which to draw attention to
|
|
42
|
+
# the link.
|
|
43
|
+
# - `:anchor` --- a destination that has already been or will be
|
|
44
|
+
# registered using
|
|
45
|
+
# `PDF::Core::Destinations#add_dest`{:.language-plain}. A clickable
|
|
46
|
+
# link will be created to that destination. Note that you must
|
|
47
|
+
# explicitly underline and color using the appropriate tags if you
|
|
48
|
+
# which to draw attention to the link.
|
|
49
|
+
# - `:local` --- a file or application to be opened locally. A clickable
|
|
50
|
+
# link will be created to the provided local file or application. If
|
|
51
|
+
# the file is another PDF, it will be opened in a new window. Note
|
|
52
|
+
# that you must explicitly underline and color using the appropriate
|
|
53
|
+
# options if you which to draw attention to the link.
|
|
54
|
+
# - `:draw_text_callback` --- if provided, this Proc will be called
|
|
55
|
+
# instead of {#draw_text!} once per fragment for every low-level
|
|
56
|
+
# addition of text to the page.
|
|
57
|
+
# - `:callback` --- an object (or array of such objects) with two
|
|
58
|
+
# methods: `#render_behind`{:.language-plain} and
|
|
59
|
+
# `#render_in_front`{:.language-plain}, which are called immediately
|
|
60
|
+
# prior to and immediately after rendering the text fragment and which
|
|
61
|
+
# are passed the fragment as an argument.
|
|
62
|
+
# @param options [Hash{Symbol => any}]
|
|
63
|
+
# Accepts the same options as {Text::Box}.
|
|
64
|
+
#
|
|
65
|
+
# @return [Array<Hash>]
|
|
66
|
+
# A formatted text array representing any text that did not print under
|
|
67
|
+
# the current settings.
|
|
68
|
+
#
|
|
69
|
+
# @raise [Prawn::Text::Formatted::Arranger::BadFontFamily]
|
|
70
|
+
# If no font family is defined for the current font.
|
|
71
|
+
# @raise [Prawn::Errors::CannotFit]
|
|
72
|
+
# If not wide enough to print any text.
|
|
73
|
+
def formatted_text_box(array, options = {})
|
|
74
|
+
Text::Formatted::Box.new(array, options.merge(document: self)).render
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
require_relative 'formatted/box'
|
|
81
|
+
require_relative 'formatted/parser'
|
|
82
|
+
require_relative 'formatted/fragment'
|