prawn-git 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (252) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +10 -0
  3. data/COPYING +2 -0
  4. data/GPLv2 +340 -0
  5. data/GPLv3 +674 -0
  6. data/Gemfile +11 -0
  7. data/LICENSE +56 -0
  8. data/Rakefile +55 -0
  9. data/data/fonts/Courier-Bold.afm +342 -0
  10. data/data/fonts/Courier-BoldOblique.afm +342 -0
  11. data/data/fonts/Courier-Oblique.afm +342 -0
  12. data/data/fonts/Courier.afm +342 -0
  13. data/data/fonts/Helvetica-Bold.afm +2827 -0
  14. data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  15. data/data/fonts/Helvetica-Oblique.afm +3051 -0
  16. data/data/fonts/Helvetica.afm +3051 -0
  17. data/data/fonts/MustRead.html +19 -0
  18. data/data/fonts/Symbol.afm +213 -0
  19. data/data/fonts/Times-Bold.afm +2588 -0
  20. data/data/fonts/Times-BoldItalic.afm +2384 -0
  21. data/data/fonts/Times-Italic.afm +2667 -0
  22. data/data/fonts/Times-Roman.afm +2419 -0
  23. data/data/fonts/ZapfDingbats.afm +225 -0
  24. data/data/images/16bit.alpha +0 -0
  25. data/data/images/16bit.color +0 -0
  26. data/data/images/16bit.png +0 -0
  27. data/data/images/arrow.png +0 -0
  28. data/data/images/arrow2.png +0 -0
  29. data/data/images/dice.alpha +0 -0
  30. data/data/images/dice.color +0 -0
  31. data/data/images/dice.png +0 -0
  32. data/data/images/dice_interlaced.png +0 -0
  33. data/data/images/fractal.jpg +0 -0
  34. data/data/images/indexed_color.dat +0 -0
  35. data/data/images/indexed_color.png +0 -0
  36. data/data/images/letterhead.jpg +0 -0
  37. data/data/images/license.md +8 -0
  38. data/data/images/page_white_text.alpha +0 -0
  39. data/data/images/page_white_text.color +0 -0
  40. data/data/images/page_white_text.png +0 -0
  41. data/data/images/pal_bk.png +0 -0
  42. data/data/images/pigs.jpg +0 -0
  43. data/data/images/prawn.png +0 -0
  44. data/data/images/ruport.png +0 -0
  45. data/data/images/ruport_data.dat +0 -0
  46. data/data/images/ruport_transparent.png +0 -0
  47. data/data/images/ruport_type0.png +0 -0
  48. data/data/images/stef.jpg +0 -0
  49. data/data/images/tru256.bmp +0 -0
  50. data/data/images/web-links.dat +1 -0
  51. data/data/images/web-links.png +0 -0
  52. data/data/pdfs/complex_template.pdf +0 -0
  53. data/data/pdfs/contains_ttf_font.pdf +0 -0
  54. data/data/pdfs/encrypted.pdf +0 -0
  55. data/data/pdfs/form.pdf +820 -0
  56. data/data/pdfs/hexagon.pdf +61 -0
  57. data/data/pdfs/indirect_reference.pdf +86 -0
  58. data/data/pdfs/multipage_template.pdf +127 -0
  59. data/data/pdfs/nested_pages.pdf +118 -0
  60. data/data/pdfs/page_without_mediabox.pdf +193 -0
  61. data/data/pdfs/resources_as_indirect_object.pdf +83 -0
  62. data/data/pdfs/two_hexagons.pdf +90 -0
  63. data/data/pdfs/version_1_6.pdf +61 -0
  64. data/data/shift_jis_text.txt +1 -0
  65. data/lib/prawn.rb +89 -0
  66. data/lib/prawn/document.rb +706 -0
  67. data/lib/prawn/document/bounding_box.rb +539 -0
  68. data/lib/prawn/document/column_box.rb +144 -0
  69. data/lib/prawn/document/internals.rb +58 -0
  70. data/lib/prawn/document/span.rb +57 -0
  71. data/lib/prawn/encoding.rb +87 -0
  72. data/lib/prawn/errors.rb +80 -0
  73. data/lib/prawn/font.rb +413 -0
  74. data/lib/prawn/font/afm.rb +256 -0
  75. data/lib/prawn/font/dfont.rb +43 -0
  76. data/lib/prawn/font/ttf.rb +355 -0
  77. data/lib/prawn/font_metric_cache.rb +46 -0
  78. data/lib/prawn/graphics.rb +646 -0
  79. data/lib/prawn/graphics/cap_style.rb +47 -0
  80. data/lib/prawn/graphics/color.rb +232 -0
  81. data/lib/prawn/graphics/dash.rb +109 -0
  82. data/lib/prawn/graphics/join_style.rb +49 -0
  83. data/lib/prawn/graphics/patterns.rb +126 -0
  84. data/lib/prawn/graphics/transformation.rb +157 -0
  85. data/lib/prawn/graphics/transparency.rb +101 -0
  86. data/lib/prawn/grid.rb +279 -0
  87. data/lib/prawn/image_handler.rb +44 -0
  88. data/lib/prawn/images.rb +199 -0
  89. data/lib/prawn/images/image.rb +49 -0
  90. data/lib/prawn/images/jpg.rb +91 -0
  91. data/lib/prawn/images/png.rb +290 -0
  92. data/lib/prawn/measurement_extensions.rb +50 -0
  93. data/lib/prawn/measurements.rb +77 -0
  94. data/lib/prawn/outline.rb +289 -0
  95. data/lib/prawn/repeater.rb +124 -0
  96. data/lib/prawn/security.rb +288 -0
  97. data/lib/prawn/security/arcfour.rb +54 -0
  98. data/lib/prawn/soft_mask.rb +94 -0
  99. data/lib/prawn/stamp.rb +136 -0
  100. data/lib/prawn/text.rb +437 -0
  101. data/lib/prawn/text/box.rb +141 -0
  102. data/lib/prawn/text/formatted.rb +7 -0
  103. data/lib/prawn/text/formatted/arranger.rb +290 -0
  104. data/lib/prawn/text/formatted/box.rb +614 -0
  105. data/lib/prawn/text/formatted/fragment.rb +264 -0
  106. data/lib/prawn/text/formatted/line_wrap.rb +277 -0
  107. data/lib/prawn/text/formatted/parser.rb +224 -0
  108. data/lib/prawn/text/formatted/wrap.rb +160 -0
  109. data/lib/prawn/utilities.rb +46 -0
  110. data/lib/prawn/version.rb +5 -0
  111. data/lib/prawn/view.rb +91 -0
  112. data/manual/absolute_position.pdf +0 -0
  113. data/manual/basic_concepts/adding_pages.rb +27 -0
  114. data/manual/basic_concepts/basic_concepts.rb +36 -0
  115. data/manual/basic_concepts/creation.rb +39 -0
  116. data/manual/basic_concepts/cursor.rb +33 -0
  117. data/manual/basic_concepts/measurement.rb +25 -0
  118. data/manual/basic_concepts/origin.rb +38 -0
  119. data/manual/basic_concepts/other_cursor_helpers.rb +40 -0
  120. data/manual/basic_concepts/view.rb +42 -0
  121. data/manual/bounding_box/bounding_box.rb +39 -0
  122. data/manual/bounding_box/bounds.rb +49 -0
  123. data/manual/bounding_box/canvas.rb +24 -0
  124. data/manual/bounding_box/creation.rb +23 -0
  125. data/manual/bounding_box/indentation.rb +46 -0
  126. data/manual/bounding_box/nesting.rb +45 -0
  127. data/manual/bounding_box/russian_boxes.rb +40 -0
  128. data/manual/bounding_box/stretchy.rb +31 -0
  129. data/manual/contents.rb +29 -0
  130. data/manual/cover.rb +39 -0
  131. data/manual/document_and_page_options/background.rb +27 -0
  132. data/manual/document_and_page_options/document_and_page_options.rb +32 -0
  133. data/manual/document_and_page_options/metadata.rb +23 -0
  134. data/manual/document_and_page_options/page_margins.rb +38 -0
  135. data/manual/document_and_page_options/page_size.rb +34 -0
  136. data/manual/document_and_page_options/print_scaling.rb +20 -0
  137. data/manual/example_helper.rb +7 -0
  138. data/manual/graphics/circle_and_ellipse.rb +22 -0
  139. data/manual/graphics/color.rb +24 -0
  140. data/manual/graphics/common_lines.rb +30 -0
  141. data/manual/graphics/fill_and_stroke.rb +42 -0
  142. data/manual/graphics/fill_rules.rb +37 -0
  143. data/manual/graphics/gradients.rb +37 -0
  144. data/manual/graphics/graphics.rb +58 -0
  145. data/manual/graphics/helper.rb +24 -0
  146. data/manual/graphics/line_width.rb +35 -0
  147. data/manual/graphics/lines_and_curves.rb +41 -0
  148. data/manual/graphics/polygon.rb +29 -0
  149. data/manual/graphics/rectangle.rb +21 -0
  150. data/manual/graphics/rotate.rb +28 -0
  151. data/manual/graphics/scale.rb +41 -0
  152. data/manual/graphics/soft_masks.rb +46 -0
  153. data/manual/graphics/stroke_cap.rb +31 -0
  154. data/manual/graphics/stroke_dash.rb +48 -0
  155. data/manual/graphics/stroke_join.rb +30 -0
  156. data/manual/graphics/translate.rb +29 -0
  157. data/manual/graphics/transparency.rb +35 -0
  158. data/manual/how_to_read_this_manual.rb +40 -0
  159. data/manual/images/absolute_position.rb +23 -0
  160. data/manual/images/fit.rb +21 -0
  161. data/manual/images/horizontal.rb +25 -0
  162. data/manual/images/images.rb +40 -0
  163. data/manual/images/plain_image.rb +18 -0
  164. data/manual/images/scale.rb +22 -0
  165. data/manual/images/vertical.rb +28 -0
  166. data/manual/images/width_and_height.rb +25 -0
  167. data/manual/layout/boxes.rb +27 -0
  168. data/manual/layout/content.rb +25 -0
  169. data/manual/layout/layout.rb +28 -0
  170. data/manual/layout/simple_grid.rb +23 -0
  171. data/manual/outline/add_subsection_to.rb +61 -0
  172. data/manual/outline/insert_section_after.rb +47 -0
  173. data/manual/outline/outline.rb +32 -0
  174. data/manual/outline/sections_and_pages.rb +67 -0
  175. data/manual/repeatable_content/alternate_page_numbering.rb +32 -0
  176. data/manual/repeatable_content/page_numbering.rb +54 -0
  177. data/manual/repeatable_content/repeatable_content.rb +32 -0
  178. data/manual/repeatable_content/repeater.rb +55 -0
  179. data/manual/repeatable_content/stamp.rb +41 -0
  180. data/manual/security/encryption.rb +31 -0
  181. data/manual/security/permissions.rb +38 -0
  182. data/manual/security/security.rb +28 -0
  183. data/manual/table.rb +16 -0
  184. data/manual/text/alignment.rb +44 -0
  185. data/manual/text/color.rb +24 -0
  186. data/manual/text/column_box.rb +32 -0
  187. data/manual/text/fallback_fonts.rb +37 -0
  188. data/manual/text/font.rb +41 -0
  189. data/manual/text/font_size.rb +45 -0
  190. data/manual/text/font_style.rb +23 -0
  191. data/manual/text/formatted_callbacks.rb +60 -0
  192. data/manual/text/formatted_text.rb +50 -0
  193. data/manual/text/free_flowing_text.rb +51 -0
  194. data/manual/text/inline.rb +41 -0
  195. data/manual/text/kerning_and_character_spacing.rb +39 -0
  196. data/manual/text/leading.rb +25 -0
  197. data/manual/text/line_wrapping.rb +41 -0
  198. data/manual/text/paragraph_indentation.rb +34 -0
  199. data/manual/text/positioned_text.rb +38 -0
  200. data/manual/text/registering_families.rb +48 -0
  201. data/manual/text/rendering_and_color.rb +37 -0
  202. data/manual/text/right_to_left_text.rb +47 -0
  203. data/manual/text/rotation.rb +43 -0
  204. data/manual/text/single_usage.rb +37 -0
  205. data/manual/text/text.rb +73 -0
  206. data/manual/text/text_box_excess.rb +32 -0
  207. data/manual/text/text_box_extensions.rb +45 -0
  208. data/manual/text/text_box_overflow.rb +48 -0
  209. data/manual/text/utf8.rb +28 -0
  210. data/manual/text/win_ansi_charset.rb +60 -0
  211. data/prawn.gemspec +45 -0
  212. data/spec/acceptance/png.rb +25 -0
  213. data/spec/annotations_spec.rb +74 -0
  214. data/spec/bounding_box_spec.rb +510 -0
  215. data/spec/column_box_spec.rb +65 -0
  216. data/spec/data/curves.pdf +66 -0
  217. data/spec/destinations_spec.rb +15 -0
  218. data/spec/document_spec.rb +748 -0
  219. data/spec/extensions/encoding_helpers.rb +11 -0
  220. data/spec/extensions/mocha.rb +46 -0
  221. data/spec/font_metric_cache_spec.rb +52 -0
  222. data/spec/font_spec.rb +474 -0
  223. data/spec/formatted_text_arranger_spec.rb +421 -0
  224. data/spec/formatted_text_box_spec.rb +705 -0
  225. data/spec/formatted_text_fragment_spec.rb +298 -0
  226. data/spec/graphics_spec.rb +683 -0
  227. data/spec/grid_spec.rb +96 -0
  228. data/spec/image_handler_spec.rb +54 -0
  229. data/spec/images_spec.rb +153 -0
  230. data/spec/inline_formatted_text_parser_spec.rb +564 -0
  231. data/spec/jpg_spec.rb +25 -0
  232. data/spec/line_wrap_spec.rb +367 -0
  233. data/spec/measurement_units_spec.rb +25 -0
  234. data/spec/outline_spec.rb +430 -0
  235. data/spec/png_spec.rb +245 -0
  236. data/spec/reference_spec.rb +25 -0
  237. data/spec/repeater_spec.rb +160 -0
  238. data/spec/security_spec.rb +158 -0
  239. data/spec/soft_mask_spec.rb +79 -0
  240. data/spec/span_spec.rb +44 -0
  241. data/spec/spec_helper.rb +54 -0
  242. data/spec/stamp_spec.rb +160 -0
  243. data/spec/stroke_styles_spec.rb +211 -0
  244. data/spec/text_at_spec.rb +143 -0
  245. data/spec/text_box_spec.rb +1043 -0
  246. data/spec/text_rendering_mode_spec.rb +45 -0
  247. data/spec/text_spacing_spec.rb +93 -0
  248. data/spec/text_spec.rb +557 -0
  249. data/spec/text_with_inline_formatting_spec.rb +35 -0
  250. data/spec/transparency_spec.rb +91 -0
  251. data/spec/view_spec.rb +43 -0
  252. 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("&", "&amp;").gsub(">", "&gt;").gsub("<", "&lt;")
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("&lt;", "<").gsub("&gt;", ">").gsub("&amp;", "&")
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
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ module Prawn
4
+ VERSION = "2.0.1"
5
+ end
@@ -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