prawn 0.11.1 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (353) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +10 -0
  3. data/COPYING +2 -2
  4. data/GPLv2 +340 -0
  5. data/GPLv3 +674 -0
  6. data/Gemfile +11 -0
  7. data/LICENSE +1 -1
  8. data/Rakefile +29 -38
  9. data/data/images/16bit.alpha +0 -0
  10. data/data/images/16bit.color +0 -0
  11. data/data/images/dice.alpha +0 -0
  12. data/data/images/dice.color +0 -0
  13. data/data/images/indexed_color.dat +0 -0
  14. data/data/images/indexed_color.png +0 -0
  15. data/data/images/page_white_text.alpha +0 -0
  16. data/data/images/page_white_text.color +0 -0
  17. data/data/pdfs/nested_pages.pdf +13 -13
  18. data/lib/prawn/document/bounding_box.rb +87 -12
  19. data/lib/prawn/document/column_box.rb +57 -28
  20. data/lib/prawn/document/graphics_state.rb +11 -74
  21. data/lib/prawn/document/internals.rb +25 -23
  22. data/lib/prawn/document/snapshot.rb +11 -8
  23. data/lib/prawn/document/span.rb +12 -10
  24. data/lib/prawn/document.rb +250 -194
  25. data/lib/prawn/encoding.rb +9 -10
  26. data/lib/prawn/errors.rb +18 -29
  27. data/lib/prawn/font/afm.rb +52 -41
  28. data/lib/prawn/font/dfont.rb +4 -3
  29. data/lib/prawn/font/ttf.rb +44 -48
  30. data/lib/prawn/font.rb +138 -88
  31. data/lib/prawn/font_metric_cache.rb +47 -0
  32. data/lib/prawn/graphics/cap_style.rb +4 -3
  33. data/lib/prawn/graphics/color.rb +13 -5
  34. data/lib/prawn/graphics/dash.rb +53 -31
  35. data/lib/prawn/graphics/join_style.rb +9 -7
  36. data/lib/prawn/graphics/patterns.rb +138 -0
  37. data/lib/prawn/graphics/transformation.rb +10 -9
  38. data/lib/prawn/graphics/transparency.rb +3 -1
  39. data/lib/prawn/graphics.rb +316 -61
  40. data/lib/prawn/image_handler.rb +36 -0
  41. data/lib/prawn/images/image.rb +49 -0
  42. data/lib/prawn/images/jpg.rb +21 -15
  43. data/lib/prawn/images/png.rb +62 -119
  44. data/lib/prawn/images.rb +89 -108
  45. data/lib/prawn/layout/grid.rb +66 -54
  46. data/lib/prawn/layout.rb +10 -15
  47. data/lib/prawn/measurement_extensions.rb +10 -6
  48. data/lib/prawn/measurements.rb +27 -21
  49. data/lib/prawn/outline.rb +6 -308
  50. data/lib/prawn/repeater.rb +11 -9
  51. data/lib/prawn/security/arcfour.rb +1 -0
  52. data/lib/prawn/security.rb +55 -33
  53. data/lib/prawn/soft_mask.rb +96 -0
  54. data/lib/prawn/stamp.rb +5 -3
  55. data/lib/prawn/table/cell/image.rb +69 -0
  56. data/lib/prawn/table/cell/in_table.rb +4 -2
  57. data/lib/prawn/table/cell/span_dummy.rb +93 -0
  58. data/lib/prawn/table/cell/subtable.rb +2 -2
  59. data/lib/prawn/table/cell/text.rb +44 -26
  60. data/lib/prawn/table/cell.rb +302 -50
  61. data/lib/prawn/table/cells.rb +147 -49
  62. data/lib/prawn/table/column_width_calculator.rb +61 -0
  63. data/lib/prawn/table.rb +297 -118
  64. data/lib/prawn/text/box.rb +21 -5
  65. data/lib/prawn/text/formatted/arranger.rb +290 -0
  66. data/lib/prawn/text/formatted/box.rb +103 -59
  67. data/lib/prawn/text/formatted/fragment.rb +34 -23
  68. data/lib/prawn/text/formatted/line_wrap.rb +266 -0
  69. data/lib/prawn/text/formatted/parser.rb +15 -5
  70. data/lib/prawn/text/formatted/wrap.rb +150 -0
  71. data/lib/prawn/text/formatted.rb +5 -4
  72. data/lib/prawn/text.rb +38 -24
  73. data/lib/prawn/utilities.rb +46 -0
  74. data/lib/prawn.rb +85 -20
  75. data/manual/basic_concepts/adding_pages.rb +27 -0
  76. data/manual/basic_concepts/basic_concepts.rb +34 -0
  77. data/manual/basic_concepts/creation.rb +39 -0
  78. data/manual/basic_concepts/cursor.rb +33 -0
  79. data/manual/basic_concepts/measurement.rb +25 -0
  80. data/manual/basic_concepts/origin.rb +38 -0
  81. data/manual/basic_concepts/other_cursor_helpers.rb +40 -0
  82. data/manual/bounding_box/bounding_box.rb +39 -0
  83. data/manual/bounding_box/bounds.rb +49 -0
  84. data/manual/bounding_box/canvas.rb +24 -0
  85. data/manual/bounding_box/creation.rb +23 -0
  86. data/manual/bounding_box/indentation.rb +46 -0
  87. data/manual/bounding_box/nesting.rb +45 -0
  88. data/manual/bounding_box/russian_boxes.rb +40 -0
  89. data/manual/bounding_box/stretchy.rb +31 -0
  90. data/manual/document_and_page_options/background.rb +27 -0
  91. data/manual/document_and_page_options/document_and_page_options.rb +32 -0
  92. data/manual/document_and_page_options/metadata.rb +23 -0
  93. data/manual/document_and_page_options/page_margins.rb +38 -0
  94. data/manual/document_and_page_options/page_size.rb +34 -0
  95. data/manual/document_and_page_options/print_scaling.rb +20 -0
  96. data/manual/example_file.rb +111 -0
  97. data/manual/example_helper.rb +411 -0
  98. data/manual/example_package.rb +53 -0
  99. data/manual/example_section.rb +46 -0
  100. data/manual/graphics/circle_and_ellipse.rb +22 -0
  101. data/manual/graphics/color.rb +24 -0
  102. data/manual/graphics/common_lines.rb +30 -0
  103. data/manual/graphics/fill_and_stroke.rb +42 -0
  104. data/manual/graphics/fill_rules.rb +37 -0
  105. data/manual/graphics/gradients.rb +37 -0
  106. data/manual/graphics/graphics.rb +58 -0
  107. data/manual/graphics/helper.rb +24 -0
  108. data/manual/graphics/line_width.rb +35 -0
  109. data/manual/graphics/lines_and_curves.rb +41 -0
  110. data/manual/graphics/polygon.rb +29 -0
  111. data/manual/graphics/rectangle.rb +21 -0
  112. data/manual/graphics/rotate.rb +28 -0
  113. data/manual/graphics/scale.rb +41 -0
  114. data/manual/graphics/soft_masks.rb +46 -0
  115. data/manual/graphics/stroke_cap.rb +31 -0
  116. data/manual/graphics/stroke_dash.rb +48 -0
  117. data/manual/graphics/stroke_join.rb +30 -0
  118. data/manual/graphics/translate.rb +29 -0
  119. data/manual/graphics/transparency.rb +35 -0
  120. data/manual/images/absolute_position.rb +23 -0
  121. data/manual/images/fit.rb +21 -0
  122. data/manual/images/horizontal.rb +25 -0
  123. data/manual/images/images.rb +40 -0
  124. data/manual/images/plain_image.rb +18 -0
  125. data/manual/images/scale.rb +22 -0
  126. data/manual/images/vertical.rb +28 -0
  127. data/manual/images/width_and_height.rb +25 -0
  128. data/manual/layout/boxes.rb +27 -0
  129. data/manual/layout/content.rb +25 -0
  130. data/manual/layout/layout.rb +28 -0
  131. data/manual/layout/simple_grid.rb +23 -0
  132. data/manual/manual/cover.rb +36 -0
  133. data/manual/manual/foreword.rb +85 -0
  134. data/manual/manual/how_to_read_this_manual.rb +41 -0
  135. data/manual/manual/manual.rb +34 -0
  136. data/manual/outline/add_subsection_to.rb +61 -0
  137. data/manual/outline/insert_section_after.rb +47 -0
  138. data/manual/outline/outline.rb +32 -0
  139. data/manual/outline/sections_and_pages.rb +67 -0
  140. data/manual/repeatable_content/page_numbering.rb +54 -0
  141. data/manual/repeatable_content/repeatable_content.rb +31 -0
  142. data/manual/repeatable_content/repeater.rb +55 -0
  143. data/manual/repeatable_content/stamp.rb +41 -0
  144. data/manual/security/encryption.rb +31 -0
  145. data/manual/security/permissions.rb +38 -0
  146. data/manual/security/security.rb +28 -0
  147. data/manual/syntax_highlight.rb +52 -0
  148. data/manual/table/basic_block.rb +53 -0
  149. data/manual/table/before_rendering_page.rb +26 -0
  150. data/manual/table/cell_border_lines.rb +24 -0
  151. data/manual/table/cell_borders_and_bg.rb +31 -0
  152. data/manual/table/cell_dimensions.rb +30 -0
  153. data/manual/table/cell_text.rb +38 -0
  154. data/manual/table/column_widths.rb +30 -0
  155. data/manual/table/content_and_subtables.rb +39 -0
  156. data/manual/table/creation.rb +27 -0
  157. data/manual/table/filtering.rb +36 -0
  158. data/manual/table/flow_and_header.rb +17 -0
  159. data/manual/table/image_cells.rb +33 -0
  160. data/manual/table/position.rb +29 -0
  161. data/manual/table/row_colors.rb +20 -0
  162. data/manual/table/span.rb +30 -0
  163. data/manual/table/style.rb +22 -0
  164. data/manual/table/table.rb +52 -0
  165. data/manual/table/width.rb +27 -0
  166. data/manual/text/alignment.rb +44 -0
  167. data/manual/text/color.rb +24 -0
  168. data/manual/text/column_box.rb +32 -0
  169. data/manual/text/fallback_fonts.rb +37 -0
  170. data/manual/text/font.rb +41 -0
  171. data/manual/text/font_size.rb +45 -0
  172. data/manual/text/font_style.rb +23 -0
  173. data/manual/text/formatted_callbacks.rb +60 -0
  174. data/manual/text/formatted_text.rb +54 -0
  175. data/manual/text/free_flowing_text.rb +51 -0
  176. data/manual/text/group.rb +31 -0
  177. data/manual/text/inline.rb +43 -0
  178. data/manual/text/kerning_and_character_spacing.rb +39 -0
  179. data/manual/text/leading.rb +25 -0
  180. data/manual/text/line_wrapping.rb +41 -0
  181. data/manual/text/paragraph_indentation.rb +26 -0
  182. data/manual/text/positioned_text.rb +38 -0
  183. data/manual/text/registering_families.rb +48 -0
  184. data/manual/text/rendering_and_color.rb +37 -0
  185. data/manual/text/right_to_left_text.rb +43 -0
  186. data/manual/text/rotation.rb +43 -0
  187. data/manual/text/single_usage.rb +37 -0
  188. data/manual/text/text.rb +75 -0
  189. data/manual/text/text_box_excess.rb +32 -0
  190. data/manual/text/text_box_extensions.rb +45 -0
  191. data/manual/text/text_box_overflow.rb +44 -0
  192. data/manual/text/utf8.rb +28 -0
  193. data/{examples/m17n → manual/text}/win_ansi_charset.rb +14 -10
  194. data/prawn.gemspec +27 -17
  195. data/spec/acceptance/png.rb +23 -0
  196. data/spec/annotations_spec.rb +16 -32
  197. data/spec/bounding_box_spec.rb +284 -2
  198. data/spec/cell_spec.rb +169 -38
  199. data/spec/column_box_spec.rb +65 -0
  200. data/spec/data/curves.pdf +66 -0
  201. data/spec/destinations_spec.rb +5 -5
  202. data/spec/document_spec.rb +212 -113
  203. data/spec/extensions/encoding_helpers.rb +9 -0
  204. data/spec/extensions/mocha.rb +2 -3
  205. data/spec/font_metric_cache_spec.rb +52 -0
  206. data/spec/font_spec.rb +205 -95
  207. data/spec/formatted_text_arranger_spec.rb +43 -43
  208. data/spec/formatted_text_box_spec.rb +63 -24
  209. data/spec/formatted_text_fragment_spec.rb +8 -8
  210. data/spec/graphics_spec.rb +175 -68
  211. data/spec/grid_spec.rb +26 -15
  212. data/spec/image_handler_spec.rb +54 -0
  213. data/spec/images_spec.rb +58 -30
  214. data/spec/inline_formatted_text_parser_spec.rb +73 -19
  215. data/spec/jpg_spec.rb +4 -4
  216. data/spec/line_wrap_spec.rb +28 -28
  217. data/spec/measurement_units_spec.rb +6 -6
  218. data/spec/object_store_spec.rb +17 -106
  219. data/spec/outline_spec.rb +103 -63
  220. data/spec/png_spec.rb +25 -25
  221. data/spec/reference_spec.rb +8 -65
  222. data/spec/repeater_spec.rb +25 -11
  223. data/spec/security_spec.rb +44 -12
  224. data/spec/snapshot_spec.rb +38 -6
  225. data/spec/soft_mask_spec.rb +117 -0
  226. data/spec/span_spec.rb +10 -15
  227. data/spec/spec_helper.rb +32 -8
  228. data/spec/stamp_spec.rb +29 -30
  229. data/spec/stroke_styles_spec.rb +36 -18
  230. data/spec/table/span_dummy_spec.rb +17 -0
  231. data/spec/table_spec.rb +850 -104
  232. data/spec/text_at_spec.rb +19 -33
  233. data/spec/text_box_spec.rb +117 -64
  234. data/spec/text_rendering_mode_spec.rb +5 -5
  235. data/spec/text_spacing_spec.rb +20 -2
  236. data/spec/text_spec.rb +111 -59
  237. data/spec/transparency_spec.rb +5 -5
  238. metadata +477 -328
  239. data/HACKING +0 -50
  240. data/README +0 -141
  241. data/data/fonts/Action Man.dfont +0 -0
  242. data/data/fonts/Activa.ttf +0 -0
  243. data/data/fonts/Chalkboard.ttf +0 -0
  244. data/data/fonts/DejaVuSans.ttf +0 -0
  245. data/data/fonts/Dustismo_Roman.ttf +0 -0
  246. data/data/fonts/comicsans.ttf +0 -0
  247. data/data/fonts/gkai00mp.ttf +0 -0
  248. data/data/images/16bit.dat +0 -0
  249. data/data/images/dice.dat +0 -0
  250. data/data/images/page_white_text.dat +0 -0
  251. data/data/images/rails.dat +0 -0
  252. data/data/images/rails.png +0 -0
  253. data/examples/bounding_box/bounding_boxes.rb +0 -44
  254. data/examples/bounding_box/indentation.rb +0 -35
  255. data/examples/bounding_box/russian_boxes.rb +0 -37
  256. data/examples/bounding_box/stretched_nesting.rb +0 -68
  257. data/examples/example_helper.rb +0 -8
  258. data/examples/general/background.rb +0 -24
  259. data/examples/general/canvas.rb +0 -16
  260. data/examples/general/context_sensitive_headers.rb +0 -38
  261. data/examples/general/float.rb +0 -12
  262. data/examples/general/margin.rb +0 -37
  263. data/examples/general/measurement_units.rb +0 -52
  264. data/examples/general/metadata-info.rb +0 -17
  265. data/examples/general/multi_page_layout.rb +0 -19
  266. data/examples/general/outlines.rb +0 -67
  267. data/examples/general/page_geometry.rb +0 -32
  268. data/examples/general/page_numbering.rb +0 -40
  269. data/examples/general/page_templates.rb +0 -20
  270. data/examples/general/repeaters.rb +0 -48
  271. data/examples/general/stamp.rb +0 -42
  272. data/examples/general/templates.rb +0 -14
  273. data/examples/graphics/basic_images.rb +0 -24
  274. data/examples/graphics/cmyk.rb +0 -13
  275. data/examples/graphics/curves.rb +0 -12
  276. data/examples/graphics/gradient.rb +0 -23
  277. data/examples/graphics/hexagon.rb +0 -14
  278. data/examples/graphics/image_fit.rb +0 -16
  279. data/examples/graphics/image_flow.rb +0 -38
  280. data/examples/graphics/image_position.rb +0 -18
  281. data/examples/graphics/line.rb +0 -33
  282. data/examples/graphics/png_types.rb +0 -23
  283. data/examples/graphics/polygons.rb +0 -17
  284. data/examples/graphics/remote_images.rb +0 -13
  285. data/examples/graphics/rounded_polygons.rb +0 -20
  286. data/examples/graphics/rounded_rectangle.rb +0 -21
  287. data/examples/graphics/ruport_style_helpers.rb +0 -20
  288. data/examples/graphics/stroke_bounds.rb +0 -21
  289. data/examples/graphics/stroke_cap_and_join.rb +0 -46
  290. data/examples/graphics/stroke_dash.rb +0 -43
  291. data/examples/graphics/transformations.rb +0 -53
  292. data/examples/graphics/transparency.rb +0 -27
  293. data/examples/grid/bounding_boxes.rb +0 -22
  294. data/examples/grid/column_gutter_grid.rb +0 -21
  295. data/examples/grid/multi_boxes.rb +0 -52
  296. data/examples/grid/show_grid.rb +0 -14
  297. data/examples/grid/simple_grid.rb +0 -21
  298. data/examples/m17n/chinese_text_wrapping.rb +0 -18
  299. data/examples/m17n/euro.rb +0 -16
  300. data/examples/m17n/full_win_ansi_character_list.rb +0 -20
  301. data/examples/m17n/sjis.rb +0 -29
  302. data/examples/m17n/utf8.rb +0 -14
  303. data/examples/security/hello_foo.rb +0 -9
  304. data/examples/table/bill.rb +0 -54
  305. data/examples/table/borders.rb +0 -25
  306. data/examples/table/cell.rb +0 -13
  307. data/examples/table/checkerboard.rb +0 -23
  308. data/examples/table/header.rb +0 -15
  309. data/examples/table/inline_format_table.rb +0 -13
  310. data/examples/table/multi_page_table.rb +0 -10
  311. data/examples/table/simple_table.rb +0 -25
  312. data/examples/table/subtable.rb +0 -13
  313. data/examples/table/widths.rb +0 -21
  314. data/examples/text/alignment.rb +0 -19
  315. data/examples/text/character_spacing.rb +0 -13
  316. data/examples/text/dfont.rb +0 -49
  317. data/examples/text/family_based_styling.rb +0 -25
  318. data/examples/text/font_calculations.rb +0 -92
  319. data/examples/text/font_size.rb +0 -34
  320. data/examples/text/hyphenation.rb +0 -45
  321. data/examples/text/indent_paragraphs.rb +0 -24
  322. data/examples/text/inline_format.rb +0 -104
  323. data/examples/text/kerning.rb +0 -31
  324. data/examples/text/rendering_mode.rb +0 -21
  325. data/examples/text/rotated.rb +0 -99
  326. data/examples/text/shaped_text_box.rb +0 -32
  327. data/examples/text/simple_text.rb +0 -18
  328. data/examples/text/simple_text_ttf.rb +0 -18
  329. data/examples/text/span.rb +0 -30
  330. data/examples/text/text_box.rb +0 -90
  331. data/examples/text/text_box_returning_excess.rb +0 -52
  332. data/examples/text/text_flow.rb +0 -68
  333. data/lib/prawn/compatibility.rb +0 -51
  334. data/lib/prawn/core/annotations.rb +0 -61
  335. data/lib/prawn/core/byte_string.rb +0 -9
  336. data/lib/prawn/core/destinations.rb +0 -90
  337. data/lib/prawn/core/document_state.rb +0 -78
  338. data/lib/prawn/core/literal_string.rb +0 -16
  339. data/lib/prawn/core/name_tree.rb +0 -165
  340. data/lib/prawn/core/object_store.rb +0 -264
  341. data/lib/prawn/core/page.rb +0 -213
  342. data/lib/prawn/core/pdf_object.rb +0 -108
  343. data/lib/prawn/core/reference.rb +0 -112
  344. data/lib/prawn/core/text/formatted/arranger.rb +0 -293
  345. data/lib/prawn/core/text/formatted/line_wrap.rb +0 -272
  346. data/lib/prawn/core/text/formatted/wrap.rb +0 -149
  347. data/lib/prawn/core/text.rb +0 -268
  348. data/lib/prawn/core.rb +0 -85
  349. data/lib/prawn/document/page_geometry.rb +0 -136
  350. data/lib/prawn/graphics/gradient.rb +0 -84
  351. data/spec/name_tree_spec.rb +0 -112
  352. data/spec/pdf_object_spec.rb +0 -170
  353. data/spec/template_spec.rb +0 -291
