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,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