prawn 2.1.0 → 2.2.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
- checksums.yaml.gz.sig +2 -0
- data.tar.gz.sig +0 -0
- data/Gemfile +1 -9
- data/Rakefile +12 -22
- data/lib/prawn.rb +29 -48
- data/lib/prawn/document.rb +148 -123
- data/lib/prawn/document/bounding_box.rb +33 -26
- data/lib/prawn/document/column_box.rb +5 -7
- data/lib/prawn/document/internals.rb +6 -6
- data/lib/prawn/document/span.rb +20 -17
- data/lib/prawn/encoding.rb +65 -67
- data/lib/prawn/errors.rb +10 -7
- data/lib/prawn/font.rb +78 -62
- data/lib/prawn/font/afm.rb +93 -66
- data/lib/prawn/font/dfont.rb +2 -10
- data/lib/prawn/font/ttc.rb +34 -0
- data/lib/prawn/font/ttf.rb +73 -65
- data/lib/prawn/font_metric_cache.rb +9 -8
- data/lib/prawn/graphics.rb +110 -70
- data/lib/prawn/graphics/blend_mode.rb +7 -8
- data/lib/prawn/graphics/cap_style.rb +2 -4
- data/lib/prawn/graphics/color.rb +23 -26
- data/lib/prawn/graphics/dash.rb +22 -12
- data/lib/prawn/graphics/join_style.rb +8 -4
- data/lib/prawn/graphics/patterns.rb +185 -96
- data/lib/prawn/graphics/transformation.rb +11 -9
- data/lib/prawn/graphics/transparency.rb +15 -13
- data/lib/prawn/grid.rb +20 -20
- data/lib/prawn/image_handler.rb +4 -6
- data/lib/prawn/images.rb +22 -15
- data/lib/prawn/images/image.rb +0 -1
- data/lib/prawn/images/jpg.rb +26 -22
- data/lib/prawn/images/png.rb +60 -57
- data/lib/prawn/measurement_extensions.rb +8 -9
- data/lib/prawn/measurements.rb +14 -15
- data/lib/prawn/outline.rb +96 -78
- data/lib/prawn/repeater.rb +12 -10
- data/lib/prawn/security.rb +66 -48
- data/lib/prawn/security/arcfour.rb +1 -3
- data/lib/prawn/soft_mask.rb +23 -25
- data/lib/prawn/stamp.rb +16 -12
- data/lib/prawn/text.rb +59 -45
- data/lib/prawn/text/box.rb +9 -8
- data/lib/prawn/text/formatted.rb +4 -6
- data/lib/prawn/text/formatted/arranger.rb +51 -30
- data/lib/prawn/text/formatted/box.rb +112 -88
- data/lib/prawn/text/formatted/fragment.rb +10 -15
- data/lib/prawn/text/formatted/line_wrap.rb +118 -61
- data/lib/prawn/text/formatted/parser.rb +134 -110
- data/lib/prawn/text/formatted/wrap.rb +42 -32
- data/lib/prawn/transformation_stack.rb +3 -4
- data/lib/prawn/utilities.rb +6 -21
- data/lib/prawn/version.rb +1 -3
- data/lib/prawn/view.rb +4 -2
- data/manual/basic_concepts/adding_pages.rb +4 -7
- data/manual/basic_concepts/basic_concepts.rb +29 -22
- data/manual/basic_concepts/creation.rb +8 -11
- data/manual/basic_concepts/cursor.rb +2 -5
- data/manual/basic_concepts/measurement.rb +3 -6
- data/manual/basic_concepts/origin.rb +3 -6
- data/manual/basic_concepts/other_cursor_helpers.rb +9 -12
- data/manual/basic_concepts/view.rb +20 -16
- data/manual/bounding_box/bounding_box.rb +27 -24
- data/manual/bounding_box/bounds.rb +9 -12
- data/manual/bounding_box/canvas.rb +2 -5
- data/manual/bounding_box/creation.rb +4 -7
- data/manual/bounding_box/indentation.rb +12 -15
- data/manual/bounding_box/nesting.rb +22 -17
- data/manual/bounding_box/russian_boxes.rb +8 -9
- data/manual/bounding_box/stretchy.rb +10 -13
- data/manual/contents.rb +26 -22
- data/manual/cover.rb +22 -20
- data/manual/document_and_page_options/background.rb +9 -13
- data/manual/document_and_page_options/document_and_page_options.rb +23 -20
- data/manual/document_and_page_options/metadata.rb +16 -16
- data/manual/document_and_page_options/page_margins.rb +16 -20
- data/manual/document_and_page_options/page_size.rb +11 -12
- data/manual/document_and_page_options/print_scaling.rb +15 -15
- data/manual/example_helper.rb +2 -4
- data/manual/graphics/blend_mode.rb +10 -9
- data/manual/graphics/circle_and_ellipse.rb +2 -5
- data/manual/graphics/color.rb +5 -9
- data/manual/graphics/common_lines.rb +5 -8
- data/manual/graphics/fill_and_stroke.rb +2 -5
- data/manual/graphics/fill_rules.rb +7 -10
- data/manual/graphics/gradients.rb +25 -21
- data/manual/graphics/graphics.rb +49 -43
- data/manual/graphics/helper.rb +10 -9
- data/manual/graphics/line_width.rb +5 -7
- data/manual/graphics/lines_and_curves.rb +5 -8
- data/manual/graphics/polygon.rb +4 -8
- data/manual/graphics/rectangle.rb +2 -5
- data/manual/graphics/rotate.rb +4 -7
- data/manual/graphics/scale.rb +12 -15
- data/manual/graphics/soft_masks.rb +1 -4
- data/manual/graphics/stroke_cap.rb +3 -6
- data/manual/graphics/stroke_dash.rb +9 -12
- data/manual/graphics/stroke_join.rb +2 -5
- data/manual/graphics/translate.rb +7 -10
- data/manual/graphics/transparency.rb +5 -8
- data/manual/how_to_read_this_manual.rb +4 -6
- data/manual/images/absolute_position.rb +4 -7
- data/manual/images/fit.rb +5 -8
- data/manual/images/horizontal.rb +6 -9
- data/manual/images/images.rb +25 -23
- data/manual/images/plain_image.rb +3 -6
- data/manual/images/scale.rb +7 -10
- data/manual/images/vertical.rb +10 -13
- data/manual/images/width_and_height.rb +8 -11
- data/manual/layout/boxes.rb +3 -6
- data/manual/layout/content.rb +5 -8
- data/manual/layout/layout.rb +16 -16
- data/manual/layout/simple_grid.rb +4 -7
- data/manual/outline/add_subsection_to.rb +18 -21
- data/manual/outline/insert_section_after.rb +13 -16
- data/manual/outline/outline.rb +19 -17
- data/manual/outline/sections_and_pages.rb +15 -18
- data/manual/repeatable_content/alternate_page_numbering.rb +19 -17
- data/manual/repeatable_content/page_numbering.rb +15 -16
- data/manual/repeatable_content/repeatable_content.rb +23 -19
- data/manual/repeatable_content/repeater.rb +12 -15
- data/manual/repeatable_content/stamp.rb +12 -15
- data/manual/security/encryption.rb +7 -10
- data/manual/security/permissions.rb +17 -14
- data/manual/security/security.rb +17 -16
- data/manual/table.rb +2 -4
- data/manual/text/alignment.rb +14 -17
- data/manual/text/color.rb +10 -11
- data/manual/text/column_box.rb +5 -8
- data/manual/text/fallback_fonts.rb +23 -21
- data/manual/text/font.rb +9 -12
- data/manual/text/font_size.rb +11 -14
- data/manual/text/font_style.rb +4 -7
- data/manual/text/formatted_callbacks.rb +23 -21
- data/manual/text/formatted_text.rb +31 -25
- data/manual/text/free_flowing_text.rb +18 -21
- data/manual/text/inline.rb +16 -19
- data/manual/text/kerning_and_character_spacing.rb +12 -15
- data/manual/text/leading.rb +5 -8
- data/manual/text/line_wrapping.rb +33 -17
- data/manual/text/paragraph_indentation.rb +11 -14
- data/manual/text/positioned_text.rb +13 -16
- data/manual/text/registering_families.rb +16 -19
- data/manual/text/rendering_and_color.rb +7 -10
- data/manual/text/right_to_left_text.rb +24 -19
- data/manual/text/rotation.rb +26 -23
- data/manual/text/single_usage.rb +6 -9
- data/manual/text/text.rb +56 -52
- data/manual/text/text_box_excess.rb +18 -17
- data/manual/text/text_box_extensions.rb +16 -15
- data/manual/text/text_box_overflow.rb +15 -18
- data/manual/text/utf8.rb +9 -12
- data/manual/text/win_ansi_charset.rb +18 -19
- data/prawn.gemspec +37 -27
- data/spec/extensions/encoding_helpers.rb +0 -2
- data/spec/manual_spec.rb +33 -0
- data/spec/prawn/document/bounding_box_spec.rb +546 -0
- data/spec/prawn/document/column_box_spec.rb +73 -0
- data/spec/prawn/document/security_spec.rb +173 -0
- data/spec/prawn/document_annotations_spec.rb +74 -0
- data/spec/prawn/document_destinations_spec.rb +13 -0
- data/spec/prawn/document_grid_spec.rb +96 -0
- data/spec/prawn/document_reference_spec.rb +25 -0
- data/spec/prawn/document_span_spec.rb +34 -0
- data/spec/prawn/document_spec.rb +751 -0
- data/spec/prawn/font_metric_cache_spec.rb +52 -0
- data/spec/prawn/font_spec.rb +513 -0
- data/spec/prawn/graphics/blend_mode_spec.rb +61 -0
- data/spec/prawn/graphics/transparency_spec.rb +79 -0
- data/spec/prawn/graphics_spec.rb +817 -0
- data/spec/prawn/graphics_stroke_styles_spec.rb +227 -0
- data/spec/{image_handler_spec.rb → prawn/image_handler_spec.rb} +13 -15
- data/spec/prawn/images/jpg_spec.rb +18 -0
- data/spec/prawn/images/png_spec.rb +281 -0
- data/spec/prawn/images_spec.rb +170 -0
- data/spec/prawn/measurements_extensions_spec.rb +22 -0
- data/spec/prawn/outline_spec.rb +408 -0
- data/spec/prawn/repeater_spec.rb +163 -0
- data/spec/prawn/soft_mask_spec.rb +72 -0
- data/spec/prawn/stamp_spec.rb +168 -0
- data/spec/prawn/text/box_spec.rb +1113 -0
- data/spec/prawn/text/formatted/arranger_spec.rb +464 -0
- data/spec/prawn/text/formatted/box_spec.rb +825 -0
- data/spec/prawn/text/formatted/fragment_spec.rb +341 -0
- data/spec/prawn/text/formatted/line_wrap_spec.rb +491 -0
- data/spec/prawn/text/formatted/parser_spec.rb +667 -0
- data/spec/prawn/text_draw_text_spec.rb +147 -0
- data/spec/prawn/text_rendering_mode_spec.rb +42 -0
- data/spec/prawn/text_spacing_spec.rb +93 -0
- data/spec/prawn/text_spec.rb +601 -0
- data/spec/prawn/text_with_inline_formatting_spec.rb +33 -0
- data/spec/{transformation_stack_spec.rb → prawn/transformation_stack_spec.rb} +21 -20
- data/spec/prawn/view_spec.rb +45 -0
- data/spec/spec_helper.rb +16 -16
- metadata +96 -151
- metadata.gz.sig +1 -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/blend_modes_bottom_layer.jpg +0 -0
- data/data/images/blend_modes_top_layer.jpg +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/indexed_transparency.png +0 -0
- data/data/images/indexed_transparency_alpha.dat +0 -0
- data/data/images/indexed_transparency_color.dat +0 -0
- data/data/images/letterhead.jpg +0 -0
- data/data/images/license.md +0 -8
- 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/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 +0 -1
- 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 +1 -819
- data/data/pdfs/hexagon.pdf +0 -61
- data/data/pdfs/indirect_reference.pdf +0 -86
- data/data/pdfs/multipage_template.pdf +0 -127
- data/data/pdfs/nested_pages.pdf +0 -118
- data/data/pdfs/page_without_mediabox.pdf +0 -193
- data/data/pdfs/resources_as_indirect_object.pdf +0 -83
- data/data/pdfs/two_hexagons.pdf +0 -90
- data/data/pdfs/version_1_6.pdf +0 -61
- data/data/shift_jis_text.txt +0 -1
- data/spec/acceptance/png_spec.rb +0 -35
- data/spec/annotations_spec.rb +0 -67
- data/spec/blend_mode_spec.rb +0 -71
- data/spec/bounding_box_spec.rb +0 -501
- data/spec/column_box_spec.rb +0 -59
- data/spec/destinations_spec.rb +0 -13
- data/spec/document_spec.rb +0 -738
- data/spec/font_metric_cache_spec.rb +0 -52
- data/spec/font_spec.rb +0 -475
- data/spec/formatted_text_arranger_spec.rb +0 -452
- data/spec/formatted_text_box_spec.rb +0 -716
- data/spec/formatted_text_fragment_spec.rb +0 -299
- data/spec/graphics_spec.rb +0 -705
- data/spec/grid_spec.rb +0 -95
- data/spec/images_spec.rb +0 -167
- data/spec/inline_formatted_text_parser_spec.rb +0 -568
- data/spec/jpg_spec.rb +0 -23
- data/spec/line_wrap_spec.rb +0 -366
- data/spec/measurement_units_spec.rb +0 -22
- data/spec/outline_spec.rb +0 -409
- data/spec/png_spec.rb +0 -257
- data/spec/reference_spec.rb +0 -25
- data/spec/repeater_spec.rb +0 -154
- data/spec/security_spec.rb +0 -151
- data/spec/soft_mask_spec.rb +0 -78
- data/spec/span_spec.rb +0 -43
- data/spec/stamp_spec.rb +0 -179
- data/spec/stroke_styles_spec.rb +0 -208
- data/spec/text_at_spec.rb +0 -142
- data/spec/text_box_spec.rb +0 -1042
- data/spec/text_rendering_mode_spec.rb +0 -45
- data/spec/text_spacing_spec.rb +0 -93
- data/spec/text_spec.rb +0 -543
- data/spec/text_with_inline_formatting_spec.rb +0 -35
- data/spec/transparency_spec.rb +0 -91
- data/spec/view_spec.rb +0 -42
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
# text/formatted/fragment.rb : Implements information about a formatted fragment
|
4
2
|
#
|
5
3
|
# Copyright March 2010, Daniel Nelson. All Rights Reserved.
|
@@ -24,14 +22,14 @@ module Prawn
|
|
24
22
|
@document = document
|
25
23
|
@word_spacing = 0
|
26
24
|
|
27
|
-
# keep the original value of "text", so we can reinitialize @text if
|
28
|
-
#
|
25
|
+
# keep the original value of "text", so we can reinitialize @text if
|
26
|
+
# formatting parameters like text direction are changed
|
29
27
|
@original_text = text
|
30
28
|
@text = process_text(@original_text)
|
31
29
|
end
|
32
30
|
|
33
31
|
def width
|
34
|
-
if @word_spacing
|
32
|
+
if @word_spacing.zero? then @width
|
35
33
|
else @width + @word_spacing * space_count
|
36
34
|
end
|
37
35
|
end
|
@@ -128,7 +126,7 @@ module Prawn
|
|
128
126
|
end
|
129
127
|
|
130
128
|
def space_count
|
131
|
-
@text.count(
|
129
|
+
@text.count(' ')
|
132
130
|
end
|
133
131
|
|
134
132
|
def callback_objects
|
@@ -213,14 +211,11 @@ module Prawn
|
|
213
211
|
if soft_hyphens_need_processing?(string)
|
214
212
|
string = process_soft_hyphens(string[0..-2]) + string[-1..-1]
|
215
213
|
end
|
216
|
-
|
217
|
-
|
218
|
-
string = process_soft_hyphens(string)
|
219
|
-
end
|
214
|
+
elsif soft_hyphens_need_processing?(string)
|
215
|
+
string = process_soft_hyphens(string)
|
220
216
|
end
|
221
217
|
|
222
|
-
|
223
|
-
when :rtl
|
218
|
+
if direction == :rtl
|
224
219
|
string.reverse
|
225
220
|
else
|
226
221
|
string
|
@@ -232,7 +227,7 @@ module Prawn
|
|
232
227
|
end
|
233
228
|
|
234
229
|
def soft_hyphens_need_processing?(string)
|
235
|
-
string.
|
230
|
+
!string.empty? && normalized_soft_hyphen
|
236
231
|
end
|
237
232
|
|
238
233
|
def normalized_soft_hyphen
|
@@ -244,12 +239,12 @@ module Prawn
|
|
244
239
|
string.force_encoding(normalized_soft_hyphen.encoding)
|
245
240
|
end
|
246
241
|
|
247
|
-
string.gsub(normalized_soft_hyphen,
|
242
|
+
string.gsub(normalized_soft_hyphen, '')
|
248
243
|
end
|
249
244
|
|
250
245
|
def strip_zero_width_spaces(string)
|
251
246
|
if string.encoding == ::Encoding::UTF_8
|
252
|
-
string.gsub(Prawn::Text::ZWSP,
|
247
|
+
string.gsub(Prawn::Text::ZWSP, '')
|
253
248
|
else
|
254
249
|
string
|
255
250
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
# core/text/formatted/line_wrap.rb : Implements individual line wrapping of
|
4
2
|
# formatted text
|
5
3
|
#
|
@@ -21,16 +19,14 @@ module Prawn
|
|
21
19
|
|
22
20
|
# The number of spaces in the last wrapped line
|
23
21
|
attr_reader :space_count
|
24
|
-
attr_reader :soft_hyphen
|
25
|
-
attr_reader :zero_width_space
|
26
22
|
|
27
23
|
# Whether this line is the last line in the paragraph
|
28
24
|
def paragraph_finished?
|
29
|
-
@newline_encountered ||
|
25
|
+
@newline_encountered || next_string_newline? || @arranger.finished?
|
30
26
|
end
|
31
27
|
|
32
28
|
def tokenize(fragment)
|
33
|
-
fragment.scan(scan_pattern)
|
29
|
+
fragment.scan(scan_pattern(fragment.encoding))
|
34
30
|
end
|
35
31
|
|
36
32
|
# Work in conjunction with the PDF::Formatted::Arranger
|
@@ -40,8 +36,10 @@ module Prawn
|
|
40
36
|
def wrap_line(options)
|
41
37
|
initialize_line(options)
|
42
38
|
|
39
|
+
# rubocop: disable Lint/AssignmentInCondition
|
43
40
|
while fragment = @arranger.next_string
|
44
|
-
|
41
|
+
# rubocop: enable Lint/AssignmentInCondition
|
42
|
+
@fragment_output = ''
|
45
43
|
|
46
44
|
fragment.lstrip! if first_fragment_on_this_line?(fragment)
|
47
45
|
next if empty_line?(fragment)
|
@@ -63,21 +61,20 @@ module Prawn
|
|
63
61
|
end
|
64
62
|
|
65
63
|
def empty_line?(fragment)
|
66
|
-
empty = line_empty? && fragment.empty? &&
|
67
|
-
|
64
|
+
empty = line_empty? && fragment.empty? && next_string_newline?
|
65
|
+
if empty
|
66
|
+
@arranger.update_last_string('', '', soft_hyphen(fragment.encoding))
|
67
|
+
end
|
68
68
|
empty
|
69
69
|
end
|
70
70
|
|
71
|
-
def
|
71
|
+
def next_string_newline?
|
72
72
|
@arranger.preview_next_string == "\n"
|
73
73
|
end
|
74
74
|
|
75
75
|
def apply_font_settings_and_add_fragment_to_line(fragment)
|
76
76
|
result = nil
|
77
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
78
|
result = add_fragment_to_line(fragment)
|
82
79
|
end
|
83
80
|
result
|
@@ -87,27 +84,31 @@ module Prawn
|
|
87
84
|
# the line
|
88
85
|
#
|
89
86
|
def add_fragment_to_line(fragment)
|
90
|
-
if fragment ==
|
87
|
+
if fragment == ''
|
91
88
|
true
|
92
89
|
elsif fragment == "\n"
|
93
90
|
@newline_encountered = true
|
94
91
|
false
|
95
92
|
else
|
96
93
|
tokenize(fragment).each do |segment|
|
97
|
-
if segment == zero_width_space
|
94
|
+
if segment == zero_width_space(segment.encoding)
|
98
95
|
segment_width = 0
|
99
96
|
else
|
100
|
-
segment_width = @document.width_of(segment, :
|
97
|
+
segment_width = @document.width_of(segment, kerning: @kerning)
|
101
98
|
end
|
102
99
|
|
103
100
|
if @accumulated_width + segment_width <= @width
|
104
101
|
@accumulated_width += segment_width
|
105
|
-
|
106
|
-
|
102
|
+
shy = soft_hyphen(segment.encoding)
|
103
|
+
if segment[-1] == shy
|
104
|
+
sh_width = @document.width_of(shy, kerning: @kerning)
|
107
105
|
@accumulated_width -= sh_width
|
108
106
|
end
|
109
107
|
@fragment_output += segment
|
110
108
|
else
|
109
|
+
if @accumulated_width.zero? && @line_contains_more_than_one_word
|
110
|
+
@line_contains_more_than_one_word = false
|
111
|
+
end
|
111
112
|
end_of_the_line_reached(segment)
|
112
113
|
fragment_finished(fragment)
|
113
114
|
return false
|
@@ -121,13 +122,24 @@ module Prawn
|
|
121
122
|
|
122
123
|
# The pattern used to determine chunks of text to place on a given line
|
123
124
|
#
|
124
|
-
def scan_pattern
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
125
|
+
def scan_pattern(encoding = ::Encoding::UTF_8)
|
126
|
+
ebc = break_chars(encoding)
|
127
|
+
eshy = soft_hyphen(encoding)
|
128
|
+
ehy = hyphen(encoding)
|
129
|
+
ews = whitespace(encoding)
|
130
|
+
|
131
|
+
patterns = [
|
132
|
+
"[^#{ebc}]+#{eshy}",
|
133
|
+
"[^#{ebc}]+#{ehy}+",
|
134
|
+
"[^#{ebc}]+",
|
135
|
+
"[#{ews}]+",
|
136
|
+
"#{ehy}+[^#{ebc}]*",
|
137
|
+
eshy.to_s
|
138
|
+
]
|
139
|
+
|
140
|
+
pattern = patterns
|
141
|
+
.map { |p| p.encode(encoding) }
|
142
|
+
.join('|')
|
131
143
|
|
132
144
|
Regexp.new(pattern)
|
133
145
|
end
|
@@ -136,24 +148,56 @@ module Prawn
|
|
136
148
|
# current line, which in turn determines whether character level
|
137
149
|
# word breaking is needed
|
138
150
|
#
|
139
|
-
def word_division_scan_pattern
|
140
|
-
|
151
|
+
def word_division_scan_pattern(encoding = ::Encoding::UTF_8)
|
152
|
+
common_whitespaces = ["\t", "\n", "\v", "\r", ' '].map do |c|
|
153
|
+
c.encode(encoding)
|
154
|
+
end
|
155
|
+
|
156
|
+
Regexp.union(
|
157
|
+
common_whitespaces +
|
158
|
+
[
|
159
|
+
zero_width_space(encoding),
|
160
|
+
soft_hyphen(encoding),
|
161
|
+
hyphen(encoding)
|
162
|
+
].compact
|
163
|
+
)
|
164
|
+
end
|
165
|
+
|
166
|
+
def soft_hyphen(encoding = ::Encoding::UTF_8)
|
167
|
+
Prawn::Text::SHY.encode(encoding)
|
168
|
+
rescue ::Encoding::InvalidByteSequenceError,
|
169
|
+
::Encoding::UndefinedConversionError
|
170
|
+
nil
|
141
171
|
end
|
142
172
|
|
143
|
-
def break_chars
|
144
|
-
|
173
|
+
def break_chars(encoding = ::Encoding::UTF_8)
|
174
|
+
[
|
175
|
+
whitespace(encoding),
|
176
|
+
soft_hyphen(encoding),
|
177
|
+
hyphen(encoding)
|
178
|
+
].join('')
|
145
179
|
end
|
146
180
|
|
147
|
-
def
|
148
|
-
|
181
|
+
def zero_width_space(encoding = ::Encoding::UTF_8)
|
182
|
+
Prawn::Text::ZWSP.encode(encoding)
|
183
|
+
rescue ::Encoding::InvalidByteSequenceError,
|
184
|
+
::Encoding::UndefinedConversionError
|
185
|
+
nil
|
149
186
|
end
|
150
187
|
|
151
|
-
def
|
152
|
-
"
|
188
|
+
def whitespace(encoding = ::Encoding::UTF_8)
|
189
|
+
"\s\t#{zero_width_space(encoding)}".encode(encoding)
|
190
|
+
end
|
191
|
+
|
192
|
+
def hyphen(_encoding = ::Encoding::UTF_8)
|
193
|
+
'-'
|
194
|
+
rescue ::Encoding::InvalidByteSequenceError,
|
195
|
+
::Encoding::UndefinedConversionError
|
196
|
+
nil
|
153
197
|
end
|
154
198
|
|
155
199
|
def line_empty?
|
156
|
-
@line_empty && @accumulated_width
|
200
|
+
@line_empty && @accumulated_width.zero?
|
157
201
|
end
|
158
202
|
|
159
203
|
def initialize_line(options)
|
@@ -174,35 +218,42 @@ module Prawn
|
|
174
218
|
@line_full = false
|
175
219
|
end
|
176
220
|
|
177
|
-
def set_soft_hyphen_and_zero_width_space
|
178
|
-
# this is done once per fragment, after the font settings for the fragment are applied --
|
179
|
-
# it could actually be skipped if the font hasn't changed
|
180
|
-
font = @document.font
|
181
|
-
@soft_hyphen = font.normalize_encoding(Prawn::Text::SHY)
|
182
|
-
@zero_width_space = font.unicode? ? Prawn::Text::ZWSP : ""
|
183
|
-
end
|
184
|
-
|
185
221
|
def fragment_finished(fragment)
|
186
222
|
if fragment == "\n"
|
187
223
|
@newline_encountered = true
|
188
224
|
@line_empty = false
|
189
225
|
else
|
190
|
-
update_output_based_on_last_fragment(
|
226
|
+
update_output_based_on_last_fragment(
|
227
|
+
fragment,
|
228
|
+
soft_hyphen(fragment.encoding)
|
229
|
+
)
|
191
230
|
update_line_status_based_on_last_output
|
192
|
-
|
231
|
+
pull_preceding_fragment_to_join_this_one?(fragment)
|
193
232
|
end
|
194
233
|
remember_this_fragment_for_backward_looking_ops
|
195
234
|
end
|
196
235
|
|
197
|
-
def update_output_based_on_last_fragment(
|
198
|
-
|
199
|
-
|
200
|
-
|
236
|
+
def update_output_based_on_last_fragment(
|
237
|
+
fragment, normalized_soft_hyphen = nil
|
238
|
+
)
|
239
|
+
remaining_text =
|
240
|
+
fragment.slice(@fragment_output.length..fragment.length)
|
241
|
+
if line_finished? && line_empty? && @fragment_output.empty? &&
|
242
|
+
!fragment.strip.empty?
|
243
|
+
raise Prawn::Errors::CannotFit
|
244
|
+
end
|
245
|
+
@arranger.update_last_string(
|
246
|
+
@fragment_output,
|
247
|
+
remaining_text,
|
248
|
+
normalized_soft_hyphen
|
249
|
+
)
|
201
250
|
end
|
202
251
|
|
203
|
-
def
|
204
|
-
if @fragment_output.empty? && !current_fragment.empty? &&
|
205
|
-
|
252
|
+
def pull_preceding_fragment_to_join_this_one?(current_fragment)
|
253
|
+
if @fragment_output.empty? && !current_fragment.empty? &&
|
254
|
+
@line_contains_more_than_one_word
|
255
|
+
unless previous_fragment_ended_with_breakable? ||
|
256
|
+
fragment_begins_with_breakable?(current_fragment)
|
206
257
|
@fragment_output = @previous_fragment_output_without_last_word
|
207
258
|
update_output_based_on_last_fragment(@previous_fragment)
|
208
259
|
end
|
@@ -212,10 +263,12 @@ module Prawn
|
|
212
263
|
def remember_this_fragment_for_backward_looking_ops
|
213
264
|
@previous_fragment = @fragment_output.dup
|
214
265
|
pf = @previous_fragment
|
215
|
-
@previous_fragment_ended_with_breakable =
|
216
|
-
|
266
|
+
@previous_fragment_ended_with_breakable =
|
267
|
+
pf =~ /[#{break_chars(pf.encoding)}]$/
|
268
|
+
last_word = pf.slice(/[^#{break_chars(pf.encoding)}]*$/)
|
217
269
|
last_word_length = last_word.nil? ? 0 : last_word.length
|
218
|
-
@previous_fragment_output_without_last_word =
|
270
|
+
@previous_fragment_output_without_last_word =
|
271
|
+
pf.slice(0, pf.length - last_word_length)
|
219
272
|
end
|
220
273
|
|
221
274
|
def previous_fragment_ended_with_breakable?
|
@@ -223,7 +276,7 @@ module Prawn
|
|
223
276
|
end
|
224
277
|
|
225
278
|
def fragment_begins_with_breakable?(fragment)
|
226
|
-
fragment =~ /^[#{break_chars}]/
|
279
|
+
fragment =~ /^[#{break_chars(fragment.encoding)}]/
|
227
280
|
end
|
228
281
|
|
229
282
|
def line_finished?
|
@@ -231,25 +284,29 @@ module Prawn
|
|
231
284
|
end
|
232
285
|
|
233
286
|
def update_line_status_based_on_last_output
|
234
|
-
|
287
|
+
if @fragment_output =~
|
288
|
+
word_division_scan_pattern(@fragment_output.encoding)
|
289
|
+
@line_contains_more_than_one_word = true
|
290
|
+
end
|
235
291
|
end
|
236
292
|
|
237
293
|
def end_of_the_line_reached(segment)
|
238
294
|
update_line_status_based_on_last_output
|
239
|
-
|
295
|
+
unless @disable_wrap_by_char || @line_contains_more_than_one_word
|
296
|
+
wrap_by_char(segment)
|
297
|
+
end
|
240
298
|
@line_full = true
|
241
299
|
end
|
242
300
|
|
243
301
|
def wrap_by_char(segment)
|
244
|
-
font = @document.font
|
245
302
|
segment.each_char do |char|
|
246
|
-
break unless append_char(char
|
303
|
+
break unless append_char(char)
|
247
304
|
end
|
248
305
|
end
|
249
306
|
|
250
|
-
def append_char(char
|
307
|
+
def append_char(char)
|
251
308
|
# kerning doesn't make sense in the context of a single character
|
252
|
-
char_width =
|
309
|
+
char_width = @document.width_of(char)
|
253
310
|
|
254
311
|
if @accumulated_width + char_width <= @width
|
255
312
|
@accumulated_width += char_width
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
# text/formatted/parser.rb : Implements a bi-directional parser between a subset
|
4
2
|
# of html and formatted text arrays
|
5
3
|
#
|
@@ -16,46 +14,50 @@ module Prawn
|
|
16
14
|
|
17
15
|
PARSER_REGEX = begin
|
18
16
|
regex_string = "\n|" \
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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>|' \
|
31
29
|
"[^<\n]+"
|
32
30
|
Regexp.new(regex_string, Regexp::MULTILINE)
|
33
31
|
end
|
34
32
|
|
35
|
-
def self.format(string, *
|
36
|
-
tokens = string.gsub(
|
37
|
-
|
33
|
+
def self.format(string, *_args)
|
34
|
+
tokens = string.gsub(%r{<br\s*/?>}, "\n").scan(PARSER_REGEX)
|
35
|
+
array_from_tokens(tokens)
|
38
36
|
end
|
39
37
|
|
40
38
|
def self.to_string(array)
|
41
|
-
prefixes = {
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
39
|
+
prefixes = {
|
40
|
+
bold: '<b>',
|
41
|
+
italic: '<i>',
|
42
|
+
underline: '<u>',
|
43
|
+
strikethrough: '<strikethrough>',
|
44
|
+
subscript: '<sub>',
|
45
|
+
superscript: '<sup>'
|
46
|
+
}
|
47
|
+
suffixes = {
|
48
|
+
bold: '</b>',
|
49
|
+
italic: '</i>',
|
50
|
+
underline: '</u>',
|
51
|
+
strikethrough: '</strikethrough>',
|
52
|
+
subscript: '</sub>',
|
53
|
+
superscript: '</sup>'
|
54
|
+
}
|
53
55
|
array.collect do |hash|
|
54
|
-
prefix =
|
55
|
-
suffix =
|
56
|
+
prefix = ''
|
57
|
+
suffix = ''
|
56
58
|
if hash[:styles]
|
57
59
|
hash[:styles].each do |style|
|
58
|
-
prefix
|
60
|
+
prefix += prefixes[style]
|
59
61
|
suffix = suffixes[style] + suffix
|
60
62
|
end
|
61
63
|
end
|
@@ -63,35 +65,37 @@ module Prawn
|
|
63
65
|
font = hash[:font] ? " name='#{hash[:font]}'" : nil
|
64
66
|
size = hash[:size] ? " size='#{hash[:size]}'" : nil
|
65
67
|
if hash[:character_spacing]
|
66
|
-
character_spacing =
|
68
|
+
character_spacing =
|
69
|
+
" character_spacing='#{hash[:character_spacing]}'"
|
67
70
|
else
|
68
71
|
character_spacing = nil
|
69
72
|
end
|
70
73
|
if font || size || character_spacing
|
71
|
-
prefix
|
72
|
-
suffix =
|
74
|
+
prefix += "<font#{font}#{size}#{character_spacing}>"
|
75
|
+
suffix = '</font>'
|
73
76
|
end
|
74
77
|
|
75
78
|
link = hash[:link] ? " href='#{hash[:link]}'" : nil
|
76
79
|
anchor = hash[:anchor] ? " anchor='#{hash[:anchor]}'" : nil
|
77
80
|
if link || anchor
|
78
|
-
prefix
|
79
|
-
suffix =
|
81
|
+
prefix += "<link#{link}#{anchor}>"
|
82
|
+
suffix = '</link>'
|
80
83
|
end
|
81
84
|
|
82
85
|
if hash[:color]
|
83
|
-
if hash[:color].
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
suffix =
|
86
|
+
prefix += if hash[:color].is_a?(Array)
|
87
|
+
"<color c='#{hash[:color][0]}'" \
|
88
|
+
" m='#{hash[:color][1]}'" \
|
89
|
+
" y='#{hash[:color][2]}'" \
|
90
|
+
" k='#{hash[:color][3]}'>"
|
91
|
+
else
|
92
|
+
"<color rgb='#{hash[:color]}'>"
|
93
|
+
end
|
94
|
+
suffix = '</color>'
|
92
95
|
end
|
93
96
|
|
94
|
-
string = hash[:text].gsub(
|
97
|
+
string = hash[:text].gsub('&', '&').gsub('>', '>')
|
98
|
+
.gsub('<', '<')
|
95
99
|
prefix + string + suffix
|
96
100
|
end.join
|
97
101
|
end
|
@@ -104,11 +108,13 @@ module Prawn
|
|
104
108
|
array.each do |hash|
|
105
109
|
hash[:text].scan(scan_pattern).each do |string|
|
106
110
|
if string == "\n"
|
107
|
-
|
111
|
+
if previous_string == "\n"
|
112
|
+
paragraph << hash.dup.merge(text: "\n")
|
113
|
+
end
|
108
114
|
paragraphs << paragraph unless paragraph.empty?
|
109
115
|
paragraph = []
|
110
116
|
else
|
111
|
-
paragraph << hash.dup.merge(:
|
117
|
+
paragraph << hash.dup.merge(text: string)
|
112
118
|
end
|
113
119
|
previous_string = string
|
114
120
|
end
|
@@ -128,88 +134,106 @@ module Prawn
|
|
128
134
|
sizes = []
|
129
135
|
character_spacings = []
|
130
136
|
|
131
|
-
|
137
|
+
tokens.each do |token|
|
132
138
|
case token
|
133
|
-
when
|
139
|
+
when '<b>', '<strong>'
|
134
140
|
styles << :bold
|
135
|
-
when
|
141
|
+
when '<i>', '<em>'
|
136
142
|
styles << :italic
|
137
|
-
when
|
143
|
+
when '<u>'
|
138
144
|
styles << :underline
|
139
|
-
when
|
145
|
+
when '<strikethrough>'
|
140
146
|
styles << :strikethrough
|
141
|
-
when
|
147
|
+
when '<sub>'
|
142
148
|
styles << :subscript
|
143
|
-
when
|
149
|
+
when '<sup>'
|
144
150
|
styles << :superscript
|
145
|
-
when
|
151
|
+
when '</b>', '</strong>'
|
146
152
|
styles.delete(:bold)
|
147
|
-
when
|
153
|
+
when '</i>', '</em>'
|
148
154
|
styles.delete(:italic)
|
149
|
-
when
|
155
|
+
when '</u>'
|
150
156
|
styles.delete(:underline)
|
151
|
-
when
|
157
|
+
when '</strikethrough>'
|
152
158
|
styles.delete(:strikethrough)
|
153
|
-
when
|
159
|
+
when '</sub>'
|
154
160
|
styles.delete(:subscript)
|
155
|
-
when
|
161
|
+
when '</sup>'
|
156
162
|
styles.delete(:superscript)
|
157
|
-
when
|
163
|
+
when '</link>', '</a>'
|
158
164
|
link = nil
|
159
165
|
anchor = nil
|
160
166
|
local = nil
|
161
|
-
when
|
167
|
+
when '</color>'
|
162
168
|
colors.pop
|
163
|
-
when
|
169
|
+
when '</font>'
|
164
170
|
fonts.pop
|
165
171
|
sizes.pop
|
166
172
|
character_spacings.pop
|
173
|
+
when /^<link[^>]*>$/, /^<a[^>]*>$/
|
174
|
+
matches = /href="([^"]*)"/.match(token) ||
|
175
|
+
/href='([^']*)'/.match(token)
|
176
|
+
link = matches[1] unless matches.nil?
|
177
|
+
|
178
|
+
matches = /anchor="([^"]*)"/.match(token) ||
|
179
|
+
/anchor='([^']*)'/.match(token)
|
180
|
+
anchor = matches[1] unless matches.nil?
|
181
|
+
|
182
|
+
matches = /local="([^"]*)"/.match(token) ||
|
183
|
+
/local='([^']*)'/.match(token)
|
184
|
+
local = matches[1] unless matches.nil?
|
185
|
+
when /^<color[^>]*>$/
|
186
|
+
matches = /rgb="#?([^"]*)"/.match(token) ||
|
187
|
+
/rgb='#?([^']*)'/.match(token)
|
188
|
+
colors << matches[1] if matches
|
189
|
+
|
190
|
+
match = /c="#?([^"]*)"/.match(token) ||
|
191
|
+
/c='#?([^']*)'/.match(token)
|
192
|
+
c = match[1].to_i unless match.nil?
|
193
|
+
match = /m="#?([^"]*)"/.match(token) ||
|
194
|
+
/m='#?([^']*)'/.match(token)
|
195
|
+
m = match[1].to_i unless match.nil?
|
196
|
+
match = /y="#?([^"]*)"/.match(token) ||
|
197
|
+
/y='#?([^']*)'/.match(token)
|
198
|
+
y = match[1].to_i unless match.nil?
|
199
|
+
match = /k="#?([^"]*)"/.match(token) ||
|
200
|
+
/k='#?([^']*)'/.match(token)
|
201
|
+
k = match[1].to_i unless match.nil?
|
202
|
+
colors << [c, m, y, k] if [c, m, y, k].all?
|
203
|
+
|
204
|
+
# intend to support rgb="#ffffff" or rgb='#ffffff',
|
205
|
+
# r="255" g="255" b="255" or r='255' g='255' b='255',
|
206
|
+
# and c="100" m="100" y="100" k="100" or
|
207
|
+
# c='100' m='100' y='100' k='100'
|
208
|
+
# color = { :rgb => "#ffffff" }
|
209
|
+
# color = { :r => 255, :g => 255, :b => 255 }
|
210
|
+
# color = { :c => 100, :m => 100, :y => 100, :k => 100 }
|
211
|
+
when /^<font[^>]*>$/
|
212
|
+
matches = /name="([^"]*)"/.match(token) ||
|
213
|
+
/name='([^']*)'/.match(token)
|
214
|
+
fonts << matches[1] unless matches.nil?
|
215
|
+
|
216
|
+
matches = /size="([^"]*)"/.match(token) ||
|
217
|
+
/size='([^']*)'/.match(token)
|
218
|
+
sizes << matches[1].to_f unless matches.nil?
|
219
|
+
|
220
|
+
matches = /character_spacing="([^"]*)"/.match(token) ||
|
221
|
+
/character_spacing='([^']*)'/.match(token)
|
222
|
+
character_spacings << matches[1].to_f unless matches.nil?
|
167
223
|
else
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
matches = /c="#?([^"]*)" +m="#?([^"]*)" +y="#?([^"]*)" +k="#?([^"]*)"/.match(token) ||
|
182
|
-
/c='#?([^']*)' +m='#?([^']*)' +y='#?([^']*)' +k='#?([^']*)'/.match(token)
|
183
|
-
colors << [matches[1].to_i, matches[2].to_i, matches[3].to_i, matches[4].to_i] if matches
|
184
|
-
|
185
|
-
# intend to support rgb="#ffffff" or rgb='#ffffff',
|
186
|
-
# r="255" g="255" b="255" or r='255' g='255' b='255',
|
187
|
-
# and c="100" m="100" y="100" k="100" or
|
188
|
-
# c='100' m='100' y='100' k='100'
|
189
|
-
# color = { :rgb => "#ffffff" }
|
190
|
-
# color = { :r => 255, :g => 255, :b => 255 }
|
191
|
-
# color = { :c => 100, :m => 100, :y => 100, :k => 100 }
|
192
|
-
elsif token =~ /^<font[^>]*>$/
|
193
|
-
matches = /name="([^"]*)"/.match(token) || /name='([^']*)'/.match(token)
|
194
|
-
fonts << matches[1] unless matches.nil?
|
195
|
-
|
196
|
-
matches = /size="([^"]*)"/.match(token) || /size='([^']*)'/.match(token)
|
197
|
-
sizes << matches[1].to_f unless matches.nil?
|
198
|
-
|
199
|
-
matches = /character_spacing="([^"]*)"/.match(token) || /character_spacing='([^']*)'/.match(token)
|
200
|
-
character_spacings << matches[1].to_f unless matches.nil?
|
201
|
-
else
|
202
|
-
string = token.gsub("<", "<").gsub(">", ">").gsub("&", "&")
|
203
|
-
array << { :text => string,
|
204
|
-
:styles => styles.dup,
|
205
|
-
:color => colors.last,
|
206
|
-
:local => local,
|
207
|
-
:link => link,
|
208
|
-
:anchor => anchor,
|
209
|
-
:font => fonts.last,
|
210
|
-
:size => sizes.last,
|
211
|
-
:character_spacing => character_spacings.last }
|
212
|
-
end
|
224
|
+
string = token.gsub('<', '<').gsub('>', '>')
|
225
|
+
.gsub('&', '&')
|
226
|
+
array << {
|
227
|
+
text: string,
|
228
|
+
styles: styles.dup,
|
229
|
+
color: colors.last,
|
230
|
+
local: local,
|
231
|
+
link: link,
|
232
|
+
anchor: anchor,
|
233
|
+
font: fonts.last,
|
234
|
+
size: sizes.last,
|
235
|
+
character_spacing: character_spacings.last
|
236
|
+
}
|
213
237
|
end
|
214
238
|
end
|
215
239
|
array
|