@@ -0,0 +1,290 @@
1
+ # encoding: utf-8
2
+
3
+ # core/text/formatted/arranger.rb : Implements a data structure for 2-stage
4
+ # processing of lines of 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
+ module Prawn
11
+ module Text
12
+ module Formatted #:nodoc:
13
+
14
+ # @private
15
+
16
+ class Arranger #:nodoc:
17
+ attr_reader :max_line_height
18
+ attr_reader :max_descender
19
+ attr_reader :max_ascender
20
+ attr_accessor :consumed
21
+
22
+ # The following present only for testing purposes
23
+ attr_reader :unconsumed
24
+ attr_reader :fragments
25
+ attr_reader :current_format_state
26
+
27
+ def initialize(document, options={})
28
+ @document = document
29
+ @fragments = []
30
+ @unconsumed = []
31
+ @kerning = options[:kerning]
32
+ end
33
+
34
+ def space_count
35
+ if @unfinalized_line
36
+ raise "Lines must be finalized before calling #space_count"
37
+ end
38
+ @fragments.inject(0) do |sum, fragment|
39
+ sum + fragment.space_count
40
+ end
41
+ end
42
+
43
+ def line_width
44
+ if @unfinalized_line
45
+ raise "Lines must be finalized before calling #line_width"
46
+ end
47
+ @fragments.inject(0) do |sum, fragment|
48
+ sum + fragment.width
49
+ end
50
+ end
51
+
52
+ def line
53
+ if @unfinalized_line
54
+ raise "Lines must be finalized before calling #line"
55
+ end
56
+ @fragments.collect do |fragment|
57
+ fragment.text.dup.force_encoding(::Encoding::UTF_8)
58
+ end.join
59
+ end
60
+
61
+ def finalize_line
62
+ @unfinalized_line = false
63
+ omit_trailing_whitespace_from_line_width
64
+ @fragments = []
65
+ @consumed.each do |hash|
66
+ text = hash[:text]
67
+ format_state = hash.dup
68
+ format_state.delete(:text)
69
+ fragment = Prawn::Text::Formatted::Fragment.new(text,
70
+ format_state,
71
+ @document)
72
+ @fragments << fragment
73
+ set_fragment_measurements(fragment)
74
+ set_line_measurement_maximums(fragment)
75
+ end
76
+ end
77
+
78
+ def format_array=(array)
79
+ initialize_line
80
+ @unconsumed = []
81
+ array.each do |hash|
82
+ hash[:text].scan(/[^\n]+|\n/) do |line|
83
+ @unconsumed << hash.merge(:text => line)
84
+ end
85
+ end
86
+ end
87
+
88
+ def initialize_line
89
+ @unfinalized_line = true
90
+ @max_line_height = 0
91
+ @max_descender = 0
92
+ @max_ascender = 0
93
+
94
+ @consumed = []
95
+ @fragments = []
96
+ end
97
+
98
+ def finished?
99
+ @unconsumed.length == 0
100
+ end
101
+
102
+ def next_string
103
+ unless @unfinalized_line
104
+ raise "Lines must not be finalized when calling #next_string"
105
+ end
106
+ hash = @unconsumed.shift
107
+ if hash.nil?
108
+ nil
109
+ else
110
+ @consumed << hash.dup
111
+ @current_format_state = hash.dup
112
+ @current_format_state.delete(:text)
113
+ hash[:text]
114
+ end
115
+ end
116
+
117
+ def preview_next_string
118
+ hash = @unconsumed.first
119
+ if hash.nil? then nil
120
+ else hash[:text]
121
+ end
122
+ end
123
+
124
+ def apply_color_and_font_settings(fragment, &block)
125
+ if fragment.color
126
+ original_fill_color = @document.fill_color
127
+ original_stroke_color = @document.stroke_color
128
+ @document.fill_color(*fragment.color)
129
+ @document.stroke_color(*fragment.color)
130
+ apply_font_settings(fragment, &block)
131
+ @document.stroke_color = original_stroke_color
132
+ @document.fill_color = original_fill_color
133
+ else
134
+ apply_font_settings(fragment, &block)
135
+ end
136
+ end
137
+
138
+ def apply_font_settings(fragment=nil, &block)
139
+ if fragment.nil?
140
+ font = current_format_state[:font]
141
+ size = current_format_state[:size]
142
+ character_spacing = current_format_state[:character_spacing] ||
143
+ @document.character_spacing
144
+ styles = current_format_state[:styles]
145
+ font_style = font_style(styles)
146
+ else
147
+ font = fragment.font
148
+ size = fragment.size
149
+ character_spacing = fragment.character_spacing
150
+ styles = fragment.styles
151
+ font_style = font_style(styles)
152
+ end
153
+
154
+ @document.character_spacing(character_spacing) do
155
+ if font || font_style != :normal
156
+ raise "Bad font family" unless @document.font.family
157
+ @document.font(font || @document.font.family, :style => font_style) do
158
+ apply_font_size(size, styles, &block)
159
+ end
160
+ else
161
+ apply_font_size(size, styles, &block)
162
+ end
163
+ end
164
+ end
165
+
166
+ def update_last_string(printed, unprinted, normalized_soft_hyphen=nil)
167
+ return if printed.nil?
168
+ if printed.empty?
169
+ @consumed.pop
170
+ else
171
+ @consumed.last[:text] = printed
172
+ if normalized_soft_hyphen
173
+ @consumed.last[:normalized_soft_hyphen] = normalized_soft_hyphen
174
+ end
175
+ end
176
+
177
+ unless unprinted.empty?
178
+ @unconsumed.unshift(@current_format_state.merge(:text => unprinted))
179
+ end
180
+
181
+ load_previous_format_state if printed.empty?
182
+ end
183
+
184
+ def retrieve_fragment
185
+ if @unfinalized_line
186
+ raise "Lines must be finalized before fragments can be retrieved"
187
+ end
188
+ @fragments.shift
189
+ end
190
+
191
+ def repack_unretrieved
192
+ new_unconsumed = []
193
+ while fragment = retrieve_fragment
194
+ fragment.include_trailing_white_space!
195
+ new_unconsumed << fragment.format_state.merge(:text => fragment.text)
196
+ end
197
+ @unconsumed = new_unconsumed.concat(@unconsumed)
198
+ end
199
+
200
+ def font_style(styles)
201
+ if styles.nil?
202
+ :normal
203
+ elsif styles.include?(:bold) && styles.include?(:italic)
204
+ :bold_italic
205
+ elsif styles.include?(:bold)
206
+ :bold
207
+ elsif styles.include?(:italic)
208
+ :italic
209
+ else
210
+ :normal
211
+ end
212
+ end
213
+
214
+ private
215
+
216
+ def load_previous_format_state
217
+ if @consumed.empty?
218
+ @current_format_state = {}
219
+ else
220
+ hash = @consumed.last
221
+ @current_format_state = hash.dup
222
+ @current_format_state.delete(:text)
223
+ end
224
+ end
225
+
226
+ def apply_font_size(size, styles)
227
+ if subscript?(styles) || superscript?(styles)
228
+ relative_size = 0.583
229
+ if size.nil?
230
+ size = @document.font_size * relative_size
231
+ else
232
+ size = size * relative_size
233
+ end
234
+ end
235
+ if size.nil?
236
+ yield
237
+ else
238
+ @document.font_size(size) { yield }
239
+ end
240
+ end
241
+
242
+ def subscript?(styles)
243
+ if styles.nil? then false
244
+ else styles.include?(:subscript)
245
+ end
246
+ end
247
+
248
+ def superscript?(styles)
249
+ if styles.nil? then false
250
+ else styles.include?(:superscript)
251
+ end
252
+ end
253
+
254
+ def omit_trailing_whitespace_from_line_width
255
+ @consumed.reverse_each do |hash|
256
+ if hash[:text] == "\n"
257
+ break
258
+ elsif hash[:text].strip.empty? && @consumed.length > 1
259
+ # this entire fragment is trailing white space
260
+ hash[:exclude_trailing_white_space] = true
261
+ else
262
+ # this fragment contains the first non-white space we have
263
+ # encountered since the end of the line
264
+ hash[:exclude_trailing_white_space] = true
265
+ break
266
+ end
267
+ end
268
+ end
269
+
270
+ def set_fragment_measurements(fragment)
271
+ apply_font_settings(fragment) do
272
+ fragment.width = @document.width_of(fragment.text,
273
+ :kerning => @kerning)
274
+ fragment.line_height = @document.font.height
275
+ fragment.descender = @document.font.descender
276
+ fragment.ascender = @document.font.ascender
277
+ end
278
+ end
279
+
280
+ def set_line_measurement_maximums(fragment)
281
+ @max_line_height = [defined?(@max_line_height) && @max_line_height, fragment.line_height].compact.max
282
+ @max_descender = [defined?(@max_descender) && @max_descender, fragment.descender].compact.max
283
+ @max_ascender = [defined?(@max_ascender) && @max_ascender, fragment.ascender].compact.max
284
+ end
285
+
286
+ end
287
+
288
+ end
289
+ end
290
+ end
@@ -10,7 +10,8 @@
10
10
  module Prawn
