alphasights-prawn 0.10.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.
- data/COPYING +340 -0
- data/HACKING +50 -0
- data/LICENSE +56 -0
- data/README +141 -0
- data/Rakefile +52 -0
- data/data/encodings/win_ansi.txt +29 -0
- data/data/fonts/Action Man.dfont +0 -0
- data/data/fonts/Activa.ttf +0 -0
- data/data/fonts/Chalkboard.ttf +0 -0
- data/data/fonts/Courier-Bold.afm +342 -0
- data/data/fonts/Courier-BoldOblique.afm +342 -0
- data/data/fonts/Courier-Oblique.afm +342 -0
- data/data/fonts/Courier.afm +342 -0
- data/data/fonts/DejaVuSans.ttf +0 -0
- data/data/fonts/Dustismo_Roman.ttf +0 -0
- data/data/fonts/Helvetica-Bold.afm +2827 -0
- data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
- data/data/fonts/Helvetica-Oblique.afm +3051 -0
- data/data/fonts/Helvetica.afm +3051 -0
- data/data/fonts/MustRead.html +19 -0
- data/data/fonts/Symbol.afm +213 -0
- data/data/fonts/Times-Bold.afm +2588 -0
- data/data/fonts/Times-BoldItalic.afm +2384 -0
- data/data/fonts/Times-Italic.afm +2667 -0
- data/data/fonts/Times-Roman.afm +2419 -0
- data/data/fonts/ZapfDingbats.afm +225 -0
- data/data/fonts/comicsans.ttf +0 -0
- data/data/fonts/gkai00mp.ttf +0 -0
- data/data/images/16bit.alpha +0 -0
- data/data/images/16bit.dat +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/barcode_issue.png +0 -0
- data/data/images/dice.alpha +0 -0
- data/data/images/dice.dat +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/letterhead.jpg +0 -0
- data/data/images/page_white_text.alpha +0 -0
- data/data/images/page_white_text.dat +0 -0
- data/data/images/page_white_text.png +0 -0
- data/data/images/pigs.jpg +0 -0
- data/data/images/rails.dat +0 -0
- data/data/images/rails.png +0 -0
- data/data/images/ruport.png +0 -0
- data/data/images/ruport_data.dat +0 -0
- data/data/images/ruport_transparent.png +0 -0
- data/data/images/ruport_type0.png +0 -0
- data/data/images/stef.jpg +0 -0
- data/data/images/tru256.bmp +0 -0
- data/data/images/web-links.dat +1 -0
- data/data/images/web-links.png +0 -0
- data/data/pdfs/complex_template.pdf +0 -0
- data/data/pdfs/contains_ttf_font.pdf +0 -0
- data/data/pdfs/encrypted.pdf +0 -0
- data/data/pdfs/hexagon.pdf +61 -0
- data/data/pdfs/indirect_reference.pdf +86 -0
- data/data/pdfs/nested_pages.pdf +118 -0
- data/data/pdfs/resources_as_indirect_object.pdf +83 -0
- data/data/pdfs/two_hexagons.pdf +90 -0
- data/data/pdfs/version_1_6.pdf +61 -0
- data/data/shift_jis_text.txt +1 -0
- data/examples/bounding_box/bounding_boxes.rb +43 -0
- data/examples/bounding_box/indentation.rb +34 -0
- data/examples/bounding_box/russian_boxes.rb +36 -0
- data/examples/bounding_box/stretched_nesting.rb +67 -0
- data/examples/builder/simple.rb +28 -0
- data/examples/example_helper.rb +4 -0
- data/examples/general/background.rb +23 -0
- data/examples/general/canvas.rb +15 -0
- data/examples/general/context_sensitive_headers.rb +37 -0
- data/examples/general/float.rb +11 -0
- data/examples/general/margin.rb +36 -0
- data/examples/general/measurement_units.rb +51 -0
- data/examples/general/metadata-info.rb +16 -0
- data/examples/general/multi_page_layout.rb +18 -0
- data/examples/general/outlines.rb +50 -0
- data/examples/general/page_geometry.rb +31 -0
- data/examples/general/page_numbering.rb +15 -0
- data/examples/general/repeaters.rb +47 -0
- data/examples/general/stamp.rb +41 -0
- data/examples/general/templates.rb +13 -0
- data/examples/graphics/basic_images.rb +23 -0
- data/examples/graphics/chunkable.rb +38 -0
- data/examples/graphics/cmyk.rb +12 -0
- data/examples/graphics/curves.rb +11 -0
- data/examples/graphics/hexagon.rb +13 -0
- data/examples/graphics/image_fit.rb +15 -0
- data/examples/graphics/image_flow.rb +37 -0
- data/examples/graphics/image_position.rb +17 -0
- data/examples/graphics/line.rb +32 -0
- data/examples/graphics/png_types.rb +22 -0
- data/examples/graphics/polygons.rb +16 -0
- data/examples/graphics/remote_images.rb +12 -0
- data/examples/graphics/rounded_polygons.rb +19 -0
- data/examples/graphics/rounded_rectangle.rb +20 -0
- data/examples/graphics/ruport_style_helpers.rb +19 -0
- data/examples/graphics/stroke_bounds.rb +20 -0
- data/examples/graphics/stroke_cap_and_join.rb +45 -0
- data/examples/graphics/stroke_dash.rb +42 -0
- data/examples/graphics/transformations.rb +52 -0
- data/examples/graphics/transparency.rb +26 -0
- data/examples/m17n/chinese_text_wrapping.rb +17 -0
- data/examples/m17n/euro.rb +15 -0
- data/examples/m17n/sjis.rb +28 -0
- data/examples/m17n/utf8.rb +13 -0
- data/examples/m17n/win_ansi_charset.rb +54 -0
- data/examples/security/hello_foo.rb +8 -0
- data/examples/table/bill.rb +53 -0
- data/examples/table/cell.rb +12 -0
- data/examples/table/checkerboard.rb +22 -0
- data/examples/table/header.rb +14 -0
- data/examples/table/inline_format_table.rb +12 -0
- data/examples/table/multi_page_table.rb +9 -0
- data/examples/table/simple_table.rb +24 -0
- data/examples/table/subtable.rb +12 -0
- data/examples/table/widths.rb +20 -0
- data/examples/text/alignment.rb +18 -0
- data/examples/text/character_spacing.rb +12 -0
- data/examples/text/dfont.rb +48 -0
- data/examples/text/family_based_styling.rb +24 -0
- data/examples/text/font_calculations.rb +91 -0
- data/examples/text/font_size.rb +33 -0
- data/examples/text/hyphenation.rb +45 -0
- data/examples/text/indent_paragraphs.rb +22 -0
- data/examples/text/inline_format.rb +103 -0
- data/examples/text/kerning.rb +30 -0
- data/examples/text/rotated.rb +98 -0
- data/examples/text/shaped_text_box.rb +31 -0
- data/examples/text/simple_text.rb +17 -0
- data/examples/text/simple_text_ttf.rb +17 -0
- data/examples/text/text_box.rb +88 -0
- data/examples/text/text_box_returning_excess.rb +51 -0
- data/examples/text/text_flow.rb +67 -0
- data/lib/prawn.rb +27 -0
- data/lib/prawn/canvas.rb +119 -0
- data/lib/prawn/chunkable.rb +37 -0
- data/lib/prawn/compatibility.rb +51 -0
- data/lib/prawn/core.rb +85 -0
- data/lib/prawn/core/annotations.rb +61 -0
- data/lib/prawn/core/byte_string.rb +9 -0
- data/lib/prawn/core/chunk.rb +36 -0
- data/lib/prawn/core/destinations.rb +90 -0
- data/lib/prawn/core/document_state.rb +78 -0
- data/lib/prawn/core/literal_string.rb +16 -0
- data/lib/prawn/core/name_tree.rb +165 -0
- data/lib/prawn/core/object_store.rb +236 -0
- data/lib/prawn/core/page.rb +179 -0
- data/lib/prawn/core/pdf_object.rb +108 -0
- data/lib/prawn/core/reference.rb +112 -0
- data/lib/prawn/core/text.rb +140 -0
- data/lib/prawn/core/text/formatted/arranger.rb +266 -0
- data/lib/prawn/core/text/formatted/line_wrap.rb +127 -0
- data/lib/prawn/core/text/formatted/wrap.rb +112 -0
- data/lib/prawn/core/text/line_wrap.rb +209 -0
- data/lib/prawn/core/text/wrap.rb +80 -0
- data/lib/prawn/document.rb +573 -0
- data/lib/prawn/document/bounding_box.rb +425 -0
- data/lib/prawn/document/graphics_state.rb +48 -0
- data/lib/prawn/document/internals.rb +170 -0
- data/lib/prawn/document/page_geometry.rb +136 -0
- data/lib/prawn/document/snapshot.rb +87 -0
- data/lib/prawn/document_builder.rb +51 -0
- data/lib/prawn/document_builder/command.rb +38 -0
- data/lib/prawn/document_builder/constructs.rb +2 -0
- data/lib/prawn/document_builder/constructs/flowing_text_construct.rb +18 -0
- data/lib/prawn/document_builder/constructs/path_construct.rb +9 -0
- data/lib/prawn/document_builder/layout.rb +25 -0
- data/lib/prawn/document_builder/modifications.rb +2 -0
- data/lib/prawn/document_builder/modifications/layout_modification.rb +9 -0
- data/lib/prawn/document_builder/modifications/path_modification.rb +9 -0
- data/lib/prawn/encoding.rb +121 -0
- data/lib/prawn/errors.rb +94 -0
- data/lib/prawn/font.rb +341 -0
- data/lib/prawn/font/afm.rb +225 -0
- data/lib/prawn/font/dfont.rb +42 -0
- data/lib/prawn/font/ttf.rb +350 -0
- data/lib/prawn/graphics.rb +325 -0
- data/lib/prawn/graphics/cap_style.rb +38 -0
- data/lib/prawn/graphics/color.rb +205 -0
- data/lib/prawn/graphics/dash.rb +71 -0
- data/lib/prawn/graphics/join_style.rb +38 -0
- data/lib/prawn/graphics/transformation.rb +156 -0
- data/lib/prawn/graphics/transparency.rb +99 -0
- data/lib/prawn/images.rb +348 -0
- data/lib/prawn/images/jpg.rb +46 -0
- data/lib/prawn/images/png.rb +226 -0
- data/lib/prawn/measurement_extensions.rb +46 -0
- data/lib/prawn/measurements.rb +71 -0
- data/lib/prawn/outline.rb +278 -0
- data/lib/prawn/repeater.rb +129 -0
- data/lib/prawn/security.rb +262 -0
- data/lib/prawn/security/arcfour.rb +51 -0
- data/lib/prawn/stamp.rb +126 -0
- data/lib/prawn/table.rb +421 -0
- data/lib/prawn/table/accessors.rb +180 -0
- data/lib/prawn/table/cell.rb +350 -0
- data/lib/prawn/table/cell/in_table.rb +27 -0
- data/lib/prawn/table/cell/subtable.rb +65 -0
- data/lib/prawn/table/cell/text.rb +125 -0
- data/lib/prawn/text.rb +449 -0
- data/lib/prawn/text/box.rb +392 -0
- data/lib/prawn/text/formatted.rb +4 -0
- data/lib/prawn/text/formatted/box.rb +228 -0
- data/lib/prawn/text/formatted/fragment.rb +181 -0
- data/lib/prawn/text/formatted/parser.rb +213 -0
- data/spec/annotations_spec.rb +90 -0
- data/spec/bounding_box_spec.rb +190 -0
- data/spec/cell_spec.rb +348 -0
- data/spec/destinations_spec.rb +15 -0
- data/spec/document_spec.rb +473 -0
- data/spec/font_spec.rb +324 -0
- data/spec/formatted_text_arranger_spec.rb +426 -0
- data/spec/formatted_text_box_spec.rb +756 -0
- data/spec/formatted_text_fragment_spec.rb +211 -0
- data/spec/graphics_spec.rb +446 -0
- data/spec/images_spec.rb +96 -0
- data/spec/inline_formatted_text_parser_spec.rb +502 -0
- data/spec/jpg_spec.rb +25 -0
- data/spec/line_wrap_spec.rb +341 -0
- data/spec/measurement_units_spec.rb +23 -0
- data/spec/name_tree_spec.rb +112 -0
- data/spec/object_store_spec.rb +160 -0
- data/spec/outline_spec.rb +269 -0
- data/spec/pdf_object_spec.rb +170 -0
- data/spec/png_spec.rb +237 -0
- data/spec/reference_spec.rb +82 -0
- data/spec/repeater_spec.rb +96 -0
- data/spec/security_spec.rb +120 -0
- data/spec/snapshot_spec.rb +138 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/stamp_spec.rb +108 -0
- data/spec/stroke_styles_spec.rb +163 -0
- data/spec/table_spec.rb +598 -0
- data/spec/template_spec.rb +158 -0
- data/spec/text_at_spec.rb +119 -0
- data/spec/text_box_spec.rb +742 -0
- data/spec/text_spacing_spec.rb +75 -0
- data/spec/text_spec.rb +333 -0
- data/spec/text_with_inline_formatting_spec.rb +193 -0
- data/spec/transparency_spec.rb +89 -0
- metadata +331 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
# core/text/formatted/line_wrap.rb : Implements individual line wrapping of
|
|
4
|
+
# formatted text
|
|
5
|
+
#
|
|
6
|
+
# Copyright February 2010, Daniel Nelson. All Rights Reserved.
|
|
7
|
+
#
|
|
8
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
module Prawn
|
|
12
|
+
module Core
|
|
13
|
+
module Text
|
|
14
|
+
module Formatted #:nodoc:
|
|
15
|
+
|
|
16
|
+
class LineWrap < Prawn::Core::Text::LineWrap #:nodoc:
|
|
17
|
+
|
|
18
|
+
# Work in conjunction with the Prawn::Core::Formatted::Arranger
|
|
19
|
+
# defined in the :arranger option to determine what formatted text
|
|
20
|
+
# will fit within the width defined by the :width option
|
|
21
|
+
#
|
|
22
|
+
def wrap_line(options)
|
|
23
|
+
initialize_line(options)
|
|
24
|
+
|
|
25
|
+
while fragment = @arranger.next_string
|
|
26
|
+
@output = ""
|
|
27
|
+
preview = @arranger.preview_next_string
|
|
28
|
+
|
|
29
|
+
fragment.lstrip! if @line_output.empty? && fragment != "\n"
|
|
30
|
+
if @line_output.empty? && fragment.empty? && preview == "\n"
|
|
31
|
+
# this line was just whitespace followed by a newline, which is
|
|
32
|
+
# equivalent to just a newline
|
|
33
|
+
@arranger.update_last_string("", "")
|
|
34
|
+
next
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
if !add_fragment_to_line(fragment)
|
|
38
|
+
fragment_finished(fragment, true)
|
|
39
|
+
break
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
fragment_finished(fragment, preview == "\n" || preview.nil?)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
@arranger.finalize_line
|
|
46
|
+
@accumulated_width = @arranger.line_width
|
|
47
|
+
@space_count = @arranger.space_count
|
|
48
|
+
@arranger.line
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def initialize_line(options)
|
|
54
|
+
@document = options[:document]
|
|
55
|
+
@kerning = options[:kerning]
|
|
56
|
+
@width = options[:width]
|
|
57
|
+
|
|
58
|
+
@scan_pattern = scan_pattern
|
|
59
|
+
@word_division_scan_pattern = word_division_scan_pattern
|
|
60
|
+
|
|
61
|
+
@accumulated_width = 0
|
|
62
|
+
@line_output = ""
|
|
63
|
+
|
|
64
|
+
@arranger = options[:arranger]
|
|
65
|
+
@arranger.initialize_line
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def fragment_finished(fragment, finished_line)
|
|
69
|
+
if fragment == "\n"
|
|
70
|
+
@line_output = "\n" if @line_output.empty?
|
|
71
|
+
else
|
|
72
|
+
update_output_based_on_last_fragment(fragment, finished_line)
|
|
73
|
+
@line_output += @output
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def update_output_based_on_last_fragment(fragment, finished_line)
|
|
78
|
+
remaining_text = fragment.slice(@output.length..fragment.length)
|
|
79
|
+
@output.rstrip! if finished_line
|
|
80
|
+
raise Errors::CannotFit if finished_line && @line_output.empty? &&
|
|
81
|
+
@output.empty? && !fragment.strip.empty?
|
|
82
|
+
@arranger.update_last_string(@output, remaining_text)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# returns true iff all text was printed without running into the end of
|
|
86
|
+
# the line
|
|
87
|
+
#
|
|
88
|
+
def add_fragment_to_line(fragment)
|
|
89
|
+
return true if fragment == ""
|
|
90
|
+
return false if fragment == "\n"
|
|
91
|
+
previous_segment = nil
|
|
92
|
+
fragment.scan(@scan_pattern).each do |segment|
|
|
93
|
+
@arranger.apply_font_settings do
|
|
94
|
+
segment_width = @document.width_of(segment, :kerning => @kerning)
|
|
95
|
+
|
|
96
|
+
if @accumulated_width + segment_width <= @width
|
|
97
|
+
@accumulated_width += segment_width
|
|
98
|
+
@output += segment
|
|
99
|
+
else
|
|
100
|
+
end_of_the_line(segment)
|
|
101
|
+
return false
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
previous_segment = segment
|
|
105
|
+
end
|
|
106
|
+
true
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# If there is more than one word on the line, then clean up the last
|
|
110
|
+
# word on the line; otherwise, wrap by character
|
|
111
|
+
#
|
|
112
|
+
def end_of_the_line(segment)
|
|
113
|
+
if (@line_output + @output) =~ @word_division_scan_pattern
|
|
114
|
+
if segment =~ new_regexp("^#{hyphen}") &&
|
|
115
|
+
@output !~ new_regexp("[#{break_chars}]$")
|
|
116
|
+
remove_last_output_word
|
|
117
|
+
end
|
|
118
|
+
else
|
|
119
|
+
wrap_by_char(segment)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
require "prawn/core/text/formatted/line_wrap"
|
|
2
|
+
require "prawn/core/text/formatted/arranger"
|
|
3
|
+
|
|
4
|
+
module Prawn
|
|
5
|
+
module Core
|
|
6
|
+
module Text
|
|
7
|
+
module Formatted #:nodoc:
|
|
8
|
+
module Wrap
|
|
9
|
+
|
|
10
|
+
def initialize(array, options)
|
|
11
|
+
super(array, options)
|
|
12
|
+
@line_wrap = Prawn::Core::Text::Formatted::LineWrap.new
|
|
13
|
+
@arranger = Prawn::Core::Text::Formatted::Arranger.new(@document)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# See the developer documentation for Prawn::Core::Text#wrap
|
|
18
|
+
#
|
|
19
|
+
# Formatted#wrap should set some of the variables slightly differently
|
|
20
|
+
# than Text#wrap;
|
|
21
|
+
# <tt>@line_height</tt>::
|
|
22
|
+
# the height of the tallest fragment in the last printed line
|
|
23
|
+
# <tt>@descender</tt>::
|
|
24
|
+
# the descender height of the tallest fragment in the last
|
|
25
|
+
# printed line
|
|
26
|
+
# <tt>@ascender</tt>::
|
|
27
|
+
# the ascender heigth of the tallest fragment in the last
|
|
28
|
+
# printed line
|
|
29
|
+
#
|
|
30
|
+
# Returns any formatted text that was not printed
|
|
31
|
+
#
|
|
32
|
+
def wrap(array) #:nodoc:
|
|
33
|
+
initialize_wrap(array)
|
|
34
|
+
|
|
35
|
+
move_baseline = true
|
|
36
|
+
while @arranger.unfinished?
|
|
37
|
+
printed_fragments = []
|
|
38
|
+
|
|
39
|
+
line_to_print = @line_wrap.wrap_line(:document => @document,
|
|
40
|
+
:kerning => @kerning,
|
|
41
|
+
:width => available_width,
|
|
42
|
+
:arranger => @arranger)
|
|
43
|
+
|
|
44
|
+
move_baseline = false
|
|
45
|
+
break unless enough_height_for_this_line?
|
|
46
|
+
move_baseline_down
|
|
47
|
+
|
|
48
|
+
accumulated_width = 0
|
|
49
|
+
word_spacing = word_spacing_for_this_line
|
|
50
|
+
while fragment = @arranger.retrieve_fragment
|
|
51
|
+
fragment.word_spacing = word_spacing
|
|
52
|
+
if fragment.text == "\n"
|
|
53
|
+
printed_fragments << "\n" if @printed_lines.last == ""
|
|
54
|
+
break
|
|
55
|
+
end
|
|
56
|
+
printed_fragments << fragment.text
|
|
57
|
+
format_and_draw_fragment(fragment, accumulated_width,
|
|
58
|
+
@line_wrap.width, word_spacing)
|
|
59
|
+
accumulated_width += fragment.width
|
|
60
|
+
end
|
|
61
|
+
@printed_lines << printed_fragments.join("")
|
|
62
|
+
break if @single_line
|
|
63
|
+
move_baseline = true unless @arranger.finished?
|
|
64
|
+
end
|
|
65
|
+
move_baseline_down if move_baseline
|
|
66
|
+
@text = @printed_lines.join("\n")
|
|
67
|
+
|
|
68
|
+
@arranger.unconsumed
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def enough_height_for_this_line?
|
|
74
|
+
@line_height = @arranger.max_line_height
|
|
75
|
+
@descender = @arranger.max_descender
|
|
76
|
+
@ascender = @arranger.max_ascender
|
|
77
|
+
required_height = @baseline_y == 0 ? @line_height : @line_height + @descender
|
|
78
|
+
if @baseline_y.abs + required_height > @height
|
|
79
|
+
# no room for the full height of this line
|
|
80
|
+
@arranger.repack_unretrieved
|
|
81
|
+
false
|
|
82
|
+
else
|
|
83
|
+
true
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def initialize_wrap(array)
|
|
88
|
+
@text = nil
|
|
89
|
+
@arranger.format_array = array
|
|
90
|
+
|
|
91
|
+
# these values will depend on the maximum value within a given line
|
|
92
|
+
@line_height = 0
|
|
93
|
+
@descender = 0
|
|
94
|
+
@ascender = 0
|
|
95
|
+
@baseline_y = 0
|
|
96
|
+
|
|
97
|
+
@printed_lines = []
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def format_and_draw_fragment(fragment, accumulated_width,
|
|
101
|
+
line_width, word_spacing)
|
|
102
|
+
@arranger.apply_color_and_font_settings(fragment) do
|
|
103
|
+
draw_fragment(fragment, accumulated_width,
|
|
104
|
+
line_width, word_spacing)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
# core/text/line_wrap.rb : Implements individual line wrapping
|
|
4
|
+
#
|
|
5
|
+
# Copyright January 2010, Daniel Nelson. All Rights Reserved.
|
|
6
|
+
#
|
|
7
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
|
8
|
+
#
|
|
9
|
+
|
|
10
|
+
module Prawn
|
|
11
|
+
module Core
|
|
12
|
+
module Text
|
|
13
|
+
|
|
14
|
+
class LineWrap #:nodoc:
|
|
15
|
+
|
|
16
|
+
# The width of the last wrapped line
|
|
17
|
+
#
|
|
18
|
+
def width
|
|
19
|
+
@accumulated_width || 0
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# The number of spaces in the last wrapped line
|
|
23
|
+
#
|
|
24
|
+
def space_count
|
|
25
|
+
@space_count
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# The number of characters consumed from the last line passed into
|
|
29
|
+
# wrap_line. This may differ from the number of characters in the
|
|
30
|
+
# returned line because trailing white spaces are removed
|
|
31
|
+
#
|
|
32
|
+
def consumed_char_count
|
|
33
|
+
@consumed_char_count
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# The pattern used to determine chunks of text to place on a given line
|
|
37
|
+
#
|
|
38
|
+
def scan_pattern
|
|
39
|
+
pattern = "[^#{break_chars}]+#{soft_hyphen}|" +
|
|
40
|
+
"[^#{break_chars}]+#{hyphen}+|" +
|
|
41
|
+
"[^#{break_chars}]+|" +
|
|
42
|
+
"[#{whitespace}]+|" +
|
|
43
|
+
"#{hyphen}+[^#{break_chars}]*|" +
|
|
44
|
+
"#{soft_hyphen}"
|
|
45
|
+
new_regexp(pattern)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# The pattern used to determine whether any word breaks exist on a
|
|
49
|
+
# current line, which in turn determines whether character level
|
|
50
|
+
# word breaking is needed
|
|
51
|
+
#
|
|
52
|
+
def word_division_scan_pattern
|
|
53
|
+
new_regexp("\\s|[#{hyphen}#{soft_hyphen}]")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Take a single line and deterimine what part of it fits within the
|
|
57
|
+
# width defined by the :width option
|
|
58
|
+
#
|
|
59
|
+
def wrap_line(line, options)
|
|
60
|
+
initialize_line(options)
|
|
61
|
+
|
|
62
|
+
previous_segment = nil
|
|
63
|
+
whitespace_pattern = new_regexp("[#{whitespace}]")
|
|
64
|
+
|
|
65
|
+
line.scan(@scan_pattern).each do |segment|
|
|
66
|
+
|
|
67
|
+
# Don't let leading white space count against available space
|
|
68
|
+
if @output.empty? && segment =~ whitespace_pattern
|
|
69
|
+
@discarded_char_count += segment.length
|
|
70
|
+
next
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
segment_width = @document.width_of(segment, :kerning => @kerning)
|
|
74
|
+
|
|
75
|
+
if @accumulated_width + segment_width <= @width
|
|
76
|
+
@accumulated_width += segment_width
|
|
77
|
+
@output += segment
|
|
78
|
+
else
|
|
79
|
+
end_of_the_line(segment)
|
|
80
|
+
break
|
|
81
|
+
end
|
|
82
|
+
previous_segment = segment
|
|
83
|
+
end
|
|
84
|
+
raise Errors::CannotFit if @output.empty? && !line.strip.empty?
|
|
85
|
+
|
|
86
|
+
finalize_line
|
|
87
|
+
|
|
88
|
+
@space_count = @output.count(" ")
|
|
89
|
+
@output
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def initialize_line(options)
|
|
95
|
+
@document = options[:document]
|
|
96
|
+
@kerning = options[:kerning]
|
|
97
|
+
@width = options[:width]
|
|
98
|
+
|
|
99
|
+
@scan_pattern = scan_pattern
|
|
100
|
+
@word_division_scan_pattern = word_division_scan_pattern
|
|
101
|
+
|
|
102
|
+
@discarded_char_count = 0
|
|
103
|
+
@accumulated_width = 0
|
|
104
|
+
@output = ""
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def break_chars
|
|
108
|
+
"#{whitespace}#{soft_hyphen}#{hyphen}"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def whitespace
|
|
112
|
+
" \\t"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def hyphen
|
|
116
|
+
"-"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def soft_hyphen
|
|
120
|
+
@document.font.normalize_encoding("")
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# If there is more than one word on the line, then clean up the last
|
|
124
|
+
# word on the line; otherwise, wrap by character
|
|
125
|
+
#
|
|
126
|
+
def end_of_the_line(segment)
|
|
127
|
+
if @output =~ @word_division_scan_pattern
|
|
128
|
+
if segment =~ new_regexp("^#{hyphen}") &&
|
|
129
|
+
@output !~ new_regexp("[#{break_chars}]$")
|
|
130
|
+
remove_last_output_word
|
|
131
|
+
end
|
|
132
|
+
else
|
|
133
|
+
wrap_by_char(segment)
|
|
134
|
+
# since when adding characters individually, kerning has no effect,
|
|
135
|
+
# recompute width of the output after adding characters
|
|
136
|
+
# IMPORTANT: leave this computation here, rather than moving it into
|
|
137
|
+
# append_char because append_char is more general than
|
|
138
|
+
# end_of_the_line, so while end_of_the_line is likely to be
|
|
139
|
+
# overridden, wrap_by_char may not be, and we don't want to
|
|
140
|
+
# recompute line width except where necessary (for example, see
|
|
141
|
+
# Prawn::Core::Text::Formatted::LineWrap)
|
|
142
|
+
@accumulated_width = compute_output_width
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def remove_last_output_word
|
|
147
|
+
segments = []
|
|
148
|
+
regexp = new_regexp("[^#{break_chars}]+|[#{break_chars}]+")
|
|
149
|
+
@output.scan(regexp).each { |segment| segments << segment }
|
|
150
|
+
segments.pop
|
|
151
|
+
@output = segments.join("")
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def finalize_line
|
|
155
|
+
@consumed_char_count = @output.length + @discarded_char_count
|
|
156
|
+
|
|
157
|
+
@output = @output[0..-2].gsub(soft_hyphen, "") + @output[-1..-1]
|
|
158
|
+
|
|
159
|
+
strip_trailing_whitespace
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def strip_trailing_whitespace
|
|
163
|
+
@accumulated_width = compute_output_width if @output.strip!
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def compute_output_width
|
|
167
|
+
@document.width_of(@output, :kerning => @kerning)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def wrap_by_char(segment)
|
|
171
|
+
if @document.font.unicode?
|
|
172
|
+
segment.unpack("U*").each do |char_int|
|
|
173
|
+
break unless append_char([char_int].pack("U"))
|
|
174
|
+
end
|
|
175
|
+
else
|
|
176
|
+
segment.each_char do |char|
|
|
177
|
+
break unless append_char(char)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def append_char(char)
|
|
183
|
+
# kerning doesn't make sense in the context of a single character
|
|
184
|
+
char_width = @document.width_of(char)
|
|
185
|
+
@accumulated_width += char_width
|
|
186
|
+
|
|
187
|
+
if @accumulated_width >= @width
|
|
188
|
+
false
|
|
189
|
+
else
|
|
190
|
+
@output << char
|
|
191
|
+
true
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def new_regexp(pattern)
|
|
196
|
+
regexp = ruby_19 {
|
|
197
|
+
Regexp.new(pattern)
|
|
198
|
+
}
|
|
199
|
+
regexp = regexp || ruby_18 {
|
|
200
|
+
lang = @document.font.unicode? ? 'U' : 'N'
|
|
201
|
+
Regexp.new(pattern, 0, lang)
|
|
202
|
+
}
|
|
203
|
+
regexp
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
require "prawn/core/text/line_wrap"
|
|
2
|
+
|
|
3
|
+
module Prawn
|
|
4
|
+
module Core
|
|
5
|
+
module Text
|
|
6
|
+
module Wrap #:nodoc:
|
|
7
|
+
|
|
8
|
+
def initialize(text, options)
|
|
9
|
+
@line_wrap = Prawn::Core::Text::LineWrap.new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# #wrap is part of the developer API. Override it in extensions to Prawn
|
|
13
|
+
# that inherit Text::Box but need a different placement algorithm.
|
|
14
|
+
# #wrap is where the actual placement of text happens. If @inked is
|
|
15
|
+
# false, then all the placement computations should be performed, and
|
|
16
|
+
# unprinted text returned, but no text should actually be drawn to the
|
|
17
|
+
# PDF. This enables look-ahead computations that need to know whether all
|
|
18
|
+
# the text was printed under a set of conditions or how tall the text was
|
|
19
|
+
# under certain conditions.
|
|
20
|
+
#
|
|
21
|
+
# #wrap is called from several places within box.rb and relies on
|
|
22
|
+
# certain conditions established by render. Do not call #wrap from
|
|
23
|
+
# outside of Text::Box or its descendants.
|
|
24
|
+
#
|
|
25
|
+
# #wrap should set the following instance variables:
|
|
26
|
+
# <tt>@text</tt>:: the text that was printed
|
|
27
|
+
# <tt>@line_height</tt>:: the height of the last printed line
|
|
28
|
+
# <tt>@descender</tt>:: the descender height of the last printed line
|
|
29
|
+
# <tt>@ascender</tt>:: the ascender heigth of the last printed line
|
|
30
|
+
# <tt>@baseline_y</tt>:: the base line of the last printed line
|
|
31
|
+
#
|
|
32
|
+
# Returns any unprinted text
|
|
33
|
+
#
|
|
34
|
+
def wrap(text) #:nodoc:
|
|
35
|
+
@text = nil
|
|
36
|
+
remaining_text = text
|
|
37
|
+
@line_height = @document.font.height
|
|
38
|
+
@descender = @document.font.descender
|
|
39
|
+
@ascender = @document.font.ascender
|
|
40
|
+
@baseline_y = -@ascender
|
|
41
|
+
|
|
42
|
+
printed_lines = []
|
|
43
|
+
|
|
44
|
+
while remaining_text &&
|
|
45
|
+
remaining_text.length > 0 &&
|
|
46
|
+
@baseline_y.abs + @descender <= @height
|
|
47
|
+
line_to_print = @line_wrap.wrap_line(remaining_text.first_line,
|
|
48
|
+
:document => @document,
|
|
49
|
+
:kerning => @kerning,
|
|
50
|
+
:width => available_width)
|
|
51
|
+
|
|
52
|
+
remaining_text = remaining_text.slice(@line_wrap.consumed_char_count..
|
|
53
|
+
remaining_text.length)
|
|
54
|
+
include_ellipses = (@overflow == :ellipses && last_line? &&
|
|
55
|
+
remaining_text.length > 0)
|
|
56
|
+
printed_lines << draw_line(line_to_print, @line_wrap.width,
|
|
57
|
+
word_spacing_for_this_line, include_ellipses)
|
|
58
|
+
@baseline_y -= (@line_height + @leading)
|
|
59
|
+
break if @single_line
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
@text = printed_lines.join("\n")
|
|
63
|
+
|
|
64
|
+
remaining_text
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def word_spacing_for_this_line
|
|
70
|
+
if @align != :justify || @line_wrap.width.to_f / available_width.to_f < 0.75
|
|
71
|
+
0
|
|
72
|
+
else
|
|
73
|
+
(available_width - @line_wrap.width) / @line_wrap.space_count
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|