prawn 2.1.0 → 2.4.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 +9 -41
- data/lib/prawn.rb +37 -49
- data/lib/prawn/document.rb +193 -141
- data/lib/prawn/document/bounding_box.rb +50 -30
- data/lib/prawn/document/column_box.rb +7 -7
- data/lib/prawn/document/internals.rb +8 -6
- data/lib/prawn/document/span.rb +22 -16
- data/lib/prawn/encoding.rb +69 -68
- data/lib/prawn/errors.rb +12 -7
- data/lib/prawn/font.rb +104 -69
- data/lib/prawn/font_metric_cache.rb +20 -13
- data/lib/prawn/{font → fonts}/afm.rb +108 -72
- 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 +126 -81
- data/lib/prawn/graphics.rb +119 -81
- data/lib/prawn/graphics/blend_mode.rb +9 -8
- data/lib/prawn/graphics/cap_style.rb +3 -3
- data/lib/prawn/graphics/color.rb +43 -39
- data/lib/prawn/graphics/dash.rb +23 -11
- data/lib/prawn/graphics/join_style.rb +9 -3
- data/lib/prawn/graphics/patterns.rb +204 -102
- data/lib/prawn/graphics/transformation.rb +15 -9
- data/lib/prawn/graphics/transparency.rb +17 -13
- data/lib/prawn/grid.rb +84 -48
- data/lib/prawn/image_handler.rb +5 -5
- data/lib/prawn/images.rb +60 -49
- data/lib/prawn/images/image.rb +2 -1
- data/lib/prawn/images/jpg.rb +31 -22
- data/lib/prawn/images/png.rb +67 -63
- data/lib/prawn/measurement_extensions.rb +10 -9
- data/lib/prawn/measurements.rb +19 -15
- data/lib/prawn/outline.rb +98 -77
- data/lib/prawn/repeater.rb +15 -11
- data/lib/prawn/security.rb +93 -70
- 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 +76 -60
- data/lib/prawn/text/box.rb +18 -14
- data/lib/prawn/text/formatted.rb +5 -5
- data/lib/prawn/text/formatted/arranger.rb +80 -40
- data/lib/prawn/text/formatted/box.rb +140 -101
- data/lib/prawn/text/formatted/fragment.rb +11 -14
- data/lib/prawn/text/formatted/line_wrap.rb +128 -67
- data/lib/prawn/text/formatted/parser.rb +147 -123
- data/lib/prawn/text/formatted/wrap.rb +48 -32
- data/lib/prawn/transformation_stack.rb +7 -5
- 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 +7 -8
- 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 +7 -8
- data/manual/bounding_box/creation.rb +6 -7
- data/manual/bounding_box/indentation.rb +14 -15
- data/manual/bounding_box/nesting.rb +25 -18
- 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 +15 -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 +18 -15
- data/manual/example_helper.rb +5 -4
- data/manual/graphics/blend_mode.rb +12 -9
- 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 +5 -6
- data/manual/graphics/fill_rules.rb +10 -10
- data/manual/graphics/gradients.rb +27 -21
- data/manual/graphics/graphics.rb +48 -40
- data/manual/graphics/helper.rb +19 -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 +3 -4
- data/manual/graphics/stroke_cap.rb +6 -7
- data/manual/graphics/stroke_dash.rb +15 -16
- data/manual/graphics/stroke_join.rb +5 -6
- data/manual/graphics/translate.rb +10 -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 +10 -11
- 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 +16 -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 +21 -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 +10 -8
- data/manual/text/formatted_callbacks.rb +33 -24
- data/manual/text/formatted_text.rb +36 -25
- data/manual/text/free_flowing_text.rb +22 -23
- 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 +12 -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 +33 -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 +20 -19
- data/manual/text/utf8.rb +11 -12
- data/manual/text/win_ansi_charset.rb +27 -25
- data/prawn.gemspec +41 -34
- data/spec/extensions/encoding_helpers.rb +3 -3
- data/spec/prawn/document/bounding_box_spec.rb +550 -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 +44 -0
- data/spec/prawn/document_spec.rb +805 -0
- data/spec/prawn/font_metric_cache_spec.rb +54 -0
- data/spec/prawn/font_spec.rb +544 -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 +872 -0
- data/spec/prawn/graphics_stroke_styles_spec.rb +229 -0
- data/spec/{image_handler_spec.rb → prawn/image_handler_spec.rb} +14 -14
- data/spec/prawn/images/jpg_spec.rb +20 -0
- data/spec/prawn/images/png_spec.rb +283 -0
- data/spec/prawn/images_spec.rb +229 -0
- data/spec/prawn/measurements_extensions_spec.rb +24 -0
- data/spec/prawn/outline_spec.rb +512 -0
- data/spec/prawn/repeater_spec.rb +166 -0
- data/spec/prawn/soft_mask_spec.rb +74 -0
- data/spec/prawn/stamp_spec.rb +173 -0
- data/spec/prawn/text/box_spec.rb +1110 -0
- data/spec/prawn/text/formatted/arranger_spec.rb +466 -0
- data/spec/prawn/text/formatted/box_spec.rb +849 -0
- data/spec/prawn/text/formatted/fragment_spec.rb +343 -0
- data/spec/prawn/text/formatted/line_wrap_spec.rb +495 -0
- data/spec/prawn/text/formatted/parser_spec.rb +697 -0
- data/spec/prawn/text_draw_text_spec.rb +150 -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/{transformation_stack_spec.rb → prawn/transformation_stack_spec.rb} +22 -19
- data/spec/prawn/view_spec.rb +63 -0
- data/spec/prawn_manual_spec.rb +35 -0
- data/spec/spec_helper.rb +18 -19
- metadata +102 -222
- metadata.gz.sig +0 -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,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
|
@@ -21,16 +21,14 @@ module Prawn
|
|
21
21
|
|
22
22
|
# The number of spaces in the last wrapped line
|
23
23
|
attr_reader :space_count
|
24
|
-
attr_reader :soft_hyphen
|
25
|
-
attr_reader :zero_width_space
|
26
24
|
|
27
25
|
# Whether this line is the last line in the paragraph
|
28
26
|
def paragraph_finished?
|
29
|
-
@newline_encountered ||
|
27
|
+
@newline_encountered || next_string_newline? || @arranger.finished?
|
30
28
|
end
|
31
29
|
|
32
30
|
def tokenize(fragment)
|
33
|
-
fragment.scan(scan_pattern)
|
31
|
+
fragment.scan(scan_pattern(fragment.encoding))
|
34
32
|
end
|
35
33
|
|
36
34
|
# Work in conjunction with the PDF::Formatted::Arranger
|
@@ -40,8 +38,10 @@ module Prawn
|
|
40
38
|
def wrap_line(options)
|
41
39
|
initialize_line(options)
|
42
40
|
|
41
|
+
# rubocop: disable Lint/AssignmentInCondition
|
43
42
|
while fragment = @arranger.next_string
|
44
|
-
|
43
|
+
# rubocop: enable Lint/AssignmentInCondition
|
44
|
+
@fragment_output = +''
|
45
45
|
|
46
46
|
fragment.lstrip! if first_fragment_on_this_line?(fragment)
|
47
47
|
next if empty_line?(fragment)
|
@@ -63,21 +63,20 @@ module Prawn
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def empty_line?(fragment)
|
66
|
-
empty = line_empty? && fragment.empty? &&
|
67
|
-
|
66
|
+
empty = line_empty? && fragment.empty? && next_string_newline?
|
67
|
+
if empty
|
68
|
+
@arranger.update_last_string('', '', soft_hyphen(fragment.encoding))
|
69
|
+
end
|
68
70
|
empty
|
69
71
|
end
|
70
72
|
|
71
|
-
def
|
73
|
+
def next_string_newline?
|
72
74
|
@arranger.preview_next_string == "\n"
|
73
75
|
end
|
74
76
|
|
75
77
|
def apply_font_settings_and_add_fragment_to_line(fragment)
|
76
78
|
result = nil
|
77
79
|
@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
80
|
result = add_fragment_to_line(fragment)
|
82
81
|
end
|
83
82
|
result
|
@@ -87,27 +86,33 @@ module Prawn
|
|
87
86
|
# the line
|
88
87
|
#
|
89
88
|
def add_fragment_to_line(fragment)
|
90
|
-
|
89
|
+
case fragment
|
90
|
+
when ''
|
91
91
|
true
|
92
|
-
|
92
|
+
when "\n"
|
93
93
|
@newline_encountered = true
|
94
94
|
false
|
95
95
|
else
|
96
96
|
tokenize(fragment).each do |segment|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
97
|
+
segment_width =
|
98
|
+
if segment == zero_width_space(segment.encoding)
|
99
|
+
0
|
100
|
+
else
|
101
|
+
@document.width_of(segment, kerning: @kerning)
|
102
|
+
end
|
102
103
|
|
103
104
|
if @accumulated_width + segment_width <= @width
|
104
105
|
@accumulated_width += segment_width
|
105
|
-
|
106
|
-
|
106
|
+
shy = soft_hyphen(segment.encoding)
|
107
|
+
if segment[-1] == shy
|
108
|
+
sh_width = @document.width_of(shy, kerning: @kerning)
|
107
109
|
@accumulated_width -= sh_width
|
108
110
|
end
|
109
111
|
@fragment_output += segment
|
110
112
|
else
|
113
|
+
if @accumulated_width.zero? && @line_contains_more_than_one_word
|
114
|
+
@line_contains_more_than_one_word = false
|
115
|
+
end
|
111
116
|
end_of_the_line_reached(segment)
|
112
117
|
fragment_finished(fragment)
|
113
118
|
return false
|
@@ -121,13 +126,24 @@ module Prawn
|
|
121
126
|
|
122
127
|
# The pattern used to determine chunks of text to place on a given line
|
123
128
|
#
|
124
|
-
def scan_pattern
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
129
|
+
def scan_pattern(encoding = ::Encoding::UTF_8)
|
130
|
+
ebc = break_chars(encoding)
|
131
|
+
eshy = soft_hyphen(encoding)
|
132
|
+
ehy = hyphen(encoding)
|
133
|
+
ews = whitespace(encoding)
|
134
|
+
|
135
|
+
patterns = [
|
136
|
+
"[^#{ebc}]+#{eshy}",
|
137
|
+
"[^#{ebc}]+#{ehy}+",
|
138
|
+
"[^#{ebc}]+",
|
139
|
+
"[#{ews}]+",
|
140
|
+
"#{ehy}+[^#{ebc}]*",
|
141
|
+
eshy.to_s
|
142
|
+
]
|
143
|
+
|
144
|
+
pattern = patterns
|
145
|
+
.map { |p| p.encode(encoding) }
|
146
|
+
.join('|')
|
131
147
|
|
132
148
|
Regexp.new(pattern)
|
133
149
|
end
|
@@ -136,24 +152,57 @@ module Prawn
|
|
136
152
|
# current line, which in turn determines whether character level
|
137
153
|
# word breaking is needed
|
138
154
|
#
|
139
|
-
def word_division_scan_pattern
|
140
|
-
|
155
|
+
def word_division_scan_pattern(encoding = ::Encoding::UTF_8)
|
156
|
+
common_whitespaces =
|
157
|
+
["\t", "\n", "\v", "\r", ' '].map do |c|
|
158
|
+
c.encode(encoding)
|
159
|
+
end
|
160
|
+
|
161
|
+
Regexp.union(
|
162
|
+
common_whitespaces +
|
163
|
+
[
|
164
|
+
zero_width_space(encoding),
|
165
|
+
soft_hyphen(encoding),
|
166
|
+
hyphen(encoding)
|
167
|
+
].compact
|
168
|
+
)
|
169
|
+
end
|
170
|
+
|
171
|
+
def soft_hyphen(encoding = ::Encoding::UTF_8)
|
172
|
+
Prawn::Text::SHY.encode(encoding)
|
173
|
+
rescue ::Encoding::InvalidByteSequenceError,
|
174
|
+
::Encoding::UndefinedConversionError
|
175
|
+
nil
|
141
176
|
end
|
142
177
|
|
143
|
-
def break_chars
|
144
|
-
|
178
|
+
def break_chars(encoding = ::Encoding::UTF_8)
|
179
|
+
[
|
180
|
+
whitespace(encoding),
|
181
|
+
soft_hyphen(encoding),
|
182
|
+
hyphen(encoding)
|
183
|
+
].join('')
|
145
184
|
end
|
146
185
|
|
147
|
-
def
|
148
|
-
|
186
|
+
def zero_width_space(encoding = ::Encoding::UTF_8)
|
187
|
+
Prawn::Text::ZWSP.encode(encoding)
|
188
|
+
rescue ::Encoding::InvalidByteSequenceError,
|
189
|
+
::Encoding::UndefinedConversionError
|
190
|
+
nil
|
149
191
|
end
|
150
192
|
|
151
|
-
def
|
152
|
-
"
|
193
|
+
def whitespace(encoding = ::Encoding::UTF_8)
|
194
|
+
"\s\t#{zero_width_space(encoding)}".encode(encoding)
|
195
|
+
end
|
196
|
+
|
197
|
+
def hyphen(_encoding = ::Encoding::UTF_8)
|
198
|
+
'-'
|
199
|
+
rescue ::Encoding::InvalidByteSequenceError,
|
200
|
+
::Encoding::UndefinedConversionError
|
201
|
+
nil
|
153
202
|
end
|
154
203
|
|
155
204
|
def line_empty?
|
156
|
-
@line_empty && @accumulated_width
|
205
|
+
@line_empty && @accumulated_width.zero?
|
157
206
|
end
|
158
207
|
|
159
208
|
def initialize_line(options)
|
@@ -174,48 +223,57 @@ module Prawn
|
|
174
223
|
@line_full = false
|
175
224
|
end
|
176
225
|
|
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
226
|
def fragment_finished(fragment)
|
186
227
|
if fragment == "\n"
|
187
228
|
@newline_encountered = true
|
188
229
|
@line_empty = false
|
189
230
|
else
|
190
|
-
update_output_based_on_last_fragment(
|
231
|
+
update_output_based_on_last_fragment(
|
232
|
+
fragment,
|
233
|
+
soft_hyphen(fragment.encoding)
|
234
|
+
)
|
191
235
|
update_line_status_based_on_last_output
|
192
|
-
|
236
|
+
pull_preceding_fragment_to_join_this_one?(fragment)
|
193
237
|
end
|
194
238
|
remember_this_fragment_for_backward_looking_ops
|
195
239
|
end
|
196
240
|
|
197
|
-
def update_output_based_on_last_fragment(
|
198
|
-
|
199
|
-
|
200
|
-
|
241
|
+
def update_output_based_on_last_fragment(
|
242
|
+
fragment, normalized_soft_hyphen = nil
|
243
|
+
)
|
244
|
+
remaining_text =
|
245
|
+
fragment.slice(@fragment_output.length..fragment.length)
|
246
|
+
if line_finished? && line_empty? && @fragment_output.empty? &&
|
247
|
+
!fragment.strip.empty?
|
248
|
+
raise Prawn::Errors::CannotFit
|
249
|
+
end
|
250
|
+
|
251
|
+
@arranger.update_last_string(
|
252
|
+
@fragment_output,
|
253
|
+
remaining_text,
|
254
|
+
normalized_soft_hyphen
|
255
|
+
)
|
201
256
|
end
|
202
257
|
|
203
|
-
def
|
204
|
-
if @fragment_output.empty? && !current_fragment.empty? &&
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
258
|
+
def pull_preceding_fragment_to_join_this_one?(current_fragment)
|
259
|
+
if @fragment_output.empty? && !current_fragment.empty? &&
|
260
|
+
@line_contains_more_than_one_word &&
|
261
|
+
!(previous_fragment_ended_with_breakable? ||
|
262
|
+
fragment_begins_with_breakable?(current_fragment))
|
263
|
+
@fragment_output = @previous_fragment_output_without_last_word
|
264
|
+
update_output_based_on_last_fragment(@previous_fragment)
|
209
265
|
end
|
210
266
|
end
|
211
267
|
|
212
268
|
def remember_this_fragment_for_backward_looking_ops
|
213
269
|
@previous_fragment = @fragment_output.dup
|
214
270
|
pf = @previous_fragment
|
215
|
-
@previous_fragment_ended_with_breakable =
|
216
|
-
|
271
|
+
@previous_fragment_ended_with_breakable =
|
272
|
+
pf =~ /[#{break_chars(pf.encoding)}]$/
|
273
|
+
last_word = pf.slice(/[^#{break_chars(pf.encoding)}]*$/)
|
217
274
|
last_word_length = last_word.nil? ? 0 : last_word.length
|
218
|
-
@previous_fragment_output_without_last_word =
|
275
|
+
@previous_fragment_output_without_last_word =
|
276
|
+
pf.slice(0, pf.length - last_word_length)
|
219
277
|
end
|
220
278
|
|
221
279
|
def previous_fragment_ended_with_breakable?
|
@@ -223,7 +281,7 @@ module Prawn
|
|
223
281
|
end
|
224
282
|
|
225
283
|
def fragment_begins_with_breakable?(fragment)
|
226
|
-
fragment =~ /^[#{break_chars}]/
|
284
|
+
fragment =~ /^[#{break_chars(fragment.encoding)}]/
|
227
285
|
end
|
228
286
|
|
229
287
|
def line_finished?
|
@@ -231,25 +289,28 @@ module Prawn
|
|
231
289
|
end
|
232
290
|
|
233
291
|
def update_line_status_based_on_last_output
|
234
|
-
|
292
|
+
if @fragment_output&.match?(word_division_scan_pattern(@fragment_output.encoding))
|
293
|
+
@line_contains_more_than_one_word = true
|
294
|
+
end
|
235
295
|
end
|
236
296
|
|
237
297
|
def end_of_the_line_reached(segment)
|
238
298
|
update_line_status_based_on_last_output
|
239
|
-
|
299
|
+
unless @disable_wrap_by_char || @line_contains_more_than_one_word
|
300
|
+
wrap_by_char(segment)
|
301
|
+
end
|
240
302
|
@line_full = true
|
241
303
|
end
|
242
304
|
|
243
305
|
def wrap_by_char(segment)
|
244
|
-
font = @document.font
|
245
306
|
segment.each_char do |char|
|
246
|
-
break unless append_char(char
|
307
|
+
break unless append_char(char)
|
247
308
|
end
|
248
309
|
end
|
249
310
|
|
250
|
-
def append_char(char
|
311
|
+
def append_char(char)
|
251
312
|
# kerning doesn't make sense in the context of a single character
|
252
|
-
char_width =
|
313
|
+
char_width = @document.width_of(char)
|
253
314
|
|
254
315
|
if @accumulated_width + char_width <= @width
|
255
316
|
@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
|
@@ -14,84 +14,88 @@ module Prawn
|
|
14
14
|
class Parser
|
15
15
|
# @group Extension API
|
16
16
|
|
17
|
-
PARSER_REGEX =
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
17
|
+
PARSER_REGEX =
|
18
|
+
begin
|
19
|
+
regex_string = "\n|" \
|
20
|
+
'<b>|</b>|' \
|
21
|
+
'<i>|</i>|' \
|
22
|
+
'<u>|</u>|' \
|
23
|
+
'<strikethrough>|</strikethrough>|' \
|
24
|
+
'<sub>|</sub>|' \
|
25
|
+
'<sup>|</sup>|' \
|
26
|
+
'<link[^>]*>|</link>|' \
|
27
|
+
'<color[^>]*>|</color>|' \
|
28
|
+
'<font[^>]*>|</font>|' \
|
29
|
+
'<strong>|</strong>|' \
|
30
|
+
'<em>|</em>|' \
|
31
|
+
'<a[^>]*>|</a>|' \
|
32
|
+
"[^<\n]+"
|
33
|
+
Regexp.new(regex_string, Regexp::MULTILINE)
|
34
|
+
end
|
34
35
|
|
35
|
-
def self.format(string, *
|
36
|
-
tokens = string.gsub(
|
37
|
-
|
36
|
+
def self.format(string, *_args)
|
37
|
+
tokens = string.gsub(%r{<br\s*/?>}, "\n").scan(PARSER_REGEX)
|
38
|
+
array_from_tokens(tokens)
|
38
39
|
end
|
39
40
|
|
40
41
|
def self.to_string(array)
|
41
|
-
prefixes = {
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
42
|
+
prefixes = {
|
43
|
+
bold: '<b>',
|
44
|
+
italic: '<i>',
|
45
|
+
underline: '<u>',
|
46
|
+
strikethrough: '<strikethrough>',
|
47
|
+
subscript: '<sub>',
|
48
|
+
superscript: '<sup>'
|
49
|
+
}
|
50
|
+
suffixes = {
|
51
|
+
bold: '</b>',
|
52
|
+
italic: '</i>',
|
53
|
+
underline: '</u>',
|
54
|
+
strikethrough: '</strikethrough>',
|
55
|
+
subscript: '</sub>',
|
56
|
+
superscript: '</sup>'
|
57
|
+
}
|
58
|
+
array.map do |hash|
|
59
|
+
prefix = ''
|
60
|
+
suffix = ''
|
61
|
+
hash[:styles]&.each do |style|
|
62
|
+
prefix += prefixes[style]
|
63
|
+
suffix = suffixes[style] + suffix
|
61
64
|
end
|
62
65
|
|
63
66
|
font = hash[:font] ? " name='#{hash[:font]}'" : nil
|
64
67
|
size = hash[:size] ? " size='#{hash[:size]}'" : nil
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
68
|
+
character_spacing =
|
69
|
+
if hash[:character_spacing]
|
70
|
+
" character_spacing='#{hash[:character_spacing]}'"
|
71
|
+
end
|
70
72
|
if font || size || character_spacing
|
71
|
-
prefix
|
72
|
-
suffix =
|
73
|
+
prefix += "<font#{font}#{size}#{character_spacing}>"
|
74
|
+
suffix = '</font>'
|
73
75
|
end
|
74
76
|
|
75
77
|
link = hash[:link] ? " href='#{hash[:link]}'" : nil
|
76
78
|
anchor = hash[:anchor] ? " anchor='#{hash[:anchor]}'" : nil
|
77
79
|
if link || anchor
|
78
|
-
prefix
|
79
|
-
suffix =
|
80
|
+
prefix += "<link#{link}#{anchor}>"
|
81
|
+
suffix = '</link>'
|
80
82
|
end
|
81
83
|
|
82
84
|
if hash[:color]
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
85
|
+
prefix +=
|
86
|
+
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
|