11
11
  module Text
12
12
  module Formatted
13
-
13
+ # @group Stable API
14
+
14
15
  # Draws the requested formatted text into a box. When the text overflows
15
16
  # the rectangle shrink to fit or truncate the text. Text boxes are
16
17
  # independent of the document y position.
@@ -45,10 +46,19 @@ module Prawn
45
46
  # the appropriate tags if you which to draw attention to the link
46
47
  # <tt>:anchor</tt>::
47
48
  # a destination that has already been or will be registered using
48
- # Prawn::Core::Destinations#add_dest. A clickable link will be
49
+ # PDF::Core::Destinations#add_dest. A clickable link will be
49
50
  # created to that destination. Note that you must explicitly underline
50
51
  # and color using the appropriate tags if you which to draw attention
51
52
  # to the link
53
+ # <tt>:local</tt>::
54
+ # a file or application to be opened locally. A clickable link will be
55
+ # created to the provided local file or application. If the file is
56
+ # another PDF, it will be opened in a new window. Note that you must
57
+ # explicitly underline and color using the appropriate tags if you which
58
+ # to draw attention to the link
59
+ # <tt>:draw_text_callback</tt>:
60
+ # if provided, this Proc will be called instead of #draw_text! once
61
+ # per fragment for every low-level addition of text to the page.
52
62
  # <tt>:callback</tt>::
