prawn 2.1.0 → 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 +36 -49
- data/lib/prawn/document.rb +180 -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 +8 -6
- 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 +102 -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 +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 +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 +190 -96
- data/lib/prawn/graphics/transformation.rb +15 -9
- 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 +65 -62
- 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 +53 -32
- 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 +122 -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 +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 +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 +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 +4 -5
- data/manual/graphics/fill_rules.rb +9 -10
- data/manual/graphics/gradients.rb +27 -21
- data/manual/graphics/graphics.rb +48 -40
- 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 +3 -4
- 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 +44 -31
- 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/{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 +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/{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 +144 -180
- 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/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,31 @@ module Prawn
|
|
87
86
|
# the line
|
88
87
|
#
|
89
88
|
def add_fragment_to_line(fragment)
|
90
|
-
if fragment ==
|
89
|
+
if fragment == ''
|
91
90
|
true
|
92
91
|
elsif fragment == "\n"
|
93
92
|
@newline_encountered = true
|
94
93
|
false
|
95
94
|
else
|
96
95
|
tokenize(fragment).each do |segment|
|
97
|
-
if segment == zero_width_space
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
96
|
+
segment_width = if segment == zero_width_space(segment.encoding)
|
97
|
+
0
|
98
|
+
else
|
99
|
+
@document.width_of(segment, kerning: @kerning)
|
100
|
+
end
|
102
101
|
|
103
102
|
if @accumulated_width + segment_width <= @width
|
104
103
|
@accumulated_width += segment_width
|
105
|
-
|
106
|
-
|
104
|
+
shy = soft_hyphen(segment.encoding)
|
105
|
+
if segment[-1] == shy
|
106
|
+
sh_width = @document.width_of(shy, kerning: @kerning)
|
107
107
|
@accumulated_width -= sh_width
|
108
108
|
end
|
109
109
|
@fragment_output += segment
|
110
110
|
else
|
111
|
+
if @accumulated_width.zero? && @line_contains_more_than_one_word
|
112
|
+
@line_contains_more_than_one_word = false
|
113
|
+
end
|
111
114
|
end_of_the_line_reached(segment)
|
112
115
|
fragment_finished(fragment)
|
113
116
|
return false
|
@@ -121,13 +124,24 @@ module Prawn
|
|
121
124
|
|
122
125
|
# The pattern used to determine chunks of text to place on a given line
|
123
126
|
#
|
124
|
-
def scan_pattern
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
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('|')
|
131
145
|
|
132
146
|
Regexp.new(pattern)
|
133
147
|
end
|
@@ -136,24 +150,56 @@ module Prawn
|
|
136
150
|
# current line, which in turn determines whether character level
|
137
151
|
# word breaking is needed
|
138
152
|
#
|
139
|
-
def word_division_scan_pattern
|
140
|
-
|
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
|
+
)
|
166
|
+
end
|
167
|
+
|
168
|
+
def soft_hyphen(encoding = ::Encoding::UTF_8)
|
169
|
+
Prawn::Text::SHY.encode(encoding)
|
170
|
+
rescue ::Encoding::InvalidByteSequenceError,
|
171
|
+
::Encoding::UndefinedConversionError
|
172
|
+
nil
|
141
173
|
end
|
142
174
|
|
143
|
-
def break_chars
|
144
|
-
|
175
|
+
def break_chars(encoding = ::Encoding::UTF_8)
|
176
|
+
[
|
177
|
+
whitespace(encoding),
|
178
|
+
soft_hyphen(encoding),
|
179
|
+
hyphen(encoding)
|
180
|
+
].join('')
|
145
181
|
end
|
146
182
|
|
147
|
-
def
|
148
|
-
|
183
|
+
def zero_width_space(encoding = ::Encoding::UTF_8)
|
184
|
+
Prawn::Text::ZWSP.encode(encoding)
|
185
|
+
rescue ::Encoding::InvalidByteSequenceError,
|
186
|
+
::Encoding::UndefinedConversionError
|
187
|
+
nil
|
149
188
|
end
|
150
189
|
|
151
|
-
def
|
152
|
-
"
|
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
|
153
199
|
end
|
154
200
|
|
155
201
|
def line_empty?
|
156
|
-
@line_empty && @accumulated_width
|
202
|
+
@line_empty && @accumulated_width.zero?
|
157
203
|
end
|
158
204
|
|
159
205
|
def initialize_line(options)
|
@@ -174,35 +220,43 @@ module Prawn
|
|
174
220
|
@line_full = false
|
175
221
|
end
|
176
222
|
|
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
223
|
def fragment_finished(fragment)
|
186
224
|
if fragment == "\n"
|
187
225
|
@newline_encountered = true
|
188
226
|
@line_empty = false
|
189
227
|
else
|
190
|
-
update_output_based_on_last_fragment(
|
228
|
+
update_output_based_on_last_fragment(
|
229
|
+
fragment,
|
230
|
+
soft_hyphen(fragment.encoding)
|
231
|
+
)
|
191
232
|
update_line_status_based_on_last_output
|
192
|
-
|
233
|
+
pull_preceding_fragment_to_join_this_one?(fragment)
|
193
234
|
end
|
194
235
|
remember_this_fragment_for_backward_looking_ops
|
195
236
|
end
|
196
237
|
|
197
|
-
def update_output_based_on_last_fragment(
|
198
|
-
|
199
|
-
|
200
|
-
|
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
|
+
)
|
201
253
|
end
|
202
254
|
|
203
|
-
def
|
204
|
-
if @fragment_output.empty? && !current_fragment.empty? &&
|
205
|
-
|
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)
|
206
260
|
@fragment_output = @previous_fragment_output_without_last_word
|
207
261
|
update_output_based_on_last_fragment(@previous_fragment)
|
208
262
|
end
|
@@ -212,10 +266,12 @@ module Prawn
|
|
212
266
|
def remember_this_fragment_for_backward_looking_ops
|
213
267
|
@previous_fragment = @fragment_output.dup
|
214
268
|
pf = @previous_fragment
|
215
|
-
@previous_fragment_ended_with_breakable =
|
216
|
-
|
269
|
+
@previous_fragment_ended_with_breakable =
|
270
|
+
pf =~ /[#{break_chars(pf.encoding)}]$/
|
271
|
+
last_word = pf.slice(/[^#{break_chars(pf.encoding)}]*$/)
|
217
272
|
last_word_length = last_word.nil? ? 0 : last_word.length
|
218
|
-
@previous_fragment_output_without_last_word =
|
273
|
+
@previous_fragment_output_without_last_word =
|
274
|
+
pf.slice(0, pf.length - last_word_length)
|
219
275
|
end
|
220
276
|
|
221
277
|
def previous_fragment_ended_with_breakable?
|
@@ -223,7 +279,7 @@ module Prawn
|
|
223
279
|
end
|
224
280
|
|
225
281
|
def fragment_begins_with_breakable?(fragment)
|
226
|
-
fragment =~ /^[#{break_chars}]/
|
282
|
+
fragment =~ /^[#{break_chars(fragment.encoding)}]/
|
227
283
|
end
|
228
284
|
|
229
285
|
def line_finished?
|
@@ -231,25 +287,28 @@ module Prawn
|
|
231
287
|
end
|
232
288
|
|
233
289
|
def update_line_status_based_on_last_output
|
234
|
-
|
290
|
+
if @fragment_output&.match?(word_division_scan_pattern(@fragment_output.encoding))
|
291
|
+
@line_contains_more_than_one_word = true
|
292
|
+
end
|
235
293
|
end
|
236
294
|
|
237
295
|
def end_of_the_line_reached(segment)
|
238
296
|
update_line_status_based_on_last_output
|
239
|
-
|
297
|
+
unless @disable_wrap_by_char || @line_contains_more_than_one_word
|
298
|
+
wrap_by_char(segment)
|
299
|
+
end
|
240
300
|
@line_full = true
|
241
301
|
end
|
242
302
|
|
243
303
|
def wrap_by_char(segment)
|
244
|
-
font = @document.font
|
245
304
|
segment.each_char do |char|
|
246
|
-
break unless append_char(char
|
305
|
+
break unless append_char(char)
|
247
306
|
end
|
248
307
|
end
|
249
308
|
|
250
|
-
def append_char(char
|
309
|
+
def append_char(char)
|
251
310
|
# kerning doesn't make sense in the context of a single character
|
252
|
-
char_width =
|
311
|
+
char_width = @document.width_of(char)
|
253
312
|
|
254
313
|
if @accumulated_width + char_width <= @width
|
255
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
|