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,65 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
4
+
5
+ describe "A column box" do
6
+ it "has sensible left and right values" do
7
+ create_pdf
8
+ @pdf.column_box [0, @pdf.cursor], :width => @pdf.bounds.width,
9
+ :height => 200, :columns => 3, :spacer => 25 do
10
+ left = @pdf.bounds.left
11
+ right = @pdf.bounds.right
12
+
13
+ @pdf.bounds.move_past_bottom # next column
14
+
15
+ @pdf.bounds.left.should be > left
16
+ @pdf.bounds.left.should be > right
17
+ @pdf.bounds.right.should be > @pdf.bounds.left
18
+ end
19
+ end
20
+
21
+ it "includes spacers between columns but not at the end" do
22
+ create_pdf
23
+ @pdf.column_box [0, @pdf.cursor], :width => 500,
24
+ :height => 200, :columns => 3, :spacer => 25 do
25
+ @pdf.bounds.width.should == 150 # (500 - (25 * 2)) / 3
26
+
27
+ @pdf.bounds.move_past_bottom
28
+ @pdf.bounds.move_past_bottom
29
+
30
+ @pdf.bounds.right.should == 500
31
+ end
32
+ end
33
+
34
+ it "does not reset the top margin on a new page by default" do
35
+ create_pdf
36
+ page_top = @pdf.cursor
37
+ @pdf.move_down 50
38
+ init_column_top = @pdf.cursor
39
+ @pdf.column_box [0, @pdf.cursor], :width => 500,
40
+ :height => 200, :columns => 2 do
41
+
42
+ @pdf.bounds.move_past_bottom
43
+ @pdf.bounds.move_past_bottom
44
+
45
+ @pdf.bounds.absolute_top.should == init_column_top
46
+ @pdf.bounds.absolute_top.should_not == page_top
47
+ end
48
+ end
49
+
50
+ it "does reset the top margin when reflow_margins is set" do
51
+ create_pdf
52
+ page_top = @pdf.cursor
53
+ @pdf.move_down 50
54
+ init_column_top = @pdf.cursor
55
+ @pdf.column_box [0, @pdf.cursor], :width => 500, :reflow_margins => true,
56
+ :height => 200, :columns => 2 do
57
+
58
+ @pdf.bounds.move_past_bottom
59
+ @pdf.bounds.move_past_bottom
60
+
61
+ @pdf.bounds.absolute_top.should == page_top
62
+ @pdf.bounds.absolute_top.should_not == init_column_top
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,66 @@
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 380
21
+ >>
22
+ stream
23
+ /DeviceRGB cs
24
+ 0.000 0.000 0.000 scn
25
+ /DeviceRGB CS
26
+ 0.000 0.000 0.000 SCN
27
+ q
28
+ 136.000 136.000 m
29
+ 96.000 126.000 96.000 126.000 86.000 86.000 c
30
+ S
31
+ 246.000 236.000 m
32
+ 246.000 241.523 241.523 246.000 236.000 246.000 c
33
+ 230.477 246.000 226.000 241.523 226.000 236.000 c
34
+ 226.000 230.477 230.477 226.000 236.000 226.000 c
35
+ 241.523 226.000 246.000 230.477 246.000 236.000 c
36
+ 236.000 236.000 m
37
+ f
38
+ Q
39
+
40
+ endstream
41
+ endobj
42
+ 5 0 obj
43
+ << /Type /Page
44
+ /Parent 2 0 R
45
+ /MediaBox [0 0 612.0 792.0]
46
+ /Contents 4 0 R
47
+ /Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
48
+ >>
49
+ >>
50
+ endobj
51
+ xref
52
+ 0 6
53
+ 0000000000 65535 f
54
+ 0000000015 00000 n
55
+ 0000000071 00000 n
56
+ 0000000128 00000 n
57
+ 0000000177 00000 n
58
+ 0000000608 00000 n
59
+ trailer
60
+ << /Size 6
61
+ /Root 3 0 R
62
+ /Info 1 0 R
63
+ >>
64
+ startxref
65
+ 762
66
+ %%EOF
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
4
+
5
+ describe "When creating destinations" do
6
+
7
+ before(:each) { create_pdf }
8
+
9
+ it "should add entry to Dests name tree" do
10
+ @pdf.dests.data.empty?.should == true
11
+ @pdf.add_dest "candy", "chocolate"
12
+ @pdf.dests.data.size.should == 1
13
+ end
14
+
15
+ end
@@ -0,0 +1,748 @@
1
+ # encoding: utf-8
2
+ require "tempfile"
3
+
4
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
5
+
6
+ describe "Prawn::Document.new" do
7
+ it "should not modify its argument" do
8
+ options = {:page_layout => :landscape}
9
+ Prawn::Document.new(options)
10
+ options.should == {:page_layout => :landscape}
11
+ end
12
+ end
13
+
14
+ describe "The cursor" do
15
+ it "should == pdf.y - bounds.absolute_bottom" do
16
+ pdf = Prawn::Document.new
17
+ pdf.cursor.should == pdf.bounds.top
18
+
19
+ pdf.y = 300
20
+ pdf.cursor.should == pdf.y - pdf.bounds.absolute_bottom
21
+ end
22
+
23
+ it "should be able to move relative to the bottom margin" do
24
+ pdf = Prawn::Document.new
25
+ pdf.move_cursor_to(10)
26
+
27
+ pdf.cursor.should == 10
28
+ pdf.y.should == pdf.cursor + pdf.bounds.absolute_bottom
29
+ end
30
+ end
31
+
32
+ describe "when generating a document with a custom text formatter" do
33
+ it "should use the provided text formatter" do
34
+ class TestTextFormatter
35
+ def self.format(string)
36
+ [
37
+ {
38
+ text: string.gsub("Dr. Who?", "Just 'The Doctor'."),
39
+ styles: [],
40
+ color: nil,
41
+ link: nil,
42
+ anchor: nil,
43
+ local: nil,
44
+ font: nil,
45
+ size: nil,
46
+ character_spacing: nil
47
+ }
48
+ ]
49
+ end
50
+ end
51
+ pdf = Prawn::Document.new text_formatter: TestTextFormatter
52
+ pdf.text "Dr. Who?", inline_format: true
53
+ text = PDF::Inspector::Text.analyze(pdf.render)
54
+ text.strings.first.should == "Just 'The Doctor'."
55
+ end
56
+ end
57
+
58
+ describe "when generating a document from a subclass" do
59
+ it "should be an instance of the subclass" do
60
+ custom_document = Class.new(Prawn::Document)
61
+ custom_document.generate(Tempfile.new("generate_test").path) do |e|
62
+ e.class.should == custom_document
63
+ e.should be_a_kind_of(Prawn::Document)
64
+ end
65
+ end
66
+
67
+ it "should retain any extensions found on Prawn::Document" do
68
+ mod1 = Module.new { attr_reader :test_extensions1 }
69
+ mod2 = Module.new { attr_reader :test_extensions2 }
70
+
71
+ Prawn::Document.extensions << mod1 << mod2
72
+
73
+ custom_document = Class.new(Prawn::Document)
74
+ custom_document.extensions.should == [mod1, mod2]
75
+
76
+ # remove the extensions we added to prawn document
77
+ Prawn::Document.extensions.delete(mod1)
78
+ Prawn::Document.extensions.delete(mod2)
79
+
80
+ Prawn::Document.new.respond_to?(:test_extensions1).should be_false
81
+ Prawn::Document.new.respond_to?(:test_extensions2).should be_false
82
+
83
+ # verify these still exist on custom class
84
+ custom_document.extensions.should == [mod1, mod2]
85
+
86
+ custom_document.new.respond_to?(:test_extensions1).should be_true
87
+ custom_document.new.respond_to?(:test_extensions2).should be_true
88
+ end
89
+
90
+ end
91
+
92
+
93
+ describe "When creating multi-page documents" do
94
+
95
+ before(:each) { create_pdf }
96
+
97
+ it "should initialize with a single page" do
98
+ page_counter = PDF::Inspector::Page.analyze(@pdf.render)
99
+
100
+ page_counter.pages.size.should == 1
101
+ @pdf.page_count.should == 1
102
+ end
103
+
104
+ it "should provide an accurate page_count" do
105
+ 3.times { @pdf.start_new_page }
106
+ page_counter = PDF::Inspector::Page.analyze(@pdf.render)
107
+
108
+ page_counter.pages.size.should == 4
109
+ @pdf.page_count.should == 4
110
+ end
111
+
112
+ end
113
+
114
+ describe "When beginning each new page" do
115
+ describe "Background image feature" do
116
+ before(:each) do
117
+ @filename = "#{Prawn::DATADIR}/images/pigs.jpg"
118
+ @pdf = Prawn::Document.new(:background => @filename)
119
+ end
120
+ it "should place a background image if it is in options block" do
121
+ output = @pdf.render
122
+ images = PDF::Inspector::XObject.analyze(output)
123
+ # there should be 2 images in the page resources
124
+ images.page_xobjects.first.size.should == 1
125
+ end
126
+ it "should place a background image if it is in options block" do
127
+ @pdf.instance_variable_defined?(:@background).should == true
128
+ @pdf.instance_variable_get(:@background).should == @filename
129
+ end
130
+ end
131
+ end
132
+
133
+ describe "Prawn::Document#float" do
134
+ it "should restore the original y-position" do
135
+ create_pdf
136
+ orig_y = @pdf.y
137
+ @pdf.float { @pdf.text "Foo" }
138
+ @pdf.y.should == orig_y
139
+ end
140
+
141
+ it "should teleport across pages if necessary" do
142
+ create_pdf
143
+
144
+ @pdf.float do
145
+ @pdf.text "Foo"
146
+ @pdf.start_new_page
147
+ @pdf.text "Bar"
148
+ end
149
+ @pdf.text "Baz"
150
+
151
+ pages = PDF::Inspector::Page.analyze(@pdf.render).pages
152
+ pages.size.should == 2
153
+ pages[0][:strings].should == ["Foo", "Baz"]
154
+ pages[1][:strings].should == ["Bar"]
155
+ end
156
+ end
157
+
158
+ describe "The page_number method" do
159
+ it "should be 1 for a new document" do
160
+ pdf = Prawn::Document.new
161
+ pdf.page_number.should == 1
162
+ end
163
+
164
+ it "should be 0 for documents with no pages" do
165
+ pdf = Prawn::Document.new(:skip_page_creation => true)
166
+ pdf.page_number.should == 0
167
+ end
168
+
169
+ it "should be changed by go_to_page" do
170
+ pdf = Prawn::Document.new
171
+ 10.times { pdf.start_new_page }
172
+ pdf.go_to_page 3
173
+ pdf.page_number.should == 3
174
+ end
175
+
176
+ end
177
+
178
+ describe "on_page_create callback" do
179
+ before do
180
+ create_pdf
181
+ end
182
+
183
+ it "should be delegated from Document to renderer" do
184
+ expect(@pdf.respond_to?(:on_page_create)).to be_true
185
+ end
186
+
187
+ it "should be invoked with document" do
188
+ called_with = nil
189
+
190
+ @pdf.renderer.on_page_create { |*args| called_with = args }
191
+
192
+ @pdf.start_new_page
193
+
194
+ called_with.should == [@pdf]
195
+ end
196
+
197
+ it "should be invoked for each new page" do
198
+ trigger = mock()
199
+ trigger.expects(:fire).times(5)
200
+
201
+ @pdf.renderer.on_page_create { trigger.fire }
202
+
203
+ 5.times { @pdf.start_new_page }
204
+ end
205
+
206
+ it "should be replaceable" do
207
+ trigger1 = mock()
208
+ trigger1.expects(:fire).times(1)
209
+
210
+ trigger2 = mock()
211
+ trigger2.expects(:fire).times(1)
212
+
213
+ @pdf.renderer.on_page_create { trigger1.fire }
214
+
215
+ @pdf.start_new_page
216
+
217
+ @pdf.renderer.on_page_create { trigger2.fire }
218
+
219
+ @pdf.start_new_page
220
+ end
221
+
222
+ it "should be clearable by calling on_page_create without a block" do
223
+ trigger = mock()
224
+ trigger.expects(:fire).times(1)
225
+
226
+ @pdf.renderer.on_page_create { trigger.fire }
227
+
228
+ @pdf.start_new_page
229
+
230
+ @pdf.renderer.on_page_create
231
+
232
+ @pdf.start_new_page
233
+ end
234
+
235
+ end
236
+
237
+ describe "Document compression" do
238
+
239
+ it "should not compress the page content stream if compression is disabled" do
240
+
241
+ pdf = Prawn::Document.new(:compress => false)
242
+ pdf.page.content.stream.stubs(:compress!).returns(true)
243
+ pdf.page.content.stream.expects(:compress!).never
244
+
245
+ pdf.text "Hi There" * 20
246
+ pdf.render
247
+ end
248
+
249
+ it "should compress the page content stream if compression is enabled" do
250
+
251
+ pdf = Prawn::Document.new(:compress => true)
252
+ pdf.page.content.stream.stubs(:compress!).returns(true)
253
+ pdf.page.content.stream.expects(:compress!).once
254
+
255
+ pdf.text "Hi There" * 20
256
+ pdf.render
257
+ end
258
+
259
+ it "should result in a smaller file size when compressed" do
260
+ doc_uncompressed = Prawn::Document.new
261
+ doc_compressed = Prawn::Document.new(:compress => true)
262
+ [doc_compressed, doc_uncompressed].each do |pdf|
263
+ pdf.font "#{Prawn::DATADIR}/fonts/gkai00mp.ttf"
264
+ pdf.text "更可怕的是,同质化竞争对手可以按照URL中后面这个ID来遍历" * 10
265
+ end
266
+
267
+ doc_compressed.render.length.should be < doc_uncompressed.render.length
268
+ end
269
+
270
+ end
271
+
272
+ describe "Document metadata" do
273
+ it "should output strings as UTF-16 with a byte order mark" do
274
+ pdf = Prawn::Document.new(:info => {:Author => "Lóránt"})
275
+ pdf.state.store.info.object.should =~
276
+ # UTF-16: BOM L ó r á n t
277
+ %r{/Author\s*<feff004c00f3007200e1006e0074>}i
278
+ end
279
+ end
280
+
281
+ describe "When reopening pages" do
282
+ it "should modify the content stream size" do
283
+ @pdf = Prawn::Document.new do |pdf|
284
+ pdf.text "Page 1"
285
+ pdf.start_new_page
286
+ pdf.text "Page 2"
287
+ pdf.go_to_page 1
288
+ pdf.text "More for page 1"
289
+ end
290
+
291
+ # Indirectly verify that the actual length does not match dictionary length.
292
+ # If it isn't, a MalformedPDFError will be raised
293
+ PDF::Inspector::Page.analyze(@pdf.render)
294
+ end
295
+
296
+ it "should insert pages after the current page when calling start_new_page" do
297
+ pdf = Prawn::Document.new
298
+ 3.times { |i| pdf.text "Old page #{i+1}"; pdf.start_new_page }
299
+ pdf.go_to_page 1
300
+ pdf.start_new_page
301
+ pdf.text "New page 2"
302
+
303
+ pdf.page_number.should == 2
304
+
305
+ pages = PDF::Inspector::Page.analyze(pdf.render).pages
306
+ pages.size.should == 5
307
+ pages[1][:strings].should == ["New page 2"]
308
+ pages[2][:strings].should == ["Old page 2"]
309
+ end
310
+
311
+ it "should restore the layout of the page" do
312
+ Prawn::Document.new do
313
+ start_new_page :layout => :landscape
314
+ lsize = [bounds.width, bounds.height]
315
+
316
+ [bounds.width, bounds.height].should == lsize
317
+ go_to_page 1
318
+ [bounds.width, bounds.height].should == lsize.reverse
319
+ end
320
+ end
321
+
322
+ it "should restore the margin box of the page" do
323
+ Prawn::Document.new(:margin => [100, 100]) do
324
+ page1_bounds = bounds
325
+
326
+ start_new_page(:margin => [200, 200])
327
+
328
+ [bounds.width, bounds.height].should == [page1_bounds.width - 200,
329
+ page1_bounds.height - 200]
330
+
331
+ go_to_page(1)
332
+
333
+ bounds.width.should == page1_bounds.width
334
+ bounds.height.should == page1_bounds.height
335
+ end
336
+
337
+ end
338
+ end
339
+
340
+ describe "When setting page size" do
341
+ it "should default to LETTER" do
342
+ @pdf = Prawn::Document.new
343
+ pages = PDF::Inspector::Page.analyze(@pdf.render).pages
344
+ pages.first[:size].should == PDF::Core::PageGeometry::SIZES["LETTER"]
345
+ end
346
+
347
+ (PDF::Core::PageGeometry::SIZES.keys - ["LETTER"]).each do |k|
348
+ it "should provide #{k} geometry" do
349
+ @pdf = Prawn::Document.new(:page_size => k)
350
+ pages = PDF::Inspector::Page.analyze(@pdf.render).pages
351
+ pages.first[:size].should == PDF::Core::PageGeometry::SIZES[k]
352
+ end
353
+ end
354
+
355
+ it "should allow custom page size" do
356
+ @pdf = Prawn::Document.new(:page_size => [1920, 1080] )
357
+ pages = PDF::Inspector::Page.analyze(@pdf.render).pages
358
+ pages.first[:size].should == [1920, 1080]
359
+ end
360
+
361
+
362
+ it "should retain page size by default when starting a new page" do
363
+ @pdf = Prawn::Document.new(:page_size => "LEGAL")
364
+ @pdf.start_new_page
365
+ pages = PDF::Inspector::Page.analyze(@pdf.render).pages
366
+ pages.each do |page|
367
+ page[:size].should == PDF::Core::PageGeometry::SIZES["LEGAL"]
368
+ end
369
+ end
370
+
371
+ end
372
+
373
+ describe "When setting page layout" do
374
+ it "should reverse coordinates for landscape" do
375
+ @pdf = Prawn::Document.new(:page_size => "A4", :page_layout => :landscape)
376
+ pages = PDF::Inspector::Page.analyze(@pdf.render).pages
377
+ pages.first[:size].should == PDF::Core::PageGeometry::SIZES["A4"].reverse
378
+ end
379
+
380
+ it "should retain page layout by default when starting a new page" do
381
+ @pdf = Prawn::Document.new(:page_layout => :landscape)
382
+ @pdf.start_new_page(:trace => true)
383
+ pages = PDF::Inspector::Page.analyze(@pdf.render).pages
384
+ pages.each do |page|
385
+ page[:size].should == PDF::Core::PageGeometry::SIZES["LETTER"].reverse
386
+ end
387
+ end
388
+
389
+ it "should swap the bounds when starting a new page with different layout" do
390
+ @pdf = Prawn::Document.new
391
+ size = [@pdf.bounds.width, @pdf.bounds.height]
392
+ @pdf.start_new_page(:layout => :landscape)
393
+ [@pdf.bounds.width, @pdf.bounds.height].should == size.reverse
394
+ end
395
+ end
396
+
397
+ describe "The mask() feature" do
398
+ it "should allow transactional restoration of attributes" do
399
+ @pdf = Prawn::Document.new
400
+ y, line_width = @pdf.y, @pdf.line_width
401
+ @pdf.mask(:y, :line_width) do
402
+ @pdf.y = y + 1
403
+ @pdf.line_width = line_width + 1
404
+ @pdf.y.should_not == y
405
+ @pdf.line_width.should_not == line_width
406
+ end
407
+ @pdf.y.should == y
408
+ @pdf.line_width.should == line_width
409
+ end
410
+ end
411
+
412
+ describe "The group() feature" do
413
+ xit "should return a true value if the content fits on one page" do
414
+ pdf = Prawn::Document.new do
415
+ val = group { text "Hello"; text "World" }
416
+ (!!val).should == true
417
+ end
418
+ end
419
+
420
+ xit "should group a simple block on a single page" do
421
+ pdf = Prawn::Document.new do
422
+ self.y = 50
423
+ val = group do
424
+ text "Hello"
425
+ text "World"
426
+ end
427
+
428
+ # group should return a false value since a new page was started
429
+ (!!val).should == false
430
+ end
431
+ pages = PDF::Inspector::Page.analyze(pdf.render).pages
432
+ pages.size.should == 2
433
+ pages[0][:strings].should == []
434
+ pages[1][:strings].should == ["Hello", "World"]
435
+ end
436
+
437
+ xit "should raise_error CannotGroup if the content is too tall" do
438
+ lambda {
439
+ Prawn::Document.new do
440
+ group do
441
+ 100.times { text "Too long" }
442
+ end
443
+ end.render
444
+ }.should raise_error(Prawn::Errors::CannotGroup)
445
+ end
446
+
447
+ xit "should group within individual column boxes" do
448
+ pdf = Prawn::Document.new do
449
+ # Set up columns with grouped blocks of 0..49. 0 to 49 is slightly short
450
+ # of the height of one page / column, so each column should get its own
451
+ # group (every column should start with zero).
452
+ column_box([0, bounds.top], :width => bounds.width, :columns => 7) do
453
+ 10.times do
454
+ group { 50.times { |i| text(i.to_s) } }
455
+ end
456
+ end
457
+ end
458
+
459
+ # Second page should start with a 0 because it's a new group.
460
+ pages = PDF::Inspector::Page.analyze(pdf.render).pages
461
+ pages.size.should == 2
462
+ pages[1][:strings].first.should == '0'
463
+ end
464
+
465
+ end
466
+
467
+ describe "The render() feature" do
468
+ it "should return a 8 bit encoded string on a m17n aware VM" do
469
+ @pdf = Prawn::Document.new(:page_size => "A4", :page_layout => :landscape)
470
+ @pdf.line [100,100], [200,200]
471
+ str = @pdf.render
472
+ str.encoding.to_s.should == "ASCII-8BIT"
473
+ end
474
+
475
+ it "should trigger before_render callbacks just before rendering" do
476
+ pdf = Prawn::Document.new
477
+
478
+ seq = sequence("callback_order")
479
+
480
+ # Verify the order: finalize -> fire callbacks -> render body
481
+ pdf.renderer.expects(:finalize_all_page_contents).in_sequence(seq)
482
+ trigger = mock()
483
+ trigger.expects(:fire).in_sequence(seq)
484
+
485
+ # Store away the render_body method to be called below
486
+ render_body = pdf.renderer.method(:render_body)
487
+ pdf.renderer.expects(:render_body).in_sequence(seq)
488
+
489
+ pdf.renderer.before_render{ trigger.fire }
490
+
491
+ # Render the body to set up object offsets
492
+ render_body.call(StringIO.new)
493
+ pdf.render
494
+ end
495
+
496
+ it "should be idempotent" do
497
+ pdf = Prawn::Document.new
498
+
499
+ contents = pdf.render
500
+ contents2 = pdf.render
501
+ contents2.should == contents
502
+ end
503
+ end
504
+
505
+ describe "PDF file versions" do
506
+ it "should default to 1.3" do
507
+ @pdf = Prawn::Document.new
508
+ str = @pdf.render
509
+ str[0,8].should == "%PDF-1.3"
510
+ end
511
+
512
+ it "should allow the default to be changed" do
513
+ @pdf = Prawn::Document.new
514
+ @pdf.renderer.min_version(1.4)
515
+ str = @pdf.render
516
+ str[0,8].should == "%PDF-1.4"
517
+ end
518
+ end
519
+
520
+ describe "Documents that use go_to_page" do
521
+ it "should have 2 pages after calling start_new_page and go_to_page" do
522
+ @pdf = Prawn::Document.new
523
+ @pdf.text "James"
524
+ @pdf.start_new_page
525
+ @pdf.text "Anthony"
526
+ @pdf.go_to_page(1)
527
+ @pdf.text "Healy"
528
+
529
+ page_counter = PDF::Inspector::Page.analyze(@pdf.render)
530
+ page_counter.pages.size.should == 2
531
+ end
532
+
533
+ it "should correctly add text to pages" do
534
+ @pdf = Prawn::Document.new
535
+ @pdf.text "James"
536
+ @pdf.start_new_page
537
+ @pdf.text "Anthony"
538
+ @pdf.go_to_page(1)
539
+ @pdf.text "Healy"
540
+
541
+ text = PDF::Inspector::Text.analyze(@pdf.render)
542
+
543
+ text.strings.size.should == 3
544
+ text.strings.include?("James").should == true
545
+ text.strings.include?("Anthony").should == true
546
+ text.strings.include?("Healy").should == true
547
+ end
548
+ end
549
+
550
+ describe "content stream characteristics" do
551
+ it "should have 1 single content stream for a single page PDF" do
552
+ @pdf = Prawn::Document.new
553
+ @pdf.text "James"
554
+ output = StringIO.new(@pdf.render)
555
+ hash = PDF::Reader::ObjectHash.new(output)
556
+
557
+ streams = hash.values.select { |obj| obj.kind_of?(PDF::Reader::Stream) }
558
+
559
+ streams.size.should == 1
560
+ end
561
+
562
+ it "should have 1 single content stream for a single page PDF, even if go_to_page is used" do
563
+ @pdf = Prawn::Document.new
564
+ @pdf.text "James"
565
+ @pdf.go_to_page(1)
566
+ @pdf.text "Healy"
567
+ output = StringIO.new(@pdf.render)
568
+ hash = PDF::Reader::ObjectHash.new(output)
569
+
570
+ streams = hash.values.select { |obj| obj.kind_of?(PDF::Reader::Stream) }
571
+
572
+ streams.size.should == 1
573
+ end
574
+ end
575
+
576
+ describe "The number_pages method" do
577
+ before do
578
+ @pdf = Prawn::Document.new(:skip_page_creation => true)
579
+ end
580
+
581
+ it "replaces the '<page>' string with the proper page number" do
582
+ @pdf.start_new_page
583
+ @pdf.expects(:text_box).with("1, test", { :height => 50 })
584
+ @pdf.number_pages "<page>, test", {:page_filter => :all}
585
+ end
586
+
587
+ it "replaces the '<total>' string with the total page count" do
588
+ @pdf.start_new_page
589
+ @pdf.expects(:text_box).with("test, 1", { :height => 50 })
590
+ @pdf.number_pages "test, <total>", {:page_filter => :all}
591
+ end
592
+
593
+ it "must print each page if given the :all page_filter" do
594
+ 10.times { @pdf.start_new_page }
595
+ @pdf.expects(:text_box).times(10)
596
+ @pdf.number_pages "test", {:page_filter => :all}
597
+ end
598
+
599
+ it "must print each page if no :page_filter is specified" do
600
+ 10.times { @pdf.start_new_page }
601
+ @pdf.expects(:text_box).times(10)
602
+ @pdf.number_pages "test"
603
+ end
604
+
605
+ it "must not print the page number if given a nil filter" do
606
+ 10.times { @pdf.start_new_page }
607
+ @pdf.expects(:text_box).never
608
+ @pdf.number_pages "test", {:page_filter => nil}
609
+ end
610
+
611
+ context "start_count_at option" do
612
+ [1, 2].each do |startat|
613
+ context "equal to #{startat}" do
614
+ it "increments the pages" do
615
+ 2.times { @pdf.start_new_page }
616
+ options = {:page_filter => :all, :start_count_at => startat}
617
+ @pdf.expects(:text_box).with("#{startat} 2", { :height => 50 })
618
+ @pdf.expects(:text_box).with("#{startat+1} 2", { :height => 50 })
619
+ @pdf.number_pages "<page> <total>", options
620
+ end
621
+ end
622
+ end
623
+
624
+ [0, nil].each do |val|
625
+ context "equal to #{val}" do
626
+ it "defaults to start at page 1" do
627
+ 3.times { @pdf.start_new_page }
628
+ options = {:page_filter => :all, :start_count_at => val}
629
+ @pdf.expects(:text_box).with("1 3", { :height => 50 })
630
+ @pdf.expects(:text_box).with("2 3", { :height => 50 })
631
+ @pdf.expects(:text_box).with("3 3", { :height => 50 })
632
+ @pdf.number_pages "<page> <total>", options
633
+ end
634
+ end
635
+ end
636
+ end
637
+
638
+ context "total_pages option" do
639
+ it "allows the total pages count to be overridden" do
640
+ 2.times { @pdf.start_new_page }
641
+ @pdf.expects(:text_box).with("1 10", { :height => 50 })
642
+ @pdf.expects(:text_box).with("2 10", { :height => 50 })
643
+ @pdf.number_pages "<page> <total>", :page_filter => :all, :total_pages => 10
644
+ end
645
+ end
646
+
647
+ context "special page filter" do
648
+ context "such as :odd" do
649
+ it "increments the pages" do
650
+ 3.times { @pdf.start_new_page }
651
+ @pdf.expects(:text_box).with("1 3", { :height => 50 })
652
+ @pdf.expects(:text_box).with("3 3", { :height => 50 })
653
+ @pdf.expects(:text_box).with("2 3", { :height => 50 }).never
654
+ @pdf.number_pages "<page> <total>", :page_filter => :odd
655
+ end
656
+ end
657
+ context "missing" do
658
+ it "does not print any page numbers" do
659
+ 3.times { @pdf.start_new_page }
660
+ @pdf.expects(:text_box).never
661
+ @pdf.number_pages "<page> <total>", :page_filter => nil
662
+ end
663
+ end
664
+ end
665
+
666
+ context "given both a special page filter and a start_count_at parameter" do
667
+ context "such as :odd and 7" do
668
+ it "increments the pages" do
669
+ 3.times { @pdf.start_new_page }
670
+ @pdf.expects(:text_box).with("1 3", { :height => 50 }).never
671
+ @pdf.expects(:text_box).with("5 3", { :height => 50 }) # page 1
672
+ @pdf.expects(:text_box).with("6 3", { :height => 50 }).never # page 2
673
+ @pdf.expects(:text_box).with("7 3", { :height => 50 }) # page 3
674
+ @pdf.number_pages "<page> <total>", :page_filter => :odd, :start_count_at => 5
675
+ end
676
+ end
677
+ context "some crazy proc and 2" do
678
+ it "increments the pages" do
679
+ 6.times { @pdf.start_new_page }
680
+ options = {:page_filter => lambda {|p| p != 2 && p != 5}, :start_count_at => 4}
681
+ @pdf.expects(:text_box).with("4 6", { :height => 50 }) # page 1
682
+ @pdf.expects(:text_box).with("5 6", { :height => 50 }).never # page 2
683
+ @pdf.expects(:text_box).with("6 6", { :height => 50 }) # page 3
684
+ @pdf.expects(:text_box).with("7 6", { :height => 50 }) # page 4
685
+ @pdf.expects(:text_box).with("8 6", { :height => 50 }).never # page 5
686
+ @pdf.expects(:text_box).with("9 6", { :height => 50 }) # page 6
687
+ @pdf.number_pages "<page> <total>", options
688
+ end
689
+ end
690
+ end
691
+
692
+ context "height option" do
693
+ before do
694
+ @pdf.start_new_page
695
+ end
696
+
697
+ it "with 10 height" do
698
+ @pdf.expects(:text_box).with("1 1", { :height => 10 })
699
+ @pdf.number_pages "<page> <total>", :height => 10
700
+ end
701
+
702
+ it "with nil height" do
703
+ @pdf.expects(:text_box).with("1 1", { :height => nil })
704
+ @pdf.number_pages "<page> <total>", :height => nil
705
+ end
706
+
707
+ it "with no height" do
708
+ @pdf.expects(:text_box).with("1 1", { :height => 50 })
709
+ @pdf.number_pages "<page> <total>"
710
+ end
711
+ end
712
+ end
713
+
714
+ describe "The page_match? method" do
715
+ before do
716
+ @pdf = Prawn::Document.new(:skip_page_creation => true)
717
+ 10.times {@pdf.start_new_page}
718
+ end
719
+
720
+ it "returns nil given no filter" do
721
+ @pdf.page_match?(:nil, 1).should be_false
722
+ end
723
+
724
+ it "must provide an :all filter" do
725
+ (1..@pdf.page_count).all? { |i| @pdf.page_match?(:all, i) }.should be_true
726
+ end
727
+
728
+ it "must provide an :odd filter" do
729
+ odd, even = (1..@pdf.page_count).partition { |e| e % 2 == 1 }
730
+ odd.all? { |i| @pdf.page_match?(:odd, i) }.should be_true
731
+ even.any? { |i| @pdf.page_match?(:odd, i) }.should be_false
732
+ end
733
+
734
+ it "must be able to filter by an array of page numbers" do
735
+ fltr = [1,2,7]
736
+ (1..10).select { |i| @pdf.page_match?(fltr, i) }.should == [1,2,7]
737
+ end
738
+
739
+ it "must be able to filter by a range of page numbers" do
740
+ fltr = 2..4
741
+ (1..10).select { |i| @pdf.page_match?(fltr, i) }.should == [2,3,4]
742
+ end
743
+
744
+ it "must be able to filter by an arbitrary proc" do
745
+ fltr = lambda { |x| x == 1 or x % 3 == 0 }
746
+ (1..10).select { |i| @pdf.page_match?(fltr, i) }.should == [1,3,6,9]
747
+ end
748
+ end