53
63
  # an object (or array of such objects) with two methods:
54
64
  # #render_behind and #render_in_front, which are called immediately
@@ -89,20 +99,9 @@ module Prawn
89
99
  # vertical space was consumed by the printed text
90
100
  #
91
101
  class Box
92
- include Prawn::Core::Text::Formatted::Wrap
102
+ include Prawn::Text::Formatted::Wrap
93
103
 
94
- def valid_options
95
- Prawn::Core::Text::VALID_OPTIONS + [:at, :height, :width,
96
- :align, :valign,
97
- :rotate, :rotate_around,
98
- :overflow, :min_font_size,
99
- :leading, :character_spacing,
100
- :mode, :single_line,
101
- :skip_encoding,
102
- :document,
103
- :direction,
104
- :fallback_fonts]
105
- end
104
+ # @group Experimental API
106
105
 
107
106
  # The text that was successfully printed (or, if <tt>dry_run</tt> was
108
107
  # used, the text that would have been successfully printed)
@@ -135,42 +134,6 @@ module Prawn
135
134
  line_height - (ascender + descender)
136
135
  end
137
136
 
138
- #
139
- # Example (see Prawn::Text::Core::Formatted::Wrap for what is required
140
- # of the wrap method if you want to override the default wrapping
141
- # algorithm):
142
- #
143
- #
144
- # module MyWrap
145
- #
146
- # def wrap(array)
147
- # initialize_wrap([{ :text => 'all your base are belong to us' }])
148
- # @line_wrap.wrap_line(:document => @document,
149
- # :kerning => @kerning,
150
- # :width => 10000,
151
- # :arranger => @arranger)
152
- # fragment = @arranger.retrieve_fragment
153
- # format_and_draw_fragment(fragment, 0, @line_wrap.width, 0)
154
- # []
155
- # end
156
- #
157
- # end
158
- #
159
- # Prawn::Text::Formatted::Box.extensions << MyWrap
160
- #
161
- # box = Prawn::Text::Formatted::Box.new('hello world')
162
- # box.render('why can't I print anything other than' +
163
- # '"all your base are belong to us"?')
164
- #
165
- #
166
- def self.extensions
167
- @extensions ||= []
168
- end
169
-
170
- def self.inherited(base) #:nodoc:
171
- extensions.each { |e| base.extensions << e }
172
- end
173
-
174
137
  # See Prawn::Text#text_box for valid options
