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