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.
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,83 @@
1
+ %PDF-1.3
2
+ %����
3
+ 1 0 obj
4
+ << /Creator (Prawn)
5
+ /Producer (Prawn)
6
+ >>
7
+ endobj
8
+ 2 0 obj
9
+ << /Type /Pages
10
+ /Count 1
11
+ /Kids [5 0 R]
12
+ >>
13
+ endobj
14
+ 3 0 obj
15
+ << /Type /Catalog
16
+ /Pages 2 0 R
17
+ >>
18
+ endobj
19
+ 4 0 obj
20
+ << /Length 275
21
+ >>
22
+ stream
23
+ 0.000 0.000 0.000 rg
24
+ 0.000 0.000 0.000 RG
25
+ q
26
+ 0.000 0.000 1.000 rg
27
+
28
+ BT
29
+ 36 733.024 Td
30
+ /F1.0 32 Tf
31
+ [<412073616d706c65205044462074686174206861732074686520706167650a>] TJ
32
+ ET
33
+
34
+
35
+ BT
36
+ 36 696.032 Td
37
+ /F1.0 32 Tf
38
+ [<7265736f757263657320617320616e20696e646972656374206f626a656374>] TJ
39
+ ET
40
+
41
+ Q
42
+
43
+ endstream
44
+ endobj
45
+ 5 0 obj
46
+ << /Resources 6 0 R
47
+ /Type /Page
48
+ /Contents 4 0 R
49
+ /MediaBox [0 0 612.0 792.0]
50
+ /Parent 2 0 R
51
+ >>
52
+ endobj
53
+ 6 0 obj
54
+ << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
55
+ /Font << /F1.0 7 0 R
56
+ >>
57
+ >>
58
+ endobj
59
+ 7 0 obj
60
+ << /Subtype /Type1
61
+ /Type /Font
62
+ /Encoding /WinAnsiEncoding
63
+ /BaseFont /Helvetica
64
+ >>
65
+ endobj
66
+ xref
67
+ 0 8
68
+ 0000000000 65535 f
69
+ 0000000015 00000 n
70
+ 0000000071 00000 n
71
+ 0000000128 00000 n
72
+ 0000000177 00000 n
73
+ 0000000503 00000 n
74
+ 0000000611 00000 n
75
+ 0000000702 00000 n
76
+ trailer
77
+ << /Size 8
78
+ /Info 1 0 R
79
+ /Root 3 0 R
80
+ >>
81
+ startxref
82
+ 799
83
+ %%EOF
@@ -0,0 +1,90 @@
1
+ %PDF-1.3
2
+ %����
3
+ 1 0 obj
4
+ << /Creator (Prawn)
5
+ /Producer (Prawn)
6
+ >>
7
+ endobj
8
+ 2 0 obj
9
+ << /Count 2
10
+ /Kids [5 0 R 7 0 R]
11
+ /Type /Pages
12
+ >>
13
+ endobj
14
+ 3 0 obj
15
+ << /Type /Catalog
16
+ /Pages 2 0 R
17
+ >>
18
+ endobj
19
+ 4 0 obj
20
+ << /Length 195
21
+ >>
22
+ stream
23
+ 0.000 0.000 0.000 rg
24
+ 0.000 0.000 0.000 RG
25
+ q
26
+ 1.000 0.000 0.000 rg
27
+ 136.000 286.000 m
28
+ 236.000 336.000 l
29
+ 336.000 286.000 l
30
+ 336.000 186.000 l
31
+ 236.000 136.000 l
32
+ 136.000 186.000 l
33
+ 136.000 286.000 l
34
+ f
35
+ Q
36
+
37
+ endstream
38
+ endobj
39
+ 5 0 obj
40
+ << /Contents 4 0 R
41
+ /Parent 2 0 R
42
+ /MediaBox [0 0 612.0 792.0]
43
+ /Type /Page
44
+ >>
45
+ endobj
46
+ 6 0 obj
47
+ << /Length 195
48
+ >>
49
+ stream
50
+ 1.000 0.000 0.000 rg
51
+ 0.000 0.000 0.000 RG
52
+ q
53
+ 1.000 0.000 0.000 rg
54
+ 136.000 286.000 m
55
+ 236.000 336.000 l
56
+ 336.000 286.000 l
57
+ 336.000 186.000 l
58
+ 236.000 136.000 l
59
+ 136.000 186.000 l
60
+ 136.000 286.000 l
61
+ f
62
+ Q
63
+
64
+ endstream
65
+ endobj
66
+ 7 0 obj
67
+ << /Contents 6 0 R
68
+ /Parent 2 0 R
69
+ /MediaBox [0 0 612.0 792.0]
70
+ /Type /Page
71
+ >>
72
+ endobj
73
+ xref
74
+ 0 8
75
+ 0000000000 65535 f
76
+ 0000000015 00000 n
77
+ 0000000071 00000 n
78
+ 0000000134 00000 n
79
+ 0000000183 00000 n
80
+ 0000000429 00000 n
81
+ 0000000520 00000 n
82
+ 0000000766 00000 n
83
+ trailer
84
+ << /Info 1 0 R
85
+ /Root 3 0 R
86
+ /Size 8
87
+ >>
88
+ startxref
89
+ 857
90
+ %%EOF
@@ -0,0 +1,61 @@
1
+ %PDF-1.6
2
+ %????
3
+ 1 0 obj
4
+ << /Creator (Prawn)
5
+ /Producer (Prawn)
6
+ >>
7
+ endobj
8
+ 2 0 obj
9
+ << /Kids [5 0 R]
10
+ /Type /Pages
11
+ /Count 1
12
+ >>
13
+ endobj
14
+ 3 0 obj
15
+ << /Type /Catalog
16
+ /Pages 2 0 R
17
+ >>
18
+ endobj
19
+ 4 0 obj
20
+ << /Length 195
21
+ >>
22
+ stream
23
+ 0.000 0.000 0.000 rg
24
+ 0.000 0.000 0.000 RG
25
+ q
26
+ 1.000 0.000 0.000 rg
27
+ 136.000 286.000 m
28
+ 236.000 336.000 l
29
+ 336.000 286.000 l
30
+ 336.000 186.000 l
31
+ 236.000 136.000 l
32
+ 136.000 186.000 l
33
+ 136.000 286.000 l
34
+ f
35
+ Q
36
+
37
+ endstream
38
+ endobj
39
+ 5 0 obj
40
+ << /Contents 4 0 R
41
+ /Type /Page
42
+ /MediaBox [0 0 612.0 792.0]
43
+ /Parent 2 0 R
44
+ >>
45
+ endobj
46
+ xref
47
+ 0 6
48
+ 0000000000 65535 f
49
+ 0000000015 00000 n
50
+ 0000000071 00000 n
51
+ 0000000128 00000 n
52
+ 0000000177 00000 n
53
+ 0000000423 00000 n
54
+ trailer
55
+ << /Info 1 0 R
56
+ /Size 6
57
+ /Root 3 0 R
58
+ >>
59
+ startxref
60
+ 514
61
+ %%EOF
@@ -0,0 +1 @@
1
+ ���C���y�[�W
@@ -0,0 +1,89 @@
1
+ # encoding: utf-8
2
+
3
+ # Welcome to Prawn, the best PDF Generation library ever.
4
+ # This documentation covers user level functionality.
5
+ #
6
+ require "set"
7
+
8
+ require 'ttfunk'
9
+ require "pdf/core"
10
+
11
+ module Prawn
12
+ extend self
13
+
14
+ file = __FILE__
15
+ file = File.readlink(file) if File.symlink?(file)
16
+ dir = File.dirname(file)
17
+
18
+ # The base source directory for Prawn as installed on the system
19
+ #
20
+ #
21
+ BASEDIR = File.expand_path(File.join(dir, '..'))
22
+ DATADIR = File.expand_path(File.join(dir, '..', 'data'))
23
+
24
+ FLOAT_PRECISION = 1.0e-9
25
+
26
+ # Whe set to true, Prawn will verify hash options to ensure only valid keys
27
+ # are used. Off by default.
28
+ #
29
+ # Example:
30
+ # >> Prawn::Document.new(:tomato => "Juicy")
31
+ # Prawn::Errors::UnknownOption:
32
+ # Detected unknown option(s): [:tomato]
33
+ # Accepted options are: [:page_size, :page_layout, :left_margin, ...]
34
+ #
35
+ attr_accessor :debug # @private
36
+
37
+ def verify_options(accepted, actual) # @private
38
+ return unless debug || $DEBUG
39
+ unless (act=Set[*actual.keys]).subset?(acc=Set[*accepted])
40
+ raise Prawn::Errors::UnknownOption,
41
+ "\nDetected unknown option(s): #{(act - acc).to_a.inspect}\n" <<
42
+ "Accepted options are: #{accepted.inspect}"
43
+ end
44
+ yield if block_given?
45
+ end
46
+
47
+ module Configurable # @private
48
+ def configuration(*args)
49
+ @config ||= Marshal.load(Marshal.dump(default_configuration))
50
+ if Hash === args[0]
51
+ @config.update(args[0])
52
+ elsif args.length > 1
53
+ @config.values_at(*args)
54
+ elsif args.length == 1
55
+ @config[args[0]]
56
+ else
57
+ @config
58
+ end
59
+ end
60
+
61
+ alias_method :C, :configuration
62
+ end
63
+ end
64
+
65
+ require_relative "prawn/version"
66
+
67
+ require_relative "prawn/errors"
68
+
69
+ require_relative "prawn/utilities"
70
+ require_relative "prawn/text"
71
+ require_relative "prawn/graphics"
72
+ require_relative "prawn/images"
73
+ require_relative "prawn/images/image"
74
+ require_relative "prawn/images/jpg"
75
+ require_relative "prawn/images/png"
76
+ require_relative "prawn/stamp"
77
+ require_relative "prawn/soft_mask"
78
+ require_relative "prawn/security"
79
+ require_relative "prawn/document"
80
+ require_relative "prawn/font"
81
+ require_relative "prawn/measurements"
82
+ require_relative "prawn/repeater"
83
+ require_relative "prawn/outline"
84
+ require_relative "prawn/grid"
85
+ require_relative "prawn/view"
86
+ require_relative "prawn/image_handler"
87
+
88
+ Prawn.image_handler.register(Prawn::Images::PNG)
89
+ Prawn.image_handler.register(Prawn::Images::JPG)
@@ -0,0 +1,706 @@
1
+ # encoding: utf-8
2
+
3
+ # document.rb : Implements PDF document generation for Prawn
4
+ #
5
+ # Copyright April 2008, Gregory Brown. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+ require "stringio"
10
+
11
+ require_relative "document/bounding_box"
12
+ require_relative "document/column_box"
13
+ require_relative "document/internals"
14
+ require_relative "document/span"
15
+
16
+ module Prawn
17
+
18
+ # The Prawn::Document class is how you start creating a PDF document.
19
+ #
20
+ # There are three basic ways you can instantiate PDF Documents in Prawn, they
21
+ # are through assignment, implicit block or explicit block. Below is an exmple
22
+ # of each type, each example does exactly the same thing, makes a PDF document
23
+ # with all the defaults and puts in the default font "Hello There" and then
24
+ # saves it to the current directory as "example.pdf"
25
+ #
26
+ # For example, assignment can be like this:
27
+ #
28
+ # pdf = Prawn::Document.new
29
+ # pdf.text "Hello There"
30
+ # pdf.render_file "example.pdf"
31
+ #
32
+ # Or you can do an implied block form:
33
+ #
34
+ # Prawn::Document.generate "example.pdf" do
35
+ # text "Hello There"
36
+ # end
37
+ #
38
+ # Or if you need to access a variable outside the scope of the block, the
39
+ # explicit block form:
40
+ #
41
+ # words = "Hello There"
42
+ # Prawn::Document.generate "example.pdf" do |pdf|
43
+ # pdf.text words
44
+ # end
45
+ #
46
+ # Usually, the block forms are used when you are simply creating a PDF document
47
+ # that you want to immediately save or render out.
48
+ #
49
+ # See the new and generate methods for further details on the above.
50
+ #
51
+ class Document
52
+ include Prawn::Document::Internals
53
+ include PDF::Core::Annotations
54
+ include PDF::Core::Destinations
55
+ include Prawn::Document::Security
56
+ include Prawn::Text
57
+ include Prawn::Graphics
58
+ include Prawn::Images
59
+ include Prawn::Stamp
60
+ include Prawn::SoftMask
61
+
62
+ # @group Extension API
63
+
64
+ # NOTE: We probably need to rethink the options validation system, but this
65
+ # constant temporarily allows for extensions to modify the list.
66
+
67
+ VALID_OPTIONS = [:page_size, :page_layout, :margin, :left_margin,
68
+ :right_margin, :top_margin, :bottom_margin, :skip_page_creation,
69
+ :compress, :background, :info,
70
+ :text_formatter, :print_scaling]
71
+
72
+ # Any module added to this array will be included into instances of
73
+ # Prawn::Document at the per-object level. These will also be inherited by
74
+ # any subclasses.
75
+ #
76
+ # Example:
77
+ #
78
+ # module MyFancyModule
79
+ #
80
+ # def party!
81
+ # text "It's a big party!"
82
+ # end
83
+ #
84
+ # end
85
+ #
86
+ # Prawn::Document.extensions << MyFancyModule
87
+ #
88
+ # Prawn::Document.generate("foo.pdf") do
89
+ # party!
90
+ # end
91
+ #
92
+ #
93
+ def self.extensions
94
+ @extensions ||= []
95
+ end
96
+
97
+ # @private
98
+ def self.inherited(base)
99
+ extensions.each { |e| base.extensions << e }
100
+ end
101
+
102
+ # @group Stable Attributes
103
+
104
+ attr_accessor :margin_box
105
+ attr_reader :margins, :y
106
+ attr_accessor :page_number
107
+
108
+ # @group Extension Attributes
109
+
110
+ attr_accessor :text_formatter
111
+
112
+ # @group Stable API
113
+
114
+ # Creates and renders a PDF document.
115
+ #
116
+ # When using the implicit block form, Prawn will evaluate the block
117
+ # within an instance of Prawn::Document, simplifying your syntax.
118
+ # However, please note that you will not be able to reference variables
119
+ # from the enclosing scope within this block.
120
+ #
121
+ # # Using implicit block form and rendering to a file
122
+ # Prawn::Document.generate "example.pdf" do
123
+ # # self here is set to the newly instantiated Prawn::Document
124
+ # # and so any variables in the outside scope are unavailable
125
+ # font "Times-Roman"
126
+ # draw_text "Hello World", :at => [200,720], :size => 32
127
+ # end
128
+ #
129
+ # If you need to access your local and instance variables, use the explicit
130
+ # block form shown below. In this case, Prawn yields an instance of
131
+ # PDF::Document and the block is an ordinary closure:
132
+ #
133
+ # # Using explicit block form and rendering to a file
134
+ # content = "Hello World"
135
+ # Prawn::Document.generate "example.pdf" do |pdf|
136
+ # # self here is left alone
137
+ # pdf.font "Times-Roman"
138
+ # pdf.draw_text content, :at => [200,720], :size => 32
139
+ # end
140
+ #
141
+ def self.generate(filename,options={},&block)
142
+ pdf = new(options,&block)
143
+ pdf.render_file(filename)
144
+ end
145
+
146
+ # Creates a new PDF Document. The following options are available (with
147
+ # the default values marked in [])
148
+ #
149
+ # <tt>:page_size</tt>:: One of the PDF::Core::PageGeometry sizes [LETTER]
150
+ # <tt>:page_layout</tt>:: Either <tt>:portrait</tt> or <tt>:landscape</tt>
151
+ # <tt>:margin</tt>:: Sets the margin on all sides in points [0.5 inch]
152
+ # <tt>:left_margin</tt>:: Sets the left margin in points [0.5 inch]
153
+ # <tt>:right_margin</tt>:: Sets the right margin in points [0.5 inch]
154
+ # <tt>:top_margin</tt>:: Sets the top margin in points [0.5 inch]
155
+ # <tt>:bottom_margin</tt>:: Sets the bottom margin in points [0.5 inch]
156
+ # <tt>:skip_page_creation</tt>:: Creates a document without starting the first page [false]
157
+ # <tt>:compress</tt>:: Compresses content streams before rendering them [false]
158
+ # <tt>:background</tt>:: An image path to be used as background on all pages [nil]
159
+ # <tt>:background_scale</tt>:: Backgound image scale [1] [nil]
160
+ # <tt>:info</tt>:: Generic hash allowing for custom metadata properties [nil]
161
+ # <tt>:text_formatter</tt>: The text formatter to use for <tt>:inline_format</tt>ted text [Prawn::Text::Formatted::Parser]
162
+ #
163
+ # Setting e.g. the :margin to 100 points and the :left_margin to 50 will result in margins
164
+ # of 100 points on every side except for the left, where it will be 50.
165
+ #
166
+ # The :margin can also be an array much like CSS shorthand:
167
+ #
168
+ # # Top and bottom are 20, left and right are 100.
169
+ # :margin => [20, 100]
170
+ # # Top is 50, left and right are 100, bottom is 20.
171
+ # :margin => [50, 100, 20]
172
+ # # Top is 10, right is 20, bottom is 30, left is 40.
173
+ # :margin => [10, 20, 30, 40]
174
+ #
175
+ # Additionally, :page_size can be specified as a simple two value array giving
176
+ # the width and height of the document you need in PDF Points.
177
+ #
178
+ # Usage:
179
+ #
180
+ # # New document, US Letter paper, portrait orientation
181
+ # pdf = Prawn::Document.new
182
+ #
183
+ # # New document, A4 paper, landscaped
184
+ # pdf = Prawn::Document.new(:page_size => "A4", :page_layout => :landscape)
185
+ #
186
+ # # New document, Custom size
187
+ # pdf = Prawn::Document.new(:page_size => [200, 300])
188
+ #
189
+ # # New document, with background
190
+ # pdf = Prawn::Document.new(:background => "#{Prawn::DATADIR}/images/pigs.jpg")
191
+ #
192
+ def initialize(options={},&block)
193
+ options = options.dup
194
+
195
+ Prawn.verify_options VALID_OPTIONS, options
196
+
197
+ # need to fix, as the refactoring breaks this
198
+ # raise NotImplementedError if options[:skip_page_creation]
199
+
200
+ self.class.extensions.reverse_each { |e| extend e }
201
+ @internal_state = PDF::Core::DocumentState.new(options)
202
+ @internal_state.populate_pages_from_store(self)
203
+ renderer.min_version(state.store.min_version) if state.store.min_version
204
+
205
+ renderer.min_version(1.6) if options[:print_scaling] == :none
206
+
207
+ @background = options[:background]
208
+ @background_scale = options[:background_scale] || 1
209
+ @font_size = 12
210
+
211
+ @bounding_box = nil
212
+ @margin_box = nil
213
+
214
+ @page_number = 0
215
+
216
+ @text_formatter = options.delete(:text_formatter) || Text::Formatted::Parser
217
+
218
+ options[:size] = options.delete(:page_size)
219
+ options[:layout] = options.delete(:page_layout)
220
+
221
+ initialize_first_page(options)
222
+
223
+ @bounding_box = @margin_box
224
+
225
+ if block
226
+ block.arity < 1 ? instance_eval(&block) : block[self]
227
+ end
228
+ end
229
+
230
+ # @group Stable API
231
+
232
+ # Creates and advances to a new page in the document.
233
+ #
234
+ # Page size, margins, and layout can also be set when generating a
235
+ # new page. These values will become the new defaults for page creation
236
+ #
237
+ # pdf.start_new_page #=> Starts new page keeping current values
238
+ # pdf.start_new_page(:size => "LEGAL", :layout => :landscape)
239
+ # pdf.start_new_page(:left_margin => 50, :right_margin => 50)
240
+ # pdf.start_new_page(:margin => 100)
241
+ #
242
+ def start_new_page(options = {})
243
+ if last_page = state.page
244
+ last_page_size = last_page.size
245
+ last_page_layout = last_page.layout
246
+ last_page_margins = last_page.margins.dup
247
+ end
248
+
249
+ page_options = {:size => options[:size] || last_page_size,
250
+ :layout => options[:layout] || last_page_layout,
251
+ :margins => last_page_margins}
252
+ if last_page
253
+ new_graphic_state = last_page.graphic_state.dup if last_page.graphic_state
254
+ #erase the color space so that it gets reset on new page for fussy pdf-readers
255
+ new_graphic_state.color_space = {} if new_graphic_state
256
+ page_options.merge!(:graphic_state => new_graphic_state)
257
+ end
258
+
259
+ state.page = PDF::Core::Page.new(self, page_options)
260
+
261
+ apply_margin_options(options)
262
+ generate_margin_box
263
+
264
+ # Reset the bounding box if the new page has different size or layout
265
+ if last_page && (last_page.size != state.page.size ||
266
+ last_page.layout != state.page.layout)
267
+ @bounding_box = @margin_box
268
+ end
269
+
270
+ use_graphic_settings
271
+
272
+ unless options[:orphan]
273
+ state.insert_page(state.page, @page_number)
274
+ @page_number += 1
275
+
276
+ canvas { image(@background, :scale => @background_scale, :at => bounds.top_left) } if @background
277
+ @y = @bounding_box.absolute_top
278
+
279
+ float do
280
+ state.on_page_create_action(self)
281
+ end
282
+ end
283
+ end
284
+
285
+ # Returns the number of pages in the document
286
+ #
287
+ # pdf = Prawn::Document.new
288
+ # pdf.page_count #=> 1
289
+ # 3.times { pdf.start_new_page }
290
+ # pdf.page_count #=> 4
291
+ #
292
+ def page_count
293
+ state.page_count
294
+ end
295
+
296
+ # Re-opens the page with the given (1-based) page number so that you can
297
+ # draw on it.
298
+ #
299
+ # See Prawn::Document#number_pages for a sample usage of this capability.
300
+ #
301
+ def go_to_page(k)
302
+ @page_number = k
303
+ state.page = state.pages[k-1]
304
+ generate_margin_box
305
+ @y = @bounding_box.absolute_top
306
+ end
307
+
308
+ def y=(new_y)
309
+ @y = new_y
310
+ bounds.update_height
311
+ end
312
+
313
+ # The current y drawing position relative to the innermost bounding box,
314
+ # or to the page margins at the top level.
315
+ #
316
+ def cursor
317
+ y - bounds.absolute_bottom
318
+ end
319
+
320
+ # Moves to the specified y position in relative terms to the bottom margin.
321
+ #
322
+ def move_cursor_to(new_y)
323
+ self.y = new_y + bounds.absolute_bottom
324
+ end
325
+
326
+ # Executes a block and then restores the original y position. If new pages
327
+ # were created during this block, it will teleport back to the original
328
+ # page when done.
329
+ #
330
+ # pdf.text "A"
331
+ #
332
+ # pdf.float do
333
+ # pdf.move_down 100
334
+ # pdf.text "C"
335
+ # end
336
+ #
337
+ # pdf.text "B"
338
+ #
339
+ def float
340
+ original_page = page_number
341
+ original_y = y
342
+ yield
343
+ go_to_page(original_page) unless page_number == original_page
344
+ self.y = original_y
345
+ end
346
+
347
+ # Renders the PDF document to string.
348
+ # Pass an open file descriptor to render to file.
349
+ #
350
+ def render(*a, &b)
351
+ (1..page_count).each do |i|
352
+ go_to_page i
353
+ repeaters.each { |r| r.run(i) }
354
+ end
355
+
356
+ renderer.render(*a, &b)
357
+ end
358
+
359
+ # Renders the PDF document to file.
360
+ #
361
+ # pdf.render_file "foo.pdf"
362
+ #
363
+ def render_file(filename)
364
+ File.open(filename, "wb") { |f| render(f) }
365
+ end
366
+
367
+ # The bounds method returns the current bounding box you are currently in,
368
+ # which is by default the box represented by the margin box on the
369
+ # document itself. When called from within a created <tt>bounding_box</tt>
370
+ # block, the box defined by that call will be returned instead of the
371
+ # document margin box.
372
+ #
373
+ # Another important point about bounding boxes is that all x and y measurements
374
+ # within a bounding box code block are relative to the bottom left corner of the
375
+ # bounding box.
376
+ #
377
+ # For example:
378
+ #
379
+ # Prawn::Document.new do
380
+ # # In the default "margin box" of a Prawn document of 0.5in along each edge
381
+ #
382
+ # # Draw a border around the page (the manual way)
383
+ # stroke do
384
+ # line(bounds.bottom_left, bounds.bottom_right)
385
+ # line(bounds.bottom_right, bounds.top_right)
386
+ # line(bounds.top_right, bounds.top_left)
387
+ # line(bounds.top_left, bounds.bottom_left)
388
+ # end
389
+ #
390
+ # # Draw a border around the page (the easy way)
391
+ # stroke_bounds
392
+ # end
393
+ #
394
+ def bounds
395
+ @bounding_box
396
+ end
397
+
398
+ # Returns the innermost non-stretchy bounding box.
399
+ #
400
+ # @private
401
+ def reference_bounds
402
+ @bounding_box.reference_bounds
403
+ end
404
+
405
+ # Sets Document#bounds to the BoundingBox provided. See above for a brief
406
+ # description of what a bounding box is. This function is useful if you
407
+ # really need to change the bounding box manually, but usually, just entering
408
+ # and exiting bounding box code blocks is good enough.
409
+ #
410
+ def bounds=(bounding_box)
411
+ @bounding_box = bounding_box
412
+ end
413
+
414
+ # Moves up the document by n points relative to the current position inside
415
+ # the current bounding box.
416
+ #
417
+ def move_up(n)
418
+ self.y += n
419
+ end
420
+
421
+ # Moves down the document by n points relative to the current position inside
422
+ # the current bounding box.
423
+ #
424
+ def move_down(n)
425
+ self.y -= n
426
+ end
427
+
428
+ # Moves down the document and then executes a block.
429
+ #
430
+ # pdf.text "some text"
431
+ # pdf.pad_top(100) do
432
+ # pdf.text "This is 100 points below the previous line of text"
433
+ # end
434
+ # pdf.text "This text appears right below the previous line of text"
435
+ #
436
+ def pad_top(y)
437
+ move_down(y)
438
+ yield
439
+ end
440
+
441
+ # Executes a block then moves down the document
442
+ #
443
+ # pdf.text "some text"
444
+ # pdf.pad_bottom(100) do
445
+ # pdf.text "This text appears right below the previous line of text"
446
+ # end
447
+ # pdf.text "This is 100 points below the previous line of text"
448
+ #
449
+ def pad_bottom(y)
450
+ yield
451
+ move_down(y)
452
+ end
453
+
454
+ # Moves down the document by y, executes a block, then moves down the
455
+ # document by y again.
456
+ #
457
+ # pdf.text "some text"
458
+ # pdf.pad(100) do
459
+ # pdf.text "This is 100 points below the previous line of text"
460
+ # end
461
+ # pdf.text "This is 100 points below the previous line of text"
462
+ #
463
+ def pad(y)
464
+ move_down(y)
465
+ yield
466
+ move_down(y)
467
+ end
468
+
469
+
470
+ # Indents the specified number of PDF points for the duration of the block
471
+ #
472
+ # pdf.text "some text"
473
+ # pdf.indent(20) do
474
+ # pdf.text "This is indented 20 points"
475
+ # end
476
+ # pdf.text "This starts 20 points left of the above line " +
477
+ # "and is flush with the first line"
478
+ # pdf.indent 20, 20 do
479
+ # pdf.text "This line is indented on both sides."
480
+ # end
481
+ #
482
+ def indent(left, right = 0, &block)
483
+ bounds.indent(left, right, &block)
484
+ end
485
+
486
+ # Places a text box on specified pages for page numbering. This should be called
487
+ # towards the end of document creation, after all your content is already in
488
+ # place. In your template string, <page> refers to the current page, and
489
+ # <total> refers to the total amount of pages in the document. Page numbering should
490
+ # occur at the end of your Prawn::Document.generate block because the method iterates
491
+ # through existing pages after they are created.
492
+ #
493
+ # Parameters are:
494
+ #
495
+ # <tt>string</tt>:: Template string for page number wording.
496
+ # Should include '<page>' and, optionally, '<total>'.
497
+ # <tt>options</tt>:: A hash for page numbering and text box options.
498
+ # <tt>:page_filter</tt>:: A filter to specify which pages to place page numbers on.
499
+ # Refer to the method 'page_match?'
500
+ # <tt>:start_count_at</tt>:: The starting count to increment pages from.
501
+ # <tt>:total_pages</tt>:: If provided, will replace <total> with the value given.
502
+ # Useful to override the total number of pages when using
503
+ # the start_count_at option.
504
+ # <tt>:color</tt>:: Text fill color.
505
+ #
506
+ # Please refer to Prawn::Text::text_box for additional options concerning text
507
+ # formatting and placement.
508
+ #
509
+ # Example: Print page numbers on every page except for the first. Start counting from
510
+ # five.
511
+ #
512
+ # Prawn::Document.generate("page_with_numbering.pdf") do
513
+ # number_pages "<page> in a total of <total>",
514
+ # {:start_count_at => 5,
515
+ # :page_filter => lambda{ |pg| pg != 1 },
516
+ # :at => [bounds.right - 50, 0],
517
+ # :align => :right,
518
+ # :size => 14}
519
+ # end
520
+ #
521
+ def number_pages(string, options={})
522
+ opts = options.dup
523
+ start_count_at = opts.delete(:start_count_at).to_i
524
+ page_filter = if opts.has_key?(:page_filter)
525
+ opts.delete(:page_filter)
526
+ else
527
+ :all
528
+ end
529
+ total_pages = opts.delete(:total_pages)
530
+ txtcolor = opts.delete(:color)
531
+ # An explicit height so that we can draw page numbers in the margins
532
+ opts[:height] = 50 unless opts.has_key?(:height)
533
+
534
+ start_count = false
535
+ pseudopage = 0
536
+ (1..page_count).each do |p|
537
+ unless start_count
538
+ pseudopage = case start_count_at
539
+ when 0
540
+ 1
541
+ else
542
+ start_count_at.to_i
543
+ end
544
+ end
545
+ if page_match?(page_filter, p)
546
+ go_to_page(p)
547
+ # have to use fill_color here otherwise text reverts back to default fill color
548
+ fill_color txtcolor unless txtcolor.nil?
549
+ total_pages = total_pages.nil? ? page_count : total_pages
550
+ str = string.gsub("<page>","#{pseudopage}").gsub("<total>","#{total_pages}")
551
+ text_box str, opts
552
+ start_count = true # increment page count as soon as first match found
553
+ end
554
+ pseudopage += 1 if start_count
555
+ end
556
+ end
557
+
558
+ # @group Experimental API
559
+
560
+ # Attempts to group the given block vertically within the current context.
561
+ # First attempts to render it in the current position on the current page.
562
+ # If that attempt overflows, it is tried anew after starting a new context
563
+ # (page or column). Returns a logically true value if the content fits in
564
+ # one page/column, false if a new page or column was needed.
565
+ #
566
+ # Raises CannotGroup if the provided content is too large to fit alone in
567
+ # the current page or column.
568
+ #
569
+ # @private
570
+ def group(*a, &b)
571
+ raise NotImplementedError,
572
+ "Document#group has been disabled because its implementation "+
573
+ "lead to corrupted documents whenever a page boundary was "+
574
+ "crossed. We will try to work on reimplementing it in a "+
575
+ "future release"
576
+ end
577
+
578
+ # @private
579
+ def transaction
580
+ raise NotImplementedError,
581
+ "Document#transaction has been disabled because its implementation "+
582
+ "lead to corrupted documents whenever a page boundary was "+
583
+ "crossed. We will try to work on reimplementing it in a "+
584
+ "future release"
585
+ end
586
+
587
+ # Provides a way to execute a block of code repeatedly based on a
588
+ # page_filter.
589
+ #
590
+ # Available page filters are:
591
+ # :all repeats on every page
592
+ # :odd repeats on odd pages
593
+ # :even repeats on even pages
594
+ # some_array repeats on every page listed in the array
595
+ # some_range repeats on every page included in the range
596
+ # some_lambda yields page number and repeats for true return values
597
+ def page_match?(page_filter, page_number)
598
+ case page_filter
599
+ when :all
600
+ true
601
+ when :odd
602
+ page_number % 2 == 1
603
+ when :even
604
+ page_number % 2 == 0
605
+ when Range, Array
606
+ page_filter.include?(page_number)
607
+ when Proc
608
+ page_filter.call(page_number)
609
+ end
610
+ end
611
+
612
+ # @private
613
+
614
+ def mask(*fields)
615
+ # Stores the current state of the named attributes, executes the block, and
616
+ # then restores the original values after the block has executed.
617
+ # -- I will remove the nodoc if/when this feature is a little less hacky
618
+ stored = {}
619
+ fields.each { |f| stored[f] = send(f) }
620
+ yield
621
+ fields.each { |f| send("#{f}=", stored[f]) }
622
+ end
623
+
624
+ # @group Extension API
625
+
626
+ def initialize_first_page(options)
627
+ if options[:skip_page_creation]
628
+ start_new_page(options.merge(:orphan => true))
629
+ else
630
+ start_new_page(options)
631
+ end
632
+ end
633
+
634
+ ## Internals. Don't depend on them!
635
+
636
+ # @private
637
+ def state
638
+ @internal_state
639
+ end
640
+
641
+ # @private
642
+ def page
643
+ state.page
644
+ end
645
+
646
+ private
647
+
648
+
649
+ # setting override_settings to true ensures that a new graphic state does not end up using
650
+ # previous settings.
651
+ def use_graphic_settings(override_settings = false)
652
+ set_fill_color if current_fill_color != "000000" || override_settings
653
+ set_stroke_color if current_stroke_color != "000000" || override_settings
654
+ write_line_width if line_width != 1 || override_settings
655
+ write_stroke_cap_style if cap_style != :butt || override_settings
656
+ write_stroke_join_style if join_style != :miter || override_settings
657
+ write_stroke_dash if dashed? || override_settings
658
+ end
659
+
660
+ def generate_margin_box
661
+ old_margin_box = @margin_box
662
+ page = state.page
663
+
664
+ @margin_box = BoundingBox.new(
665
+ self,
666
+ nil, # margin box has no parent
667
+ [ page.margins[:left], page.dimensions[-1] - page.margins[:top] ] ,
668
+ :width => page.dimensions[-2] - (page.margins[:left] + page.margins[:right]),
669
+ :height => page.dimensions[-1] - (page.margins[:top] + page.margins[:bottom])
670
+ )
671
+
672
+ # This check maintains indentation settings across page breaks
673
+ if old_margin_box
674
+ @margin_box.add_left_padding(old_margin_box.total_left_padding)
675
+ @margin_box.add_right_padding(old_margin_box.total_right_padding)
676
+ end
677
+
678
+ # we must update bounding box if not flowing from the previous page
679
+ #
680
+ @bounding_box = @margin_box unless @bounding_box && @bounding_box.parent
681
+ end
682
+
683
+ def apply_margin_options(options)
684
+ if options[:margin]
685
+ # Treat :margin as CSS shorthand with 1-4 values.
686
+ margin = Array(options[:margin])
687
+ positions = { 4 => [0,1,2,3], 3 => [0,1,2,1],
688
+ 2 => [0,1,0,1], 1 => [0,0,0,0] }[margin.length]
689
+
690
+ [:top, :right, :bottom, :left].zip(positions).each do |p,i|
691
+ options[:"#{p}_margin"] ||= margin[i]
692
+ end
693
+ end
694
+
695
+ [:left,:right,:top,:bottom].each do |side|
696
+ if margin = options[:"#{side}_margin"]
697
+ state.page.margins[side] = margin
698
+ end
699
+ end
700
+ end
701
+
702
+ def font_metric_cache #:nodoc:
703
+ @font_metric_cache ||= FontMetricCache.new( self )
704
+ end
705
+ end
706
+ end