175
138
  #
176
139
  def initialize(formatted_text, options={})
@@ -205,6 +168,12 @@ module Prawn
205
168
  @rotate_around = options[:rotate_around] || :upper_left
206
169
  @single_line = options[:single_line]
207
170
  @skip_encoding = options[:skip_encoding] || @document.skip_encoding
171
+ @draw_text_callback = options[:draw_text_callback]
172
+
173
+ # if the text rendering mode is :unknown, force it back to :fill
174
+ if @mode == :unknown
175
+ @mode = :fill
176
+ end
208
177
 
209
178
  if @overflow == :expand
210
179
  # if set to expand, then we simply set the bottom
@@ -273,7 +242,7 @@ module Prawn
273
242
  end
274
243
 
275
244
  # The height actually used during the previous <tt>render</tt>
276
- #
245
+ #
277
246
  def height
278
247
  return 0 if @baseline_y.nil? || @descender.nil?
279
248
  (@baseline_y - @descender).abs
@@ -310,14 +279,71 @@ module Prawn
310
279
  draw_fragment_underlays(fragment)
311
280
 
312
281
  @document.word_spacing(word_spacing) {
313
- @document.draw_text!(fragment.text, :at => [x, y],
314
- :kerning => @kerning)
282
+ if @draw_text_callback
283
+ @draw_text_callback.call(fragment.text, :at => [x, y],
284
+ :kerning => @kerning)
285
+ else
286
+ @document.draw_text!(fragment.text, :at => [x, y],
287
+ :kerning => @kerning)
288
+ end
315
289
  }
