prawn-git 2.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/.yardopts +10 -0
- data/COPYING +2 -0
- data/GPLv2 +340 -0
- data/GPLv3 +674 -0
- data/Gemfile +11 -0
- data/LICENSE +56 -0
- data/Rakefile +55 -0
- data/data/fonts/Courier-Bold.afm +342 -0
- data/data/fonts/Courier-BoldOblique.afm +342 -0
- data/data/fonts/Courier-Oblique.afm +342 -0
- data/data/fonts/Courier.afm +342 -0
- data/data/fonts/Helvetica-Bold.afm +2827 -0
- data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
- data/data/fonts/Helvetica-Oblique.afm +3051 -0
- data/data/fonts/Helvetica.afm +3051 -0
- data/data/fonts/MustRead.html +19 -0
- data/data/fonts/Symbol.afm +213 -0
- data/data/fonts/Times-Bold.afm +2588 -0
- data/data/fonts/Times-BoldItalic.afm +2384 -0
- data/data/fonts/Times-Italic.afm +2667 -0
- data/data/fonts/Times-Roman.afm +2419 -0
- data/data/fonts/ZapfDingbats.afm +225 -0
- data/data/images/16bit.alpha +0 -0
- data/data/images/16bit.color +0 -0
- data/data/images/16bit.png +0 -0
- data/data/images/arrow.png +0 -0
- data/data/images/arrow2.png +0 -0
- data/data/images/dice.alpha +0 -0
- data/data/images/dice.color +0 -0
- data/data/images/dice.png +0 -0
- data/data/images/dice_interlaced.png +0 -0
- data/data/images/fractal.jpg +0 -0
- data/data/images/indexed_color.dat +0 -0
- data/data/images/indexed_color.png +0 -0
- data/data/images/letterhead.jpg +0 -0
- data/data/images/license.md +8 -0
- data/data/images/page_white_text.alpha +0 -0
- data/data/images/page_white_text.color +0 -0
- data/data/images/page_white_text.png +0 -0
- data/data/images/pal_bk.png +0 -0
- data/data/images/pigs.jpg +0 -0
- data/data/images/prawn.png +0 -0
- data/data/images/ruport.png +0 -0
- data/data/images/ruport_data.dat +0 -0
- data/data/images/ruport_transparent.png +0 -0
- data/data/images/ruport_type0.png +0 -0
- data/data/images/stef.jpg +0 -0
- data/data/images/tru256.bmp +0 -0
- data/data/images/web-links.dat +1 -0
- data/data/images/web-links.png +0 -0
- data/data/pdfs/complex_template.pdf +0 -0
- data/data/pdfs/contains_ttf_font.pdf +0 -0
- data/data/pdfs/encrypted.pdf +0 -0
- data/data/pdfs/form.pdf +820 -0
- data/data/pdfs/hexagon.pdf +61 -0
- data/data/pdfs/indirect_reference.pdf +86 -0
- data/data/pdfs/multipage_template.pdf +127 -0
- data/data/pdfs/nested_pages.pdf +118 -0
- data/data/pdfs/page_without_mediabox.pdf +193 -0
- data/data/pdfs/resources_as_indirect_object.pdf +83 -0
- data/data/pdfs/two_hexagons.pdf +90 -0
- data/data/pdfs/version_1_6.pdf +61 -0
- data/data/shift_jis_text.txt +1 -0
- data/lib/prawn.rb +89 -0
- data/lib/prawn/document.rb +706 -0
- data/lib/prawn/document/bounding_box.rb +539 -0
- data/lib/prawn/document/column_box.rb +144 -0
- data/lib/prawn/document/internals.rb +58 -0
- data/lib/prawn/document/span.rb +57 -0
- data/lib/prawn/encoding.rb +87 -0
- data/lib/prawn/errors.rb +80 -0
- data/lib/prawn/font.rb +413 -0
- data/lib/prawn/font/afm.rb +256 -0
- data/lib/prawn/font/dfont.rb +43 -0
- data/lib/prawn/font/ttf.rb +355 -0
- data/lib/prawn/font_metric_cache.rb +46 -0
- data/lib/prawn/graphics.rb +646 -0
- data/lib/prawn/graphics/cap_style.rb +47 -0
- data/lib/prawn/graphics/color.rb +232 -0
- data/lib/prawn/graphics/dash.rb +109 -0
- data/lib/prawn/graphics/join_style.rb +49 -0
- data/lib/prawn/graphics/patterns.rb +126 -0
- data/lib/prawn/graphics/transformation.rb +157 -0
- data/lib/prawn/graphics/transparency.rb +101 -0
- data/lib/prawn/grid.rb +279 -0
- data/lib/prawn/image_handler.rb +44 -0
- data/lib/prawn/images.rb +199 -0
- data/lib/prawn/images/image.rb +49 -0
- data/lib/prawn/images/jpg.rb +91 -0
- data/lib/prawn/images/png.rb +290 -0
- data/lib/prawn/measurement_extensions.rb +50 -0
- data/lib/prawn/measurements.rb +77 -0
- data/lib/prawn/outline.rb +289 -0
- data/lib/prawn/repeater.rb +124 -0
- data/lib/prawn/security.rb +288 -0
- data/lib/prawn/security/arcfour.rb +54 -0
- data/lib/prawn/soft_mask.rb +94 -0
- data/lib/prawn/stamp.rb +136 -0
- data/lib/prawn/text.rb +437 -0
- data/lib/prawn/text/box.rb +141 -0
- data/lib/prawn/text/formatted.rb +7 -0
- data/lib/prawn/text/formatted/arranger.rb +290 -0
- data/lib/prawn/text/formatted/box.rb +614 -0
- data/lib/prawn/text/formatted/fragment.rb +264 -0
- data/lib/prawn/text/formatted/line_wrap.rb +277 -0
- data/lib/prawn/text/formatted/parser.rb +224 -0
- data/lib/prawn/text/formatted/wrap.rb +160 -0
- data/lib/prawn/utilities.rb +46 -0
- data/lib/prawn/version.rb +5 -0
- data/lib/prawn/view.rb +91 -0
- data/manual/absolute_position.pdf +0 -0
- data/manual/basic_concepts/adding_pages.rb +27 -0
- data/manual/basic_concepts/basic_concepts.rb +36 -0
- data/manual/basic_concepts/creation.rb +39 -0
- data/manual/basic_concepts/cursor.rb +33 -0
- data/manual/basic_concepts/measurement.rb +25 -0
- data/manual/basic_concepts/origin.rb +38 -0
- data/manual/basic_concepts/other_cursor_helpers.rb +40 -0
- data/manual/basic_concepts/view.rb +42 -0
- data/manual/bounding_box/bounding_box.rb +39 -0
- data/manual/bounding_box/bounds.rb +49 -0
- data/manual/bounding_box/canvas.rb +24 -0
- data/manual/bounding_box/creation.rb +23 -0
- data/manual/bounding_box/indentation.rb +46 -0
- data/manual/bounding_box/nesting.rb +45 -0
- data/manual/bounding_box/russian_boxes.rb +40 -0
- data/manual/bounding_box/stretchy.rb +31 -0
- data/manual/contents.rb +29 -0
- data/manual/cover.rb +39 -0
- data/manual/document_and_page_options/background.rb +27 -0
- data/manual/document_and_page_options/document_and_page_options.rb +32 -0
- data/manual/document_and_page_options/metadata.rb +23 -0
- data/manual/document_and_page_options/page_margins.rb +38 -0
- data/manual/document_and_page_options/page_size.rb +34 -0
- data/manual/document_and_page_options/print_scaling.rb +20 -0
- data/manual/example_helper.rb +7 -0
- data/manual/graphics/circle_and_ellipse.rb +22 -0
- data/manual/graphics/color.rb +24 -0
- data/manual/graphics/common_lines.rb +30 -0
- data/manual/graphics/fill_and_stroke.rb +42 -0
- data/manual/graphics/fill_rules.rb +37 -0
- data/manual/graphics/gradients.rb +37 -0
- data/manual/graphics/graphics.rb +58 -0
- data/manual/graphics/helper.rb +24 -0
- data/manual/graphics/line_width.rb +35 -0
- data/manual/graphics/lines_and_curves.rb +41 -0
- data/manual/graphics/polygon.rb +29 -0
- data/manual/graphics/rectangle.rb +21 -0
- data/manual/graphics/rotate.rb +28 -0
- data/manual/graphics/scale.rb +41 -0
- data/manual/graphics/soft_masks.rb +46 -0
- data/manual/graphics/stroke_cap.rb +31 -0
- data/manual/graphics/stroke_dash.rb +48 -0
- data/manual/graphics/stroke_join.rb +30 -0
- data/manual/graphics/translate.rb +29 -0
- data/manual/graphics/transparency.rb +35 -0
- data/manual/how_to_read_this_manual.rb +40 -0
- data/manual/images/absolute_position.rb +23 -0
- data/manual/images/fit.rb +21 -0
- data/manual/images/horizontal.rb +25 -0
- data/manual/images/images.rb +40 -0
- data/manual/images/plain_image.rb +18 -0
- data/manual/images/scale.rb +22 -0
- data/manual/images/vertical.rb +28 -0
- data/manual/images/width_and_height.rb +25 -0
- data/manual/layout/boxes.rb +27 -0
- data/manual/layout/content.rb +25 -0
- data/manual/layout/layout.rb +28 -0
- data/manual/layout/simple_grid.rb +23 -0
- data/manual/outline/add_subsection_to.rb +61 -0
- data/manual/outline/insert_section_after.rb +47 -0
- data/manual/outline/outline.rb +32 -0
- data/manual/outline/sections_and_pages.rb +67 -0
- data/manual/repeatable_content/alternate_page_numbering.rb +32 -0
- data/manual/repeatable_content/page_numbering.rb +54 -0
- data/manual/repeatable_content/repeatable_content.rb +32 -0
- data/manual/repeatable_content/repeater.rb +55 -0
- data/manual/repeatable_content/stamp.rb +41 -0
- data/manual/security/encryption.rb +31 -0
- data/manual/security/permissions.rb +38 -0
- data/manual/security/security.rb +28 -0
- data/manual/table.rb +16 -0
- data/manual/text/alignment.rb +44 -0
- data/manual/text/color.rb +24 -0
- data/manual/text/column_box.rb +32 -0
- data/manual/text/fallback_fonts.rb +37 -0
- data/manual/text/font.rb +41 -0
- data/manual/text/font_size.rb +45 -0
- data/manual/text/font_style.rb +23 -0
- data/manual/text/formatted_callbacks.rb +60 -0
- data/manual/text/formatted_text.rb +50 -0
- data/manual/text/free_flowing_text.rb +51 -0
- data/manual/text/inline.rb +41 -0
- data/manual/text/kerning_and_character_spacing.rb +39 -0
- data/manual/text/leading.rb +25 -0
- data/manual/text/line_wrapping.rb +41 -0
- data/manual/text/paragraph_indentation.rb +34 -0
- data/manual/text/positioned_text.rb +38 -0
- data/manual/text/registering_families.rb +48 -0
- data/manual/text/rendering_and_color.rb +37 -0
- data/manual/text/right_to_left_text.rb +47 -0
- data/manual/text/rotation.rb +43 -0
- data/manual/text/single_usage.rb +37 -0
- data/manual/text/text.rb +73 -0
- data/manual/text/text_box_excess.rb +32 -0
- data/manual/text/text_box_extensions.rb +45 -0
- data/manual/text/text_box_overflow.rb +48 -0
- data/manual/text/utf8.rb +28 -0
- data/manual/text/win_ansi_charset.rb +60 -0
- data/prawn.gemspec +45 -0
- data/spec/acceptance/png.rb +25 -0
- data/spec/annotations_spec.rb +74 -0
- data/spec/bounding_box_spec.rb +510 -0
- data/spec/column_box_spec.rb +65 -0
- data/spec/data/curves.pdf +66 -0
- data/spec/destinations_spec.rb +15 -0
- data/spec/document_spec.rb +748 -0
- data/spec/extensions/encoding_helpers.rb +11 -0
- data/spec/extensions/mocha.rb +46 -0
- data/spec/font_metric_cache_spec.rb +52 -0
- data/spec/font_spec.rb +474 -0
- data/spec/formatted_text_arranger_spec.rb +421 -0
- data/spec/formatted_text_box_spec.rb +705 -0
- data/spec/formatted_text_fragment_spec.rb +298 -0
- data/spec/graphics_spec.rb +683 -0
- data/spec/grid_spec.rb +96 -0
- data/spec/image_handler_spec.rb +54 -0
- data/spec/images_spec.rb +153 -0
- data/spec/inline_formatted_text_parser_spec.rb +564 -0
- data/spec/jpg_spec.rb +25 -0
- data/spec/line_wrap_spec.rb +367 -0
- data/spec/measurement_units_spec.rb +25 -0
- data/spec/outline_spec.rb +430 -0
- data/spec/png_spec.rb +245 -0
- data/spec/reference_spec.rb +25 -0
- data/spec/repeater_spec.rb +160 -0
- data/spec/security_spec.rb +158 -0
- data/spec/soft_mask_spec.rb +79 -0
- data/spec/span_spec.rb +44 -0
- data/spec/spec_helper.rb +54 -0
- data/spec/stamp_spec.rb +160 -0
- data/spec/stroke_styles_spec.rb +211 -0
- data/spec/text_at_spec.rb +143 -0
- data/spec/text_box_spec.rb +1043 -0
- data/spec/text_rendering_mode_spec.rb +45 -0
- data/spec/text_spacing_spec.rb +93 -0
- data/spec/text_spec.rb +557 -0
- data/spec/text_with_inline_formatting_spec.rb +35 -0
- data/spec/transparency_spec.rb +91 -0
- data/spec/view_spec.rb +43 -0
- metadata +509 -0
@@ -0,0 +1,264 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# text/formatted/fragment.rb : Implements information about a formatted fragment
|
4
|
+
#
|
5
|
+
# Copyright March 2010, Daniel Nelson. All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
|
+
|
9
|
+
module Prawn
|
10
|
+
module Text
|
11
|
+
module Formatted
|
12
|
+
|
13
|
+
|
14
|
+
# Prawn::Text::Formatted::Fragment is a state store for a formatted text
|
15
|
+
# fragment. It does not render anything.
|
16
|
+
#
|
17
|
+
# @private
|
18
|
+
class Fragment
|
19
|
+
|
20
|
+
attr_reader :format_state, :text
|
21
|
+
attr_writer :width
|
22
|
+
attr_accessor :line_height, :descender, :ascender
|
23
|
+
attr_accessor :word_spacing, :left, :baseline
|
24
|
+
|
25
|
+
def initialize(text, format_state, document)
|
26
|
+
@format_state = format_state
|
27
|
+
@document = document
|
28
|
+
@word_spacing = 0
|
29
|
+
|
30
|
+
# keep the original value of "text", so we can reinitialize @text if formatting parameters
|
31
|
+
# like text direction are changed
|
32
|
+
@original_text = text
|
33
|
+
@text = process_text(@original_text)
|
34
|
+
end
|
35
|
+
|
36
|
+
def width
|
37
|
+
if @word_spacing == 0 then @width
|
38
|
+
else @width + @word_spacing * space_count
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def height
|
43
|
+
top - bottom
|
44
|
+
end
|
45
|
+
|
46
|
+
def subscript?
|
47
|
+
styles.include?(:subscript)
|
48
|
+
end
|
49
|
+
|
50
|
+
def superscript?
|
51
|
+
styles.include?(:superscript)
|
52
|
+
end
|
53
|
+
|
54
|
+
def y_offset
|
55
|
+
if subscript? then -descender
|
56
|
+
elsif superscript? then 0.85 * ascender
|
57
|
+
else 0
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def bounding_box
|
62
|
+
[left, bottom, right, top]
|
63
|
+
end
|
64
|
+
|
65
|
+
def absolute_bounding_box
|
66
|
+
box = bounding_box
|
67
|
+
box[0] += @document.bounds.absolute_left
|
68
|
+
box[2] += @document.bounds.absolute_left
|
69
|
+
box[1] += @document.bounds.absolute_bottom
|
70
|
+
box[3] += @document.bounds.absolute_bottom
|
71
|
+
box
|
72
|
+
end
|
73
|
+
|
74
|
+
def underline_points
|
75
|
+
y = baseline - 1.25
|
76
|
+
[[left, y], [right, y]]
|
77
|
+
end
|
78
|
+
|
79
|
+
def strikethrough_points
|
80
|
+
y = baseline + ascender * 0.3
|
81
|
+
[[left, y], [right, y]]
|
82
|
+
end
|
83
|
+
|
84
|
+
def styles
|
85
|
+
@format_state[:styles] || []
|
86
|
+
end
|
87
|
+
|
88
|
+
def link
|
89
|
+
@format_state[:link]
|
90
|
+
end
|
91
|
+
|
92
|
+
def anchor
|
93
|
+
@format_state[:anchor]
|
94
|
+
end
|
95
|
+
|
96
|
+
def local
|
97
|
+
@format_state[:local]
|
98
|
+
end
|
99
|
+
|
100
|
+
def color
|
101
|
+
@format_state[:color]
|
102
|
+
end
|
103
|
+
|
104
|
+
def font
|
105
|
+
@format_state[:font]
|
106
|
+
end
|
107
|
+
|
108
|
+
def size
|
109
|
+
@format_state[:size]
|
110
|
+
end
|
111
|
+
|
112
|
+
def character_spacing
|
113
|
+
@format_state[:character_spacing] ||
|
114
|
+
@document.character_spacing
|
115
|
+
end
|
116
|
+
|
117
|
+
def direction
|
118
|
+
@format_state[:direction]
|
119
|
+
end
|
120
|
+
|
121
|
+
def default_direction=(direction)
|
122
|
+
unless @format_state[:direction]
|
123
|
+
@format_state[:direction] = direction
|
124
|
+
@text = process_text(@original_text)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def include_trailing_white_space!
|
129
|
+
@format_state.delete(:exclude_trailing_white_space)
|
130
|
+
@text = process_text(@original_text)
|
131
|
+
end
|
132
|
+
|
133
|
+
def space_count
|
134
|
+
@text.count(" ")
|
135
|
+
end
|
136
|
+
|
137
|
+
def callback_objects
|
138
|
+
callback = @format_state[:callback]
|
139
|
+
if callback.nil?
|
140
|
+
[]
|
141
|
+
elsif callback.is_a?(Array)
|
142
|
+
callback
|
143
|
+
else
|
144
|
+
[callback]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def right
|
149
|
+
left + width
|
150
|
+
end
|
151
|
+
|
152
|
+
def top
|
153
|
+
baseline + ascender
|
154
|
+
end
|
155
|
+
|
156
|
+
def bottom
|
157
|
+
baseline - descender
|
158
|
+
end
|
159
|
+
|
160
|
+
def top_left
|
161
|
+
[left, top]
|
162
|
+
end
|
163
|
+
|
164
|
+
def top_right
|
165
|
+
[right, top]
|
166
|
+
end
|
167
|
+
|
168
|
+
def bottom_right
|
169
|
+
[right, bottom]
|
170
|
+
end
|
171
|
+
|
172
|
+
def bottom_left
|
173
|
+
[left, bottom]
|
174
|
+
end
|
175
|
+
|
176
|
+
def absolute_left
|
177
|
+
absolute_bounding_box[0]
|
178
|
+
end
|
179
|
+
|
180
|
+
def absolute_right
|
181
|
+
absolute_bounding_box[2]
|
182
|
+
end
|
183
|
+
|
184
|
+
def absolute_top
|
185
|
+
absolute_bounding_box[3]
|
186
|
+
end
|
187
|
+
|
188
|
+
def absolute_bottom
|
189
|
+
absolute_bounding_box[1]
|
190
|
+
end
|
191
|
+
|
192
|
+
def absolute_top_left
|
193
|
+
[absolute_left, absolute_top]
|
194
|
+
end
|
195
|
+
|
196
|
+
def absolute_top_right
|
197
|
+
[absolute_right, absolute_top]
|
198
|
+
end
|
199
|
+
|
200
|
+
def absolute_bottom_left
|
201
|
+
[absolute_left, absolute_bottom]
|
202
|
+
end
|
203
|
+
|
204
|
+
def absolute_bottom_right
|
205
|
+
[absolute_right, absolute_bottom]
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
|
210
|
+
def process_text(text)
|
211
|
+
string = strip_zero_width_spaces(text)
|
212
|
+
|
213
|
+
if exclude_trailing_white_space?
|
214
|
+
string = string.rstrip
|
215
|
+
|
216
|
+
if soft_hyphens_need_processing?(string)
|
217
|
+
string = process_soft_hyphens(string[0..-2]) + string[-1..-1]
|
218
|
+
end
|
219
|
+
else
|
220
|
+
if soft_hyphens_need_processing?(string)
|
221
|
+
string = process_soft_hyphens(string)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
case direction
|
226
|
+
when :rtl
|
227
|
+
string.reverse
|
228
|
+
else
|
229
|
+
string
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def exclude_trailing_white_space?
|
234
|
+
@format_state[:exclude_trailing_white_space]
|
235
|
+
end
|
236
|
+
|
237
|
+
def soft_hyphens_need_processing?(string)
|
238
|
+
string.length > 0 && normalized_soft_hyphen
|
239
|
+
end
|
240
|
+
|
241
|
+
def normalized_soft_hyphen
|
242
|
+
@format_state[:normalized_soft_hyphen]
|
243
|
+
end
|
244
|
+
|
245
|
+
def process_soft_hyphens(string)
|
246
|
+
if string.encoding != normalized_soft_hyphen.encoding
|
247
|
+
string.force_encoding(normalized_soft_hyphen.encoding)
|
248
|
+
end
|
249
|
+
|
250
|
+
string.gsub(normalized_soft_hyphen, "")
|
251
|
+
end
|
252
|
+
|
253
|
+
def strip_zero_width_spaces(string)
|
254
|
+
if string.encoding == ::Encoding::UTF_8
|
255
|
+
string.gsub(Prawn::Text::ZWSP, "")
|
256
|
+
else
|
257
|
+
string
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
@@ -0,0 +1,277 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# core/text/formatted/line_wrap.rb : Implements individual line wrapping of
|
4
|
+
# formatted text
|
5
|
+
#
|
6
|
+
# Copyright February 2010, Daniel Nelson. All Rights Reserved.
|
7
|
+
#
|
8
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
9
|
+
#
|
10
|
+
|
11
|
+
module Prawn
|
12
|
+
module Text
|
13
|
+
module Formatted #:nodoc:
|
14
|
+
|
15
|
+
# @private
|
16
|
+
class LineWrap #:nodoc:
|
17
|
+
|
18
|
+
# The width of the last wrapped line
|
19
|
+
#
|
20
|
+
def width
|
21
|
+
@accumulated_width || 0
|
22
|
+
end
|
23
|
+
|
24
|
+
# The number of spaces in the last wrapped line
|
25
|
+
attr_reader :space_count
|
26
|
+
|
27
|
+
# Whether this line is the last line in the paragraph
|
28
|
+
def paragraph_finished?
|
29
|
+
@newline_encountered || is_next_string_newline? || @arranger.finished?
|
30
|
+
end
|
31
|
+
|
32
|
+
def tokenize(fragment)
|
33
|
+
fragment.scan(scan_pattern)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Work in conjunction with the PDF::Formatted::Arranger
|
37
|
+
# defined in the :arranger option to determine what formatted text
|
38
|
+
# will fit within the width defined by the :width option
|
39
|
+
#
|
40
|
+
def wrap_line(options)
|
41
|
+
initialize_line(options)
|
42
|
+
|
43
|
+
while fragment = @arranger.next_string
|
44
|
+
@fragment_output = ""
|
45
|
+
|
46
|
+
fragment.lstrip! if first_fragment_on_this_line?(fragment)
|
47
|
+
next if empty_line?(fragment)
|
48
|
+
|
49
|
+
unless apply_font_settings_and_add_fragment_to_line(fragment)
|
50
|
+
break
|
51
|
+
end
|
52
|
+
end
|
53
|
+
@arranger.finalize_line
|
54
|
+
@accumulated_width = @arranger.line_width
|
55
|
+
@space_count = @arranger.space_count
|
56
|
+
@arranger.line
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def first_fragment_on_this_line?(fragment)
|
62
|
+
line_empty? && fragment != "\n"
|
63
|
+
end
|
64
|
+
|
65
|
+
def empty_line?(fragment)
|
66
|
+
empty = line_empty? && fragment.empty? && is_next_string_newline?
|
67
|
+
@arranger.update_last_string("", "", soft_hyphen) if empty
|
68
|
+
empty
|
69
|
+
end
|
70
|
+
|
71
|
+
def is_next_string_newline?
|
72
|
+
@arranger.preview_next_string == "\n"
|
73
|
+
end
|
74
|
+
|
75
|
+
def apply_font_settings_and_add_fragment_to_line(fragment)
|
76
|
+
result = nil
|
77
|
+
@arranger.apply_font_settings do
|
78
|
+
# if font has changed from Unicode to non-Unicode, or vice versa, the characters used for soft hyphens
|
79
|
+
# and zero-width spaces will be different
|
80
|
+
set_soft_hyphen_and_zero_width_space
|
81
|
+
result = add_fragment_to_line(fragment)
|
82
|
+
end
|
83
|
+
result
|
84
|
+
end
|
85
|
+
|
86
|
+
# returns true if all text was printed without running into the end of
|
87
|
+
# the line
|
88
|
+
#
|
89
|
+
def add_fragment_to_line(fragment)
|
90
|
+
if fragment == ""
|
91
|
+
true
|
92
|
+
elsif fragment == "\n"
|
93
|
+
@newline_encountered = true
|
94
|
+
false
|
95
|
+
else
|
96
|
+
tokenize(fragment).each do |segment|
|
97
|
+
if segment == zero_width_space
|
98
|
+
segment_width = 0
|
99
|
+
else
|
100
|
+
segment_width = @document.width_of(segment, :kerning => @kerning)
|
101
|
+
end
|
102
|
+
|
103
|
+
if @accumulated_width + segment_width <= @width
|
104
|
+
@accumulated_width += segment_width
|
105
|
+
if segment[-1] == soft_hyphen
|
106
|
+
sh_width = @document.width_of("#{soft_hyphen}", :kerning => @kerning)
|
107
|
+
@accumulated_width -= sh_width
|
108
|
+
end
|
109
|
+
@fragment_output += segment
|
110
|
+
else
|
111
|
+
end_of_the_line_reached(segment)
|
112
|
+
fragment_finished(fragment)
|
113
|
+
return false
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
fragment_finished(fragment)
|
118
|
+
true
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# The pattern used to determine chunks of text to place on a given line
|
123
|
+
#
|
124
|
+
def scan_pattern
|
125
|
+
pattern = "[^#{break_chars}]+#{soft_hyphen}|" +
|
126
|
+
"[^#{break_chars}]+#{hyphen}+|" +
|
127
|
+
"[^#{break_chars}]+|" +
|
128
|
+
"[#{whitespace}]+|" +
|
129
|
+
"#{hyphen}+[^#{break_chars}]*|" +
|
130
|
+
"#{soft_hyphen}"
|
131
|
+
|
132
|
+
Regexp.new(pattern)
|
133
|
+
end
|
134
|
+
|
135
|
+
# The pattern used to determine whether any word breaks exist on a
|
136
|
+
# current line, which in turn determines whether character level
|
137
|
+
# word breaking is needed
|
138
|
+
#
|
139
|
+
def word_division_scan_pattern
|
140
|
+
Regexp.new("\\s|[#{zero_width_space}#{soft_hyphen}#{hyphen}]")
|
141
|
+
end
|
142
|
+
|
143
|
+
def break_chars
|
144
|
+
"#{whitespace}#{soft_hyphen}#{hyphen}"
|
145
|
+
end
|
146
|
+
|
147
|
+
def whitespace
|
148
|
+
" \\t#{zero_width_space}"
|
149
|
+
end
|
150
|
+
|
151
|
+
def hyphen
|
152
|
+
"-"
|
153
|
+
end
|
154
|
+
|
155
|
+
def soft_hyphen
|
156
|
+
@soft_hyphen
|
157
|
+
end
|
158
|
+
|
159
|
+
def zero_width_space
|
160
|
+
@zero_width_space
|
161
|
+
end
|
162
|
+
|
163
|
+
def line_empty?
|
164
|
+
@line_empty && @accumulated_width == 0
|
165
|
+
end
|
166
|
+
|
167
|
+
def initialize_line(options)
|
168
|
+
@document = options[:document]
|
169
|
+
@kerning = options[:kerning]
|
170
|
+
@width = options[:width]
|
171
|
+
|
172
|
+
@disable_wrap_by_char = options[:disable_wrap_by_char]
|
173
|
+
|
174
|
+
@accumulated_width = 0
|
175
|
+
@line_empty = true
|
176
|
+
@line_contains_more_than_one_word = false
|
177
|
+
|
178
|
+
@arranger = options[:arranger]
|
179
|
+
@arranger.initialize_line
|
180
|
+
|
181
|
+
@newline_encountered = false
|
182
|
+
@line_full = false
|
183
|
+
end
|
184
|
+
|
185
|
+
def set_soft_hyphen_and_zero_width_space
|
186
|
+
# this is done once per fragment, after the font settings for the fragment are applied --
|
187
|
+
# it could actually be skipped if the font hasn't changed
|
188
|
+
font = @document.font
|
189
|
+
@soft_hyphen = font.normalize_encoding(Prawn::Text::SHY)
|
190
|
+
@zero_width_space = font.unicode? ? Prawn::Text::ZWSP : ""
|
191
|
+
end
|
192
|
+
|
193
|
+
def fragment_finished(fragment)
|
194
|
+
if fragment == "\n"
|
195
|
+
@newline_encountered = true
|
196
|
+
@line_empty = false
|
197
|
+
else
|
198
|
+
update_output_based_on_last_fragment(fragment, soft_hyphen)
|
199
|
+
update_line_status_based_on_last_output
|
200
|
+
determine_whether_to_pull_preceding_fragment_to_join_this_one(fragment)
|
201
|
+
end
|
202
|
+
remember_this_fragment_for_backward_looking_ops
|
203
|
+
end
|
204
|
+
|
205
|
+
def update_output_based_on_last_fragment(fragment, normalized_soft_hyphen=nil)
|
206
|
+
remaining_text = fragment.slice(@fragment_output.length..fragment.length)
|
207
|
+
raise Prawn::Errors::CannotFit if line_finished? && line_empty? &&
|
208
|
+
@fragment_output.empty? && !fragment.strip.empty?
|
209
|
+
@arranger.update_last_string(@fragment_output, remaining_text, normalized_soft_hyphen)
|
210
|
+
end
|
211
|
+
|
212
|
+
def determine_whether_to_pull_preceding_fragment_to_join_this_one(current_fragment)
|
213
|
+
if @fragment_output.empty? &&
|
214
|
+
!current_fragment.empty? &&
|
215
|
+
@line_contains_more_than_one_word
|
216
|
+
unless previous_fragment_ended_with_breakable? ||
|
217
|
+
fragment_begins_with_breakable?(current_fragment)
|
218
|
+
@fragment_output = @previous_fragment_output_without_last_word
|
219
|
+
update_output_based_on_last_fragment(@previous_fragment)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def remember_this_fragment_for_backward_looking_ops
|
225
|
+
@previous_fragment = @fragment_output.dup
|
226
|
+
pf = @previous_fragment
|
227
|
+
@previous_fragment_ended_with_breakable = pf =~ /[#{break_chars}]$/
|
228
|
+
last_word = pf.slice(/[^#{break_chars}]*$/)
|
229
|
+
last_word_length = last_word.nil? ? 0 : last_word.length
|
230
|
+
@previous_fragment_output_without_last_word = pf.slice(0, pf.length - last_word_length)
|
231
|
+
end
|
232
|
+
|
233
|
+
def previous_fragment_ended_with_breakable?
|
234
|
+
@previous_fragment_ended_with_breakable
|
235
|
+
end
|
236
|
+
|
237
|
+
def fragment_begins_with_breakable?(fragment)
|
238
|
+
fragment =~ /^[#{break_chars}]/
|
239
|
+
end
|
240
|
+
|
241
|
+
def line_finished?
|
242
|
+
@line_full || paragraph_finished?
|
243
|
+
end
|
244
|
+
|
245
|
+
def update_line_status_based_on_last_output
|
246
|
+
@line_contains_more_than_one_word = true if @fragment_output =~ word_division_scan_pattern
|
247
|
+
end
|
248
|
+
|
249
|
+
def end_of_the_line_reached(segment)
|
250
|
+
update_line_status_based_on_last_output
|
251
|
+
wrap_by_char(segment) unless @disable_wrap_by_char || @line_contains_more_than_one_word
|
252
|
+
@line_full = true
|
253
|
+
end
|
254
|
+
|
255
|
+
def wrap_by_char(segment)
|
256
|
+
font = @document.font
|
257
|
+
segment.each_char do |char|
|
258
|
+
break unless append_char(char,font)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def append_char(char,font)
|
263
|
+
# kerning doesn't make sense in the context of a single character
|
264
|
+
char_width = font.compute_width_of(char)
|
265
|
+
|
266
|
+
if @accumulated_width + char_width <= @width
|
267
|
+
@accumulated_width += char_width
|
268
|
+
@fragment_output << char
|
269
|
+
true
|
270
|
+
else
|
271
|
+
false
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|