prawn-git 2.0.1
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 +7 -0
- data/.yardopts +10 -0
- data/COPYING +2 -0
- data/GPLv2 +340 -0
- data/GPLv3 +674 -0
- data/Gemfile +11 -0
- data/LICENSE +56 -0
- data/Rakefile +55 -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/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/images/16bit.alpha +0 -0
- data/data/images/16bit.color +0 -0
- data/data/images/16bit.png +0 -0
- data/data/images/arrow.png +0 -0
- data/data/images/arrow2.png +0 -0
- data/data/images/dice.alpha +0 -0
- data/data/images/dice.color +0 -0
- data/data/images/dice.png +0 -0
- data/data/images/dice_interlaced.png +0 -0
- data/data/images/fractal.jpg +0 -0
- data/data/images/indexed_color.dat +0 -0
- data/data/images/indexed_color.png +0 -0
- data/data/images/letterhead.jpg +0 -0
- data/data/images/license.md +8 -0
- data/data/images/page_white_text.alpha +0 -0
- data/data/images/page_white_text.color +0 -0
- data/data/images/page_white_text.png +0 -0
- data/data/images/pal_bk.png +0 -0
- data/data/images/pigs.jpg +0 -0
- data/data/images/prawn.png +0 -0
- data/data/images/ruport.png +0 -0
- data/data/images/ruport_data.dat +0 -0
- data/data/images/ruport_transparent.png +0 -0
- data/data/images/ruport_type0.png +0 -0
- data/data/images/stef.jpg +0 -0
- data/data/images/tru256.bmp +0 -0
- data/data/images/web-links.dat +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/form.pdf +820 -0
- data/data/pdfs/hexagon.pdf +61 -0
- data/data/pdfs/indirect_reference.pdf +86 -0
- data/data/pdfs/multipage_template.pdf +127 -0
- data/data/pdfs/nested_pages.pdf +118 -0
- data/data/pdfs/page_without_mediabox.pdf +193 -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/lib/prawn.rb +89 -0
- data/lib/prawn/document.rb +706 -0
- data/lib/prawn/document/bounding_box.rb +539 -0
- data/lib/prawn/document/column_box.rb +144 -0
- data/lib/prawn/document/internals.rb +58 -0
- data/lib/prawn/document/span.rb +57 -0
- data/lib/prawn/encoding.rb +87 -0
- data/lib/prawn/errors.rb +80 -0
- data/lib/prawn/font.rb +413 -0
- data/lib/prawn/font/afm.rb +256 -0
- data/lib/prawn/font/dfont.rb +43 -0
- data/lib/prawn/font/ttf.rb +355 -0
- data/lib/prawn/font_metric_cache.rb +46 -0
- data/lib/prawn/graphics.rb +646 -0
- data/lib/prawn/graphics/cap_style.rb +47 -0
- data/lib/prawn/graphics/color.rb +232 -0
- data/lib/prawn/graphics/dash.rb +109 -0
- data/lib/prawn/graphics/join_style.rb +49 -0
- data/lib/prawn/graphics/patterns.rb +126 -0
- data/lib/prawn/graphics/transformation.rb +157 -0
- data/lib/prawn/graphics/transparency.rb +101 -0
- data/lib/prawn/grid.rb +279 -0
- data/lib/prawn/image_handler.rb +44 -0
- data/lib/prawn/images.rb +199 -0
- data/lib/prawn/images/image.rb +49 -0
- data/lib/prawn/images/jpg.rb +91 -0
- data/lib/prawn/images/png.rb +290 -0
- data/lib/prawn/measurement_extensions.rb +50 -0
- data/lib/prawn/measurements.rb +77 -0
- data/lib/prawn/outline.rb +289 -0
- data/lib/prawn/repeater.rb +124 -0
- data/lib/prawn/security.rb +288 -0
- data/lib/prawn/security/arcfour.rb +54 -0
- data/lib/prawn/soft_mask.rb +94 -0
- data/lib/prawn/stamp.rb +136 -0
- data/lib/prawn/text.rb +437 -0
- data/lib/prawn/text/box.rb +141 -0
- data/lib/prawn/text/formatted.rb +7 -0
- data/lib/prawn/text/formatted/arranger.rb +290 -0
- data/lib/prawn/text/formatted/box.rb +614 -0
- data/lib/prawn/text/formatted/fragment.rb +264 -0
- data/lib/prawn/text/formatted/line_wrap.rb +277 -0
- data/lib/prawn/text/formatted/parser.rb +224 -0
- data/lib/prawn/text/formatted/wrap.rb +160 -0
- data/lib/prawn/utilities.rb +46 -0
- data/lib/prawn/version.rb +5 -0
- data/lib/prawn/view.rb +91 -0
- data/manual/absolute_position.pdf +0 -0
- data/manual/basic_concepts/adding_pages.rb +27 -0
- data/manual/basic_concepts/basic_concepts.rb +36 -0
- data/manual/basic_concepts/creation.rb +39 -0
- data/manual/basic_concepts/cursor.rb +33 -0
- data/manual/basic_concepts/measurement.rb +25 -0
- data/manual/basic_concepts/origin.rb +38 -0
- data/manual/basic_concepts/other_cursor_helpers.rb +40 -0
- data/manual/basic_concepts/view.rb +42 -0
- data/manual/bounding_box/bounding_box.rb +39 -0
- data/manual/bounding_box/bounds.rb +49 -0
- data/manual/bounding_box/canvas.rb +24 -0
- data/manual/bounding_box/creation.rb +23 -0
- data/manual/bounding_box/indentation.rb +46 -0
- data/manual/bounding_box/nesting.rb +45 -0
- data/manual/bounding_box/russian_boxes.rb +40 -0
- data/manual/bounding_box/stretchy.rb +31 -0
- data/manual/contents.rb +29 -0
- data/manual/cover.rb +39 -0
- data/manual/document_and_page_options/background.rb +27 -0
- data/manual/document_and_page_options/document_and_page_options.rb +32 -0
- data/manual/document_and_page_options/metadata.rb +23 -0
- data/manual/document_and_page_options/page_margins.rb +38 -0
- data/manual/document_and_page_options/page_size.rb +34 -0
- data/manual/document_and_page_options/print_scaling.rb +20 -0
- data/manual/example_helper.rb +7 -0
- data/manual/graphics/circle_and_ellipse.rb +22 -0
- data/manual/graphics/color.rb +24 -0
- data/manual/graphics/common_lines.rb +30 -0
- data/manual/graphics/fill_and_stroke.rb +42 -0
- data/manual/graphics/fill_rules.rb +37 -0
- data/manual/graphics/gradients.rb +37 -0
- data/manual/graphics/graphics.rb +58 -0
- data/manual/graphics/helper.rb +24 -0
- data/manual/graphics/line_width.rb +35 -0
- data/manual/graphics/lines_and_curves.rb +41 -0
- data/manual/graphics/polygon.rb +29 -0
- data/manual/graphics/rectangle.rb +21 -0
- data/manual/graphics/rotate.rb +28 -0
- data/manual/graphics/scale.rb +41 -0
- data/manual/graphics/soft_masks.rb +46 -0
- data/manual/graphics/stroke_cap.rb +31 -0
- data/manual/graphics/stroke_dash.rb +48 -0
- data/manual/graphics/stroke_join.rb +30 -0
- data/manual/graphics/translate.rb +29 -0
- data/manual/graphics/transparency.rb +35 -0
- data/manual/how_to_read_this_manual.rb +40 -0
- data/manual/images/absolute_position.rb +23 -0
- data/manual/images/fit.rb +21 -0
- data/manual/images/horizontal.rb +25 -0
- data/manual/images/images.rb +40 -0
- data/manual/images/plain_image.rb +18 -0
- data/manual/images/scale.rb +22 -0
- data/manual/images/vertical.rb +28 -0
- data/manual/images/width_and_height.rb +25 -0
- data/manual/layout/boxes.rb +27 -0
- data/manual/layout/content.rb +25 -0
- data/manual/layout/layout.rb +28 -0
- data/manual/layout/simple_grid.rb +23 -0
- data/manual/outline/add_subsection_to.rb +61 -0
- data/manual/outline/insert_section_after.rb +47 -0
- data/manual/outline/outline.rb +32 -0
- data/manual/outline/sections_and_pages.rb +67 -0
- data/manual/repeatable_content/alternate_page_numbering.rb +32 -0
- data/manual/repeatable_content/page_numbering.rb +54 -0
- data/manual/repeatable_content/repeatable_content.rb +32 -0
- data/manual/repeatable_content/repeater.rb +55 -0
- data/manual/repeatable_content/stamp.rb +41 -0
- data/manual/security/encryption.rb +31 -0
- data/manual/security/permissions.rb +38 -0
- data/manual/security/security.rb +28 -0
- data/manual/table.rb +16 -0
- data/manual/text/alignment.rb +44 -0
- data/manual/text/color.rb +24 -0
- data/manual/text/column_box.rb +32 -0
- data/manual/text/fallback_fonts.rb +37 -0
- data/manual/text/font.rb +41 -0
- data/manual/text/font_size.rb +45 -0
- data/manual/text/font_style.rb +23 -0
- data/manual/text/formatted_callbacks.rb +60 -0
- data/manual/text/formatted_text.rb +50 -0
- data/manual/text/free_flowing_text.rb +51 -0
- data/manual/text/inline.rb +41 -0
- data/manual/text/kerning_and_character_spacing.rb +39 -0
- data/manual/text/leading.rb +25 -0
- data/manual/text/line_wrapping.rb +41 -0
- data/manual/text/paragraph_indentation.rb +34 -0
- data/manual/text/positioned_text.rb +38 -0
- data/manual/text/registering_families.rb +48 -0
- data/manual/text/rendering_and_color.rb +37 -0
- data/manual/text/right_to_left_text.rb +47 -0
- data/manual/text/rotation.rb +43 -0
- data/manual/text/single_usage.rb +37 -0
- data/manual/text/text.rb +73 -0
- data/manual/text/text_box_excess.rb +32 -0
- data/manual/text/text_box_extensions.rb +45 -0
- data/manual/text/text_box_overflow.rb +48 -0
- data/manual/text/utf8.rb +28 -0
- data/manual/text/win_ansi_charset.rb +60 -0
- data/prawn.gemspec +45 -0
- data/spec/acceptance/png.rb +25 -0
- data/spec/annotations_spec.rb +74 -0
- data/spec/bounding_box_spec.rb +510 -0
- data/spec/column_box_spec.rb +65 -0
- data/spec/data/curves.pdf +66 -0
- data/spec/destinations_spec.rb +15 -0
- data/spec/document_spec.rb +748 -0
- data/spec/extensions/encoding_helpers.rb +11 -0
- data/spec/extensions/mocha.rb +46 -0
- data/spec/font_metric_cache_spec.rb +52 -0
- data/spec/font_spec.rb +474 -0
- data/spec/formatted_text_arranger_spec.rb +421 -0
- data/spec/formatted_text_box_spec.rb +705 -0
- data/spec/formatted_text_fragment_spec.rb +298 -0
- data/spec/graphics_spec.rb +683 -0
- data/spec/grid_spec.rb +96 -0
- data/spec/image_handler_spec.rb +54 -0
- data/spec/images_spec.rb +153 -0
- data/spec/inline_formatted_text_parser_spec.rb +564 -0
- data/spec/jpg_spec.rb +25 -0
- data/spec/line_wrap_spec.rb +367 -0
- data/spec/measurement_units_spec.rb +25 -0
- data/spec/outline_spec.rb +430 -0
- data/spec/png_spec.rb +245 -0
- data/spec/reference_spec.rb +25 -0
- data/spec/repeater_spec.rb +160 -0
- data/spec/security_spec.rb +158 -0
- data/spec/soft_mask_spec.rb +79 -0
- data/spec/span_spec.rb +44 -0
- data/spec/spec_helper.rb +54 -0
- data/spec/stamp_spec.rb +160 -0
- data/spec/stroke_styles_spec.rb +211 -0
- data/spec/text_at_spec.rb +143 -0
- data/spec/text_box_spec.rb +1043 -0
- data/spec/text_rendering_mode_spec.rb +45 -0
- data/spec/text_spacing_spec.rb +93 -0
- data/spec/text_spec.rb +557 -0
- data/spec/text_with_inline_formatting_spec.rb +35 -0
- data/spec/transparency_spec.rb +91 -0
- data/spec/view_spec.rb +43 -0
- metadata +509 -0
@@ -0,0 +1,224 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# text/formatted/parser.rb : Implements a bi-directional parser between a subset
|
4
|
+
# of html and formatted text arrays
|
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 Text
|
13
|
+
module Formatted
|
14
|
+
|
15
|
+
class Parser
|
16
|
+
# @group Extension API
|
17
|
+
|
18
|
+
PARSER_REGEX = 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
|
35
|
+
|
36
|
+
def self.format(string, *args)
|
37
|
+
tokens = string.gsub(/<br\s*\/?>/, "\n").scan(PARSER_REGEX)
|
38
|
+
self.array_from_tokens(tokens)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.to_string(array)
|
42
|
+
prefixes = { :bold => "<b>",
|
43
|
+
:italic => "<i>",
|
44
|
+
:underline => "<u>",
|
45
|
+
:strikethrough => "<strikethrough>",
|
46
|
+
:subscript => "<sub>",
|
47
|
+
:superscript => "<sup>" }
|
48
|
+
suffixes = { :bold => "</b>",
|
49
|
+
:italic => "</i>",
|
50
|
+
:underline => "</u>",
|
51
|
+
:strikethrough => "</strikethrough>",
|
52
|
+
:subscript => "</sub>",
|
53
|
+
:superscript => "</sup>" }
|
54
|
+
array.collect do |hash|
|
55
|
+
prefix = ""
|
56
|
+
suffix = ""
|
57
|
+
if hash[:styles]
|
58
|
+
hash[:styles].each do |style|
|
59
|
+
prefix = prefix + prefixes[style]
|
60
|
+
suffix = suffixes[style] + suffix
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
font = hash[:font] ? " name='#{hash[:font]}'" : nil
|
65
|
+
size = hash[:size] ? " size='#{hash[:size]}'" : nil
|
66
|
+
if hash[:character_spacing]
|
67
|
+
character_spacing = " character_spacing='#{hash[:character_spacing]}'"
|
68
|
+
else
|
69
|
+
character_spacing = nil
|
70
|
+
end
|
71
|
+
if font || size || character_spacing
|
72
|
+
prefix = prefix + "<font#{font}#{size}#{character_spacing}>"
|
73
|
+
suffix = "</font>"
|
74
|
+
end
|
75
|
+
|
76
|
+
link = hash[:link] ? " href='#{hash[:link]}'" : nil
|
77
|
+
anchor = hash[:anchor] ? " anchor='#{hash[:anchor]}'" : nil
|
78
|
+
if link || anchor
|
79
|
+
prefix = prefix + "<link#{link}#{anchor}>"
|
80
|
+
suffix = "</link>"
|
81
|
+
end
|
82
|
+
|
83
|
+
if hash[:color]
|
84
|
+
if hash[:color].kind_of?(Array)
|
85
|
+
prefix = prefix + "<color c='#{hash[:color][0]}'" +
|
86
|
+
" m='#{hash[:color][1]}'" +
|
87
|
+
" y='#{hash[:color][2]}'" +
|
88
|
+
" k='#{hash[:color][3]}'>"
|
89
|
+
else
|
90
|
+
prefix = prefix + "<color rgb='#{hash[:color]}'>"
|
91
|
+
end
|
92
|
+
suffix = "</color>"
|
93
|
+
end
|
94
|
+
|
95
|
+
string = hash[:text].gsub("&", "&").gsub(">", ">").gsub("<", "<")
|
96
|
+
prefix + string + suffix
|
97
|
+
end.join
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.array_paragraphs(array) #:nodoc:
|
101
|
+
paragraphs = []
|
102
|
+
paragraph = []
|
103
|
+
previous_string = "\n"
|
104
|
+
scan_pattern = /[^\n]+|\n/
|
105
|
+
array.each do |hash|
|
106
|
+
hash[:text].scan(scan_pattern).each do |string|
|
107
|
+
if string == "\n"
|
108
|
+
paragraph << hash.dup.merge(:text => "\n") if previous_string == "\n"
|
109
|
+
paragraphs << paragraph unless paragraph.empty?
|
110
|
+
paragraph = []
|
111
|
+
else
|
112
|
+
paragraph << hash.dup.merge(:text => string)
|
113
|
+
end
|
114
|
+
previous_string = string
|
115
|
+
end
|
116
|
+
end
|
117
|
+
paragraphs << paragraph unless paragraph.empty?
|
118
|
+
paragraphs
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def self.array_from_tokens(tokens)
|
124
|
+
array = []
|
125
|
+
styles = []
|
126
|
+
colors = []
|
127
|
+
link = nil
|
128
|
+
anchor = nil
|
129
|
+
local = nil
|
130
|
+
fonts = []
|
131
|
+
sizes = []
|
132
|
+
character_spacings = []
|
133
|
+
|
134
|
+
while token = tokens.shift
|
135
|
+
case token
|
136
|
+
when "<b>", "<strong>"
|
137
|
+
styles << :bold
|
138
|
+
when "<i>", "<em>"
|
139
|
+
styles << :italic
|
140
|
+
when "<u>"
|
141
|
+
styles << :underline
|
142
|
+
when "<strikethrough>"
|
143
|
+
styles << :strikethrough
|
144
|
+
when "<sub>"
|
145
|
+
styles << :subscript
|
146
|
+
when "<sup>"
|
147
|
+
styles << :superscript
|
148
|
+
when "</b>", "</strong>"
|
149
|
+
styles.delete(:bold)
|
150
|
+
when "</i>", "</em>"
|
151
|
+
styles.delete(:italic)
|
152
|
+
when "</u>"
|
153
|
+
styles.delete(:underline)
|
154
|
+
when "</strikethrough>"
|
155
|
+
styles.delete(:strikethrough)
|
156
|
+
when "</sub>"
|
157
|
+
styles.delete(:subscript)
|
158
|
+
when "</sup>"
|
159
|
+
styles.delete(:superscript)
|
160
|
+
when "</link>", "</a>"
|
161
|
+
link = nil
|
162
|
+
anchor = nil
|
163
|
+
local = nil
|
164
|
+
when "</color>"
|
165
|
+
colors.pop
|
166
|
+
when "</font>"
|
167
|
+
fonts.pop
|
168
|
+
sizes.pop
|
169
|
+
character_spacings.pop
|
170
|
+
else
|
171
|
+
if token =~ /^<link[^>]*>$/ or token =~ /^<a[^>]*>$/
|
172
|
+
matches = /href="([^"]*)"/.match(token) || /href='([^']*)'/.match(token)
|
173
|
+
link = matches[1] unless matches.nil?
|
174
|
+
|
175
|
+
matches = /anchor="([^"]*)"/.match(token) || /anchor='([^']*)'/.match(token)
|
176
|
+
anchor = matches[1] unless matches.nil?
|
177
|
+
|
178
|
+
matches = /local="([^"]*)"/.match(token) || /local='([^']*)'/.match(token)
|
179
|
+
local = matches[1] unless matches.nil?
|
180
|
+
elsif token =~ /^<color[^>]*>$/
|
181
|
+
matches = /rgb="#?([^"]*)"/.match(token) || /rgb='#?([^']*)'/.match(token)
|
182
|
+
colors << matches[1] if matches
|
183
|
+
|
184
|
+
matches = /c="#?([^"]*)" +m="#?([^"]*)" +y="#?([^"]*)" +k="#?([^"]*)"/.match(token) ||
|
185
|
+
/c='#?([^']*)' +m='#?([^']*)' +y='#?([^']*)' +k='#?([^']*)'/.match(token)
|
186
|
+
colors << [matches[1].to_i, matches[2].to_i, matches[3].to_i, matches[4].to_i] if matches
|
187
|
+
|
188
|
+
# intend to support rgb="#ffffff" or rgb='#ffffff',
|
189
|
+
# r="255" g="255" b="255" or r='255' g='255' b='255',
|
190
|
+
# and c="100" m="100" y="100" k="100" or
|
191
|
+
# c='100' m='100' y='100' k='100'
|
192
|
+
# color = { :rgb => "#ffffff" }
|
193
|
+
# color = { :r => 255, :g => 255, :b => 255 }
|
194
|
+
# color = { :c => 100, :m => 100, :y => 100, :k => 100 }
|
195
|
+
elsif token =~ /^<font[^>]*>$/
|
196
|
+
matches = /name="([^"]*)"/.match(token) || /name='([^']*)'/.match(token)
|
197
|
+
fonts << matches[1] unless matches.nil?
|
198
|
+
|
199
|
+
matches = /size="([^"]*)"/.match(token) || /size='([^']*)'/.match(token)
|
200
|
+
sizes << matches[1].to_f unless matches.nil?
|
201
|
+
|
202
|
+
matches = /character_spacing="([^"]*)"/.match(token) || /character_spacing='([^']*)'/.match(token)
|
203
|
+
character_spacings << matches[1].to_f unless matches.nil?
|
204
|
+
else
|
205
|
+
string = token.gsub("<", "<").gsub(">", ">").gsub("&", "&")
|
206
|
+
array << { :text => string,
|
207
|
+
:styles => styles.dup,
|
208
|
+
:color => colors.last,
|
209
|
+
:local => local,
|
210
|
+
:link => link,
|
211
|
+
:anchor => anchor,
|
212
|
+
:font => fonts.last,
|
213
|
+
:size => sizes.last,
|
214
|
+
:character_spacing => character_spacings.last }
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
array
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# wrap.rb: Handles text wrapping for for formatted text
|
4
|
+
#
|
5
|
+
# Contributed by Daniel Nelson
|
6
|
+
#
|
7
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
|
+
|
9
|
+
require_relative "line_wrap"
|
10
|
+
require_relative "arranger"
|
11
|
+
|
12
|
+
module Prawn
|
13
|
+
module Text
|
14
|
+
module Formatted #:nodoc:
|
15
|
+
# @private
|
16
|
+
|
17
|
+
module Wrap #:nodoc:
|
18
|
+
|
19
|
+
def initialize(array, options)
|
20
|
+
@line_wrap = Prawn::Text::Formatted::LineWrap.new
|
21
|
+
@arranger = Prawn::Text::Formatted::Arranger.new(@document,
|
22
|
+
:kerning => options[:kerning])
|
23
|
+
@disable_wrap_by_char = options[:disable_wrap_by_char]
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
# See the developer documentation for PDF::Core::Text#wrap
|
28
|
+
#
|
29
|
+
# Formatted#wrap should set the following variables:
|
30
|
+
# <tt>@line_height</tt>::
|
31
|
+
# the height of the tallest fragment in the last printed line
|
32
|
+
# <tt>@descender</tt>::
|
33
|
+
# the descender height of the tallest fragment in the last
|
34
|
+
# printed line
|
35
|
+
# <tt>@ascender</tt>::
|
36
|
+
# the ascender heigth of the tallest fragment in the last
|
37
|
+
# printed line
|
38
|
+
# <tt>@baseline_y</tt>::
|
39
|
+
# the baseline of the current line
|
40
|
+
# <tt>@nothing_printed</tt>::
|
41
|
+
# set to true until something is printed, then false
|
42
|
+
# <tt>@everything_printed</tt>::
|
43
|
+
# set to false until everything printed, then true
|
44
|
+
#
|
45
|
+
# Returns any formatted text that was not printed
|
46
|
+
#
|
47
|
+
def wrap(array) #:nodoc:
|
48
|
+
initialize_wrap(array)
|
49
|
+
|
50
|
+
stop = false
|
51
|
+
while !stop
|
52
|
+
# wrap before testing if enough height for this line because the
|
53
|
+
# height of the highest fragment on this line will be used to
|
54
|
+
# determine the line height
|
55
|
+
@line_wrap.wrap_line(:document => @document,
|
56
|
+
:kerning => @kerning,
|
57
|
+
:width => available_width,
|
58
|
+
:arranger => @arranger,
|
59
|
+
:disable_wrap_by_char => @disable_wrap_by_char)
|
60
|
+
|
61
|
+
if enough_height_for_this_line?
|
62
|
+
move_baseline_down
|
63
|
+
print_line
|
64
|
+
else
|
65
|
+
stop = true
|
66
|
+
end
|
67
|
+
|
68
|
+
stop ||= @single_line || @arranger.finished?
|
69
|
+
end
|
70
|
+
@text = @printed_lines.join("\n")
|
71
|
+
@everything_printed = @arranger.finished?
|
72
|
+
@arranger.unconsumed
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def print_line
|
78
|
+
@nothing_printed = false
|
79
|
+
printed_fragments = []
|
80
|
+
fragments_this_line = []
|
81
|
+
|
82
|
+
word_spacing = word_spacing_for_this_line
|
83
|
+
while fragment = @arranger.retrieve_fragment
|
84
|
+
fragment.word_spacing = word_spacing
|
85
|
+
if fragment.text == "\n"
|
86
|
+
printed_fragments << "\n" if @printed_lines.last == ""
|
87
|
+
break
|
88
|
+
end
|
89
|
+
printed_fragments << fragment.text
|
90
|
+
fragments_this_line << fragment
|
91
|
+
end
|
92
|
+
|
93
|
+
accumulated_width = 0
|
94
|
+
fragments_this_line.reverse! if @direction == :rtl
|
95
|
+
fragments_this_line.each do |fragment_this_line|
|
96
|
+
fragment_this_line.default_direction = @direction
|
97
|
+
format_and_draw_fragment(fragment_this_line, accumulated_width,
|
98
|
+
@line_wrap.width, word_spacing)
|
99
|
+
accumulated_width += fragment_this_line.width
|
100
|
+
end
|
101
|
+
|
102
|
+
@printed_lines << printed_fragments.map { |s| s.force_encoding(::Encoding::UTF_8) }.join
|
103
|
+
end
|
104
|
+
|
105
|
+
def word_spacing_for_this_line
|
106
|
+
if @align == :justify &&
|
107
|
+
@line_wrap.space_count > 0 &&
|
108
|
+
!@line_wrap.paragraph_finished?
|
109
|
+
(available_width - @line_wrap.width) / @line_wrap.space_count
|
110
|
+
else
|
111
|
+
0
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def enough_height_for_this_line?
|
116
|
+
@line_height = @arranger.max_line_height
|
117
|
+
@descender = @arranger.max_descender
|
118
|
+
@ascender = @arranger.max_ascender
|
119
|
+
if @baseline_y == 0
|
120
|
+
diff = @ascender + @descender
|
121
|
+
else
|
122
|
+
diff = @descender + @line_height + @leading
|
123
|
+
end
|
124
|
+
require_relatived_total_height = @baseline_y.abs + diff
|
125
|
+
if require_relatived_total_height > @height + 0.0001
|
126
|
+
# no room for the full height of this line
|
127
|
+
@arranger.repack_unretrieved
|
128
|
+
false
|
129
|
+
else
|
130
|
+
true
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def initialize_wrap(array)
|
135
|
+
@text = nil
|
136
|
+
@arranger.format_array = array
|
137
|
+
|
138
|
+
# these values will depend on the maximum value within a given line
|
139
|
+
@line_height = 0
|
140
|
+
@descender = 0
|
141
|
+
@ascender = 0
|
142
|
+
@baseline_y = 0
|
143
|
+
|
144
|
+
@printed_lines = []
|
145
|
+
@nothing_printed = true
|
146
|
+
@everything_printed = false
|
147
|
+
end
|
148
|
+
|
149
|
+
def format_and_draw_fragment(fragment, accumulated_width,
|
150
|
+
line_width, word_spacing)
|
151
|
+
@arranger.apply_color_and_font_settings(fragment) do
|
152
|
+
draw_fragment(fragment, accumulated_width,
|
153
|
+
line_width, word_spacing)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# utilities.rb : General-purpose utility classes which don't fit anywhere else
|
4
|
+
#
|
5
|
+
# Copyright August 2012, Alex Dowad. All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
|
+
|
9
|
+
require 'thread'
|
10
|
+
|
11
|
+
module Prawn
|
12
|
+
|
13
|
+
# Throughout the Prawn codebase, repeated calculations which can benefit from caching are made
|
14
|
+
# In some cases, caching and reusing results can not only save CPU cycles but also greatly
|
15
|
+
# reduce memory requirements
|
16
|
+
# But at the same time, we don't want to throw away thread safety
|
17
|
+
# We have two interchangeable thread-safe cache implementations:
|
18
|
+
|
19
|
+
# @private
|
20
|
+
class SynchronizedCache
|
21
|
+
# As an optimization, this could access the hash directly on VMs with a global interpreter lock (like MRI)
|
22
|
+
def initialize
|
23
|
+
@cache = {}
|
24
|
+
@mutex = Mutex.new
|
25
|
+
end
|
26
|
+
def [](key)
|
27
|
+
@mutex.synchronize { @cache[key] }
|
28
|
+
end
|
29
|
+
def []=(key,value)
|
30
|
+
@mutex.synchronize { @cache[key] = value }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# @private
|
35
|
+
class ThreadLocalCache
|
36
|
+
def initialize
|
37
|
+
@cache_id = "cache_#{self.object_id}".to_sym
|
38
|
+
end
|
39
|
+
def [](key)
|
40
|
+
(Thread.current[@cache_id] ||= {})[key]
|
41
|
+
end
|
42
|
+
def []=(key,value)
|
43
|
+
(Thread.current[@cache_id] ||= {})[key] = value
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/prawn/view.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# prawn/view.rb : Implements a mixin for Prawn's DSL
|
4
|
+
#
|
5
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
6
|
+
|
7
|
+
module Prawn
|
8
|
+
# This mixin allows you to create modular Prawn code without the
|
9
|
+
# need to create subclasses of Prawn::Document.
|
10
|
+
#
|
11
|
+
# class Greeter
|
12
|
+
# include Prawn::View
|
13
|
+
#
|
14
|
+
# def initialize(name)
|
15
|
+
# @name = name
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# def say_hello
|
19
|
+
# text "Hello, #{@name}!"
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# def say_goodbye
|
23
|
+
# font("Courier") do
|
24
|
+
# text "Goodbye, #{@name}!"
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# greeter = Greeter.new("Gregory")
|
30
|
+
#
|
31
|
+
# greeter.say_hello
|
32
|
+
# greeter.say_goodbye
|
33
|
+
#
|
34
|
+
# greeter.save_as("greetings.pdf")
|
35
|
+
#
|
36
|
+
# The short story about why you should use this mixin rather than
|
37
|
+
# creating subclasses of +Prawn::Document+ is that it helps
|
38
|
+
# prevent accidental conflicts between your code and Prawn's
|
39
|
+
# code.
|
40
|
+
#
|
41
|
+
# Here's the slightly longer story...
|
42
|
+
#
|
43
|
+
# By using composition rather than inheritance under the hood, this
|
44
|
+
# mixin allows you to keep your state separate from +Prawn::Document+'s
|
45
|
+
# state, and also will prevent unexpected method name collisions due
|
46
|
+
# to late binding effects.
|
47
|
+
#
|
48
|
+
# This mixin is mostly meant for extending Prawn's functionality
|
49
|
+
# with your own additions, but you can also use it to replace or
|
50
|
+
# wrap existing Prawn methods. Calling +super+ will still work
|
51
|
+
# as expected, and alternatively you can explictly call
|
52
|
+
# +document.some_method+ to delegate to Prawn where needed.
|
53
|
+
module View
|
54
|
+
# @group Experimental API
|
55
|
+
|
56
|
+
# Lazily instantiates a +Prawn::Document+ object.
|
57
|
+
#
|
58
|
+
# You can also redefine this method in your own classes to use
|
59
|
+
# a custom document class.
|
60
|
+
def document
|
61
|
+
@document ||= Prawn::Document.new
|
62
|
+
end
|
63
|
+
|
64
|
+
# Delegates all unhandled calls to object returned by +document+ method.
|
65
|
+
# (which is an instance of Prawn::Document by default)
|
66
|
+
def method_missing(m, *a, &b)
|
67
|
+
return super unless document.respond_to?(m)
|
68
|
+
|
69
|
+
document.send(m, *a, &b)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Syntactic sugar that uses +instance_eval+ under the hood to provide
|
73
|
+
# a block-based DSL.
|
74
|
+
#
|
75
|
+
# greeter.update do
|
76
|
+
# say_hello
|
77
|
+
# say_goodbye
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
def update(&b)
|
81
|
+
instance_eval(&b)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Syntatic sugar that calls +document.render_file+ under the hood.
|
85
|
+
#
|
86
|
+
# greeter.save_as("greetings.pdf")
|
87
|
+
def save_as(filename)
|
88
|
+
document.render_file(filename)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|