316
290
 
317
291
  draw_fragment_overlays(fragment)
318
292
  end
319
293
  end
320
294
 
295
+ # @group Extension API
296
+
297
+ # Example (see Prawn::Text::Core::Formatted::Wrap for what is required
298
+ # of the wrap method if you want to override the default wrapping
299
+ # algorithm):
300
+ #
301
+ #
302
+ # module MyWrap
303
+ #
304
+ # def wrap(array)
305
+ # initialize_wrap([{ :text => 'all your base are belong to us' }])
306
+ # @line_wrap.wrap_line(:document => @document,
307
+ # :kerning => @kerning,
308
+ # :width => 10000,
309
+ # :arranger => @arranger)
310
+ # fragment = @arranger.retrieve_fragment
311
+ # format_and_draw_fragment(fragment, 0, @line_wrap.width, 0)
312
+ # []
313
+ # end
314
+ #
315
+ # end
316
+ #
317
+ # Prawn::Text::Formatted::Box.extensions << MyWrap
318
+ #
319
+ # box = Prawn::Text::Formatted::Box.new('hello world')
320
+ # box.render('why can't I print anything other than' +
321
+ # '"all your base are belong to us"?')
322
+ #
323
+ #
324
+ def self.extensions
325
+ @extensions ||= []
326
+ end
327
+
328
+ # @private
329
+ def self.inherited(base)
330
+ extensions.each { |e| base.extensions << e }
331
+ end
332
+
333
+ def valid_options
334
+ PDF::Core::Text::VALID_OPTIONS + [:at, :height, :width,
335
+ :align, :valign,
336
+ :rotate, :rotate_around,
337
+ :overflow, :min_font_size,
338
+ :leading, :character_spacing,
339
+ :mode, :single_line,
340
+ :skip_encoding,
341
+ :document,
342
+ :direction,
343
+ :fallback_fonts,
344
+ :draw_text_callback]
345
+ end
346
+
321
347
  private
322
348
 
323
349
  def original_text
@@ -371,8 +397,7 @@ module Prawn
371
397
  # all fonts
372
398
  fallback_fonts << fragment_font
373
399
 
374
- hash[:text].unpack("U*").each do |char_int|
375
- char = [char_int].pack("U")
400
+ hash[:text].each_char do |char|
376
401
  @document.font(fragment_font)
377
402
  font_glyph_pairs << [find_font_for_this_glyph(char,
378
403
  @document.font.family,
@@ -437,6 +462,13 @@ module Prawn
437
462
  end
438
463
 
439
464
  def process_vertical_alignment(text)
465
+ # The vertical alignment must only be done once per text box, but
466
+ # we need to wait until render() is called so that the fonts are set
467
+ # up properly for wrapping. So guard with a boolean to ensure this is
468
+ # only run once.
469
+ return if defined?(@vertical_alignment_processed) && @vertical_alignment_processed
470
+ @vertical_alignment_processed = true
471
+
440
472
  return if @vertical_align == :top
441
473
  wrap(text)
442
474
 
@@ -444,7 +476,7 @@ module Prawn
444
476
  when :center
445
477
  @at[1] = @at[1] - (@height - height) * 0.5
446
478
  when :bottom
447
- @at[1] = @at[1] - (@height - height)
479
+ @at[1] = @at[1] - (@height - height) + @descender
448
480
  end
449
481
  @height = height
450
482
  end
@@ -505,6 +537,7 @@ module Prawn
505
537
  draw_fragment_overlay_styles(fragment)
506
538
  draw_fragment_overlay_link(fragment)
507
539
  draw_fragment_overlay_anchor(fragment)
540
+ draw_fragment_overlay_local(fragment)
508
541
  fragment.callback_objects.each do |obj|
509
542
  obj.render_in_front(fragment) if obj.respond_to?(:render_in_front)
510
543
  end
@@ -517,7 +550,7 @@ module Prawn
517
550
  :Border => [0, 0, 0],
518
551
  :A => { :Type => :Action,
519
552
  :S => :URI,
520
- :URI => Prawn::Core::LiteralString.new(fragment.link) })
553
+ :URI => PDF::Core::LiteralString.new(fragment.link) })
521
554
  end
522
555
 
523
556
  def draw_fragment_overlay_anchor(fragment)
@@ -528,12 +561,23 @@ module Prawn
528
561
  :Dest => fragment.anchor)
529
562
  end
530
563
 
564
+ def draw_fragment_overlay_local(fragment)
565
+ return unless fragment.local
566
+ box = fragment.absolute_bounding_box
567
+ @document.link_annotation(box,
568
+ :Border => [0, 0, 0],
569
+ :A => { :Type => :Action,
570
+ :S => :Launch,
571
+ :F => PDF::Core::LiteralString.new(fragment.local),
572
+ :NewWindow => true })
573
+ end
574
+
531
575
  def draw_fragment_overlay_styles(fragment)
532
576
  underline = fragment.styles.include?(:underline)
533
577
  if underline
534
578
  @document.stroke_line(fragment.underline_points)
535
579
  end
536
-
580
+
537
581
  strikethrough = fragment.styles.include?(:strikethrough)
538
582
  if strikethrough
539
583
  @document.stroke_line(fragment.strikethrough_points)
@@ -10,39 +10,27 @@ module Prawn
10
10
  module Text
11
11
  module Formatted
12
12
 
13
+
13
14
  # Prawn::Text::Formatted::Fragment is a state store for a formatted text
14
15
  # fragment. It does not render anything.
15
16
  #
17
+ # @private
16
18
  class Fragment
17
19
 
18
- attr_reader :format_state
20
+ attr_reader :format_state, :text
19
21
  attr_writer :width
20
22
  attr_accessor :line_height, :descender, :ascender
21
23
  attr_accessor :word_spacing, :left, :baseline
22
24
 
23
25
  def initialize(text, format_state, document)
24
- @text = text
25
26
  @format_state = format_state
26
27
  @document = document
27
28
  @word_spacing = 0
28
- end
29
29
 
30
- def text
31
- string = strip_zero_width_spaces(@text)
32
- if exclude_trailing_white_space?
33
- string = string.rstrip
34
- string = process_soft_hyphens(string)
35
- end
36
- case direction
37
- when :rtl
38
- if ruby_18 { true }
39
- string.scan(/./mu).reverse.join
40
- else
41
- string.reverse
42
- end
43
- else
44
- string
45
- end
30
+ # keep the original value of "text", so we can reinitialize @text if formatting parameters
31
+ # like text direction are changed
32
+ @original_text = text
33
+ @text = process_text(@original_text)
46
34
  end
47
35
 
48
36
  def width
@@ -105,6 +93,10 @@ module Prawn
105
93
  @format_state[:anchor]
106
94
  end
107
95
 
96
+ def local
97
+ @format_state[:local]
98
+ end
99
+
108
100
  def color
109
101
  @format_state[:color]
110
102
  end
@@ -127,16 +119,19 @@ module Prawn
127
119
  end
128
120
 
129
121
  def default_direction=(direction)
130
- @format_state[:direction] = direction unless @format_state[:direction]
122
+ unless @format_state[:direction]
123
+ @format_state[:direction] = direction
124
+ @text = process_text(@original_text)
125
+ end
131
126
  end
132
127
 
133
128
  def include_trailing_white_space!
134
129
  @format_state.delete(:exclude_trailing_white_space)
130
+ @text = process_text(@original_text)
135
131
  end
136
132
 
137
133
  def space_count
138
- string = exclude_trailing_white_space? ? @text.rstrip : @text
139
- string.count(" ")
134
+ @text.count(" ")
140
135
  end
141
136
 
142
137
  def callback_objects
@@ -212,6 +207,19 @@ module Prawn
212
207
 
213
208
  private
214
209
 
210
+ def process_text(text)
211
+ string = strip_zero_width_spaces(text)
212
+ if exclude_trailing_white_space?
213
+ string = process_soft_hyphens(string.rstrip)
214
+ end
215
+ case direction
216
+ when :rtl
217
+ string.reverse
218
+ else
219
+ string
220
+ end
221
+ end
222
+
215
223
  def exclude_trailing_white_space?
216
224
  @format_state[:exclude_trailing_white_space]
217
225
  end
@@ -222,6 +230,9 @@ module Prawn
222
230
 
223
231
  def process_soft_hyphens(string)
224
232
  if string.length > 0 && normalized_soft_hyphen
233
+ if string.encoding != normalized_soft_hyphen.encoding
234
+ string.force_encoding(normalized_soft_hyphen.encoding)
235
+ end
225
236
  string[0..-2].gsub(normalized_soft_hyphen, "") + string[-1..-1]
226
237
  else
227
238
  string
@@ -229,7 +240,7 @@ module Prawn
229
240
  end
230
241
 
231
242
  def strip_zero_width_spaces(string)
232
- if !"".respond_to?(:encoding) || string.encoding.to_s == "UTF-8"
243
+ if string.encoding == ::Encoding::UTF_8
233
244
  string.gsub(Prawn::Text::ZWSP, "")
234
245
  else
235
246
  string