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,25 @@
1
+ # encoding: utf-8
2
+
3
+ # Spec'ing the PNG class. Not complete yet - still needs to check the
4
+ # contents of palette and transparency to ensure they're correct.
5
+ # Need to find files that have these sections first.
6
+
7
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
8
+
9
+ describe "When reading a JPEG file" do
10
+
11
+ before(:each) do
12
+ @filename = "#{Prawn::DATADIR}/images/pigs.jpg"
13
+ @img_data = File.open(@filename, "rb") { |f| f.read }
14
+ end
15
+
16
+ it "should read the basic attributes correctly" do
17
+ jpg = Prawn::Images::JPG.new(@img_data)
18
+
19
+ jpg.width.should == 604
20
+ jpg.height.should == 453
21
+ jpg.bits.should == 8
22
+ jpg.channels.should == 3
23
+ end
24
+ end
25
+
@@ -0,0 +1,367 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
4
+
5
+ describe "Core::Text::Formatted::LineWrap#wrap_line" do
6
+ before(:each) do
7
+ create_pdf
8
+ @arranger = Prawn::Text::Formatted::Arranger.new(@pdf)
9
+ @line_wrap = Prawn::Text::Formatted::LineWrap.new
10
+ @one_word_width = 50
11
+ end
12
+ it "should strip leading and trailing spaces" do
13
+ array = [{ :text => " hello world, " },
14
+ { :text => "goodbye ", :style => [:bold] }]
15
+ @arranger.format_array = array
16
+ string = @line_wrap.wrap_line(:arranger => @arranger,
17
+ :width => 300,
18
+ :document => @pdf)
19
+ string.should == "hello world, goodbye"
20
+ end
21
+ it "should strip trailing spaces when a white-space-only fragment was" +
22
+ " successfully pushed onto the end of a line but no other non-white" +
23
+ " space fragment fits after it" do
24
+ array = [{ :text => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa " },
25
+ { :text => " ", :style => [:bold] },
26
+ { :text => " bbbbbbbbbbbbbbbbbbbbbbbbbbbb" }]
27
+ @arranger.format_array = array
28
+ string = @line_wrap.wrap_line(:arranger => @arranger,
29
+ :width => 300,
30
+ :document => @pdf)
31
+ string.should == "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
32
+ end
33
+ it "should raise_error CannotFit if a too-small width is given" do
34
+ array = [{ :text => " hello world, " },
35
+ { :text => "goodbye ", :style => [:bold] }]
36
+ @arranger.format_array = array
37
+ lambda do
38
+ @line_wrap.wrap_line(:arranger => @arranger,
39
+ :width => 1,
40
+ :document => @pdf)
41
+ end.should raise_error(Prawn::Errors::CannotFit)
42
+ end
43
+
44
+ it "should break on space" do
45
+ array = [{ :text => "hello world" }]
46
+ @arranger.format_array = array
47
+ string = @line_wrap.wrap_line(:arranger => @arranger,
48
+ :width => @one_word_width,
49
+ :document => @pdf)
50
+ string.should == "hello"
51
+ end
52
+
53
+ it "should break on zero-width space" do
54
+ @pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
55
+ array = [{ :text => "hello#{Prawn::Text::ZWSP}world" }]
56
+ @arranger.format_array = array
57
+ string = @line_wrap.wrap_line(:arranger => @arranger,
58
+ :width => @one_word_width,
59
+ :document => @pdf)
60
+ string.should == "hello"
61
+ end
62
+
63
+ it "should not display zero-width space" do
64
+ @pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
65
+ array = [{ :text => "hello#{Prawn::Text::ZWSP}world" }]
66
+ @arranger.format_array = array
67
+ string = @line_wrap.wrap_line(:arranger => @arranger,
68
+ :width => 300,
69
+ :document => @pdf)
70
+ string.should == "helloworld"
71
+ end
72
+
73
+ it "should break on tab" do
74
+ array = [{ :text => "hello\tworld" }]
75
+ @arranger.format_array = array
76
+ string = @line_wrap.wrap_line(:arranger => @arranger,
77
+ :width => @one_word_width,
78
+ :document => @pdf)
79
+ string.should == "hello"
80
+ end
81
+
82
+ it "should break on hyphens" do
83
+ array = [{ :text => "hello-world" }]
84
+ @arranger.format_array = array
85
+ string = @line_wrap.wrap_line(:arranger => @arranger,
86
+ :width => @one_word_width,
87
+ :document => @pdf)
88
+ string.should == "hello-"
89
+ end
90
+
91
+ it "should not break after a hyphen that follows white space and" +
92
+ "precedes a word" do
93
+ array = [{ :text => "hello -" }]
94
+ @arranger.format_array = array
95
+ string = @line_wrap.wrap_line(:arranger => @arranger,
96
+ :width => @one_word_width,
97
+ :document => @pdf)
98
+ string.should == "hello -"
99
+
100
+ array = [{ :text => "hello -world" }]
101
+ @arranger.format_array = array
102
+ string = @line_wrap.wrap_line(:arranger => @arranger,
103
+ :width => @one_word_width,
104
+ :document => @pdf)
105
+ string.should == "hello"
106
+ end
107
+
108
+ it "should break on a soft hyphen" do
109
+ string = @pdf.font.normalize_encoding("hello#{Prawn::Text::SHY}world")
110
+ array = [{ :text => string }]
111
+ @arranger.format_array = array
112
+ string = @line_wrap.wrap_line(:arranger => @arranger,
113
+ :width => @one_word_width,
114
+ :document => @pdf)
115
+ expected = @pdf.font.normalize_encoding("hello#{Prawn::Text::SHY}")
116
+ expected.force_encoding(Encoding::UTF_8)
117
+ string.should == expected
118
+
119
+ @pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
120
+ @line_wrap = Prawn::Text::Formatted::LineWrap.new
121
+
122
+ string = "hello#{Prawn::Text::SHY}world"
123
+ array = [{ :text => string }]
124
+ @arranger.format_array = array
125
+ string = @line_wrap.wrap_line(:arranger => @arranger,
126
+ :width => @one_word_width,
127
+ :document => @pdf)
128
+ string.should == "hello#{Prawn::Text::SHY}"
129
+ end
130
+
131
+ it "should ignore width of a soft-hyphen during adding fragments to line", :issue =>775 do
132
+ hyphen_string = "Hy#{Prawn::Text::SHY}phe#{Prawn::Text::SHY}nat#{Prawn::Text::SHY}ions "
133
+ string1 = @pdf.font.normalize_encoding(hyphen_string * 5)
134
+ string2 = @pdf.font.normalize_encoding("Hyphenations " * 3 + hyphen_string)
135
+
136
+ array1 = [{text: string1}]
137
+ array2 = [{text: string2}]
138
+
139
+ @arranger.format_array = array1
140
+
141
+ res1 = @line_wrap.wrap_line(:arranger => @arranger,
142
+ :width => 300,
143
+ :document => @pdf)
144
+
145
+ @line_wrap = Prawn::Text::Formatted::LineWrap.new
146
+
147
+ @arranger.format_array = array2
148
+
149
+ res2 = @line_wrap.wrap_line(:arranger => @arranger,
150
+ :width => 300,
151
+ :document => @pdf)
152
+ res1.should == res2
153
+ end
154
+
155
+ it "should not display soft hyphens except at the end of a line " +
156
+ "for more than one element in format_array", :issue => 347 do
157
+ @pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
158
+ @line_wrap = Prawn::Text::Formatted::LineWrap.new
159
+
160
+ string1 = @pdf.font.normalize_encoding("hello#{Prawn::Text::SHY}world ")
161
+ string2 = @pdf.font.normalize_encoding("hi#{Prawn::Text::SHY}earth")
162
+ array = [{ :text => string1 }, { :text => string2 }]
163
+ @arranger.format_array = array
164
+ string = @line_wrap.wrap_line(:arranger => @arranger,
165
+ :width => 300,
166
+ :document => @pdf)
167
+ string.should == "helloworld hiearth"
168
+ end
169
+
170
+ it "should not break before a hard hyphen that follows a word" do
171
+ enough_width_for_hello_world = 60
172
+
173
+ array = [{ :text => "hello world" }]
174
+ @arranger.format_array = array
175
+ string = @line_wrap.wrap_line(:arranger => @arranger,
176
+ :width => enough_width_for_hello_world,
177
+ :document => @pdf)
178
+ string.should == "hello world"
179
+
180
+ array = [{ :text => "hello world-" }]
181
+ @arranger.format_array = array
182
+ string = @line_wrap.wrap_line(:arranger => @arranger,
183
+ :width => enough_width_for_hello_world,
184
+ :document => @pdf)
185
+ string.should == "hello"
186
+
187
+ @pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
188
+ @line_wrap = Prawn::Text::Formatted::LineWrap.new
189
+ enough_width_for_hello_world = 68
190
+
191
+ array = [{ :text => "hello world" }]
192
+ @arranger.format_array = array
193
+ string = @line_wrap.wrap_line(:arranger => @arranger,
194
+ :width => enough_width_for_hello_world,
195
+ :document => @pdf)
196
+ string.should == "hello world"
197
+
198
+ array = [{ :text => "hello world-" }]
199
+ @arranger.format_array = array
200
+ string = @line_wrap.wrap_line(:arranger => @arranger,
201
+ :width => enough_width_for_hello_world,
202
+ :document => @pdf)
203
+ string.should == "hello"
204
+ end
205
+
206
+ it "should not break after a hard hyphen that follows a soft hyphen and" +
207
+ "precedes a word" do
208
+ string = @pdf.font.normalize_encoding("hello#{Prawn::Text::SHY}-")
209
+ array = [{ :text => string }]
210
+ @arranger.format_array = array
211
+ string = @line_wrap.wrap_line(:arranger => @arranger,
212
+ :width => @one_word_width,
213
+ :document => @pdf)
214
+ string.should == "hello-"
215
+
216
+ string = @pdf.font.normalize_encoding("hello#{Prawn::Text::SHY}-world")
217
+ array = [{ :text => string }]
218
+ @arranger.format_array = array
219
+ string = @line_wrap.wrap_line(:arranger => @arranger,
220
+ :width => @one_word_width,
221
+ :document => @pdf)
222
+ expected = @pdf.font.normalize_encoding("hello#{Prawn::Text::SHY}")
223
+ expected.force_encoding(Encoding::UTF_8)
224
+ string.should == expected
225
+
226
+ @pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
227
+ @line_wrap = Prawn::Text::Formatted::LineWrap.new
228
+
229
+ string = "hello#{Prawn::Text::SHY}-"
230
+ array = [{ :text => string }]
231
+ @arranger.format_array = array
232
+ string = @line_wrap.wrap_line(:arranger => @arranger,
233
+ :width => @one_word_width,
234
+ :document => @pdf)
235
+ string.should == "hello-"
236
+
237
+ string = "hello#{Prawn::Text::SHY}-world"
238
+ array = [{ :text => string }]
239
+ @arranger.format_array = array
240
+ string = @line_wrap.wrap_line(:arranger => @arranger,
241
+ :width => @one_word_width,
242
+ :document => @pdf)
243
+ string.should == "hello#{Prawn::Text::SHY}"
244
+ end
245
+
246
+ it "should process UTF-8 chars", :unresolved, :issue => 693 do
247
+ array = [{ :text => "Test" }]
248
+ @arranger.format_array = array
249
+
250
+ # Should not raise an encoding error
251
+ string = @line_wrap.wrap_line(:arranger => @arranger,
252
+ :width => 300,
253
+ :document => @pdf)
254
+ string.should == "Test"
255
+ end
256
+ end
257
+
258
+ describe "Core::Text::Formatted::LineWrap#space_count" do
259
+ before(:each) do
260
+ create_pdf
261
+ @arranger = Prawn::Text::Formatted::Arranger.new(@pdf)
262
+ @line_wrap = Prawn::Text::Formatted::LineWrap.new
263
+ end
264
+ it "should return the number of spaces in the last wrapped line" do
265
+ array = [{ :text => "hello world, " },
266
+ { :text => "goodbye", :style => [:bold] }]
267
+ @arranger.format_array = array
268
+ @line_wrap.wrap_line(:arranger => @arranger,
269
+ :width => 300,
270
+ :document => @pdf)
271
+ @line_wrap.space_count.should == 2
272
+ end
273
+ it "should exclude preceding and trailing spaces from the count" do
274
+ array = [{ :text => " hello world, " },
275
+ { :text => "goodbye ", :style => [:bold] }]
276
+ @arranger.format_array = array
277
+ @line_wrap.wrap_line(:arranger => @arranger,
278
+ :width => 300,
279
+ :document => @pdf)
280
+ @line_wrap.space_count.should == 2
281
+ end
282
+ end
283
+
284
+ describe "Core::Text::Formatted::LineWrap" do
285
+ before(:each) do
286
+ create_pdf
287
+ @arranger = Prawn::Text::Formatted::Arranger.new(@pdf)
288
+ array = [{ :text => "hello\nworld\n\n\nhow are you?" },
289
+ { :text => "\n" },
290
+ { :text => "\n" },
291
+ { :text => "" },
292
+ { :text => "fine, thanks. " * 4 },
293
+ { :text => "" },
294
+ { :text => "\n" },
295
+ { :text => "" }]
296
+ @arranger.format_array = array
297
+ @line_wrap = Prawn::Text::Formatted::LineWrap.new
298
+ end
299
+ it "should only return an empty string if nothing fit or there" +
300
+ "was nothing to wrap" do
301
+ 8.times do
302
+ line = @line_wrap.wrap_line(:arranger => @arranger,
303
+ :width => 200,
304
+ :document => @pdf)
305
+ line.should_not be_empty
306
+ end
307
+ line = @line_wrap.wrap_line(:arranger => @arranger,
308
+ :width => 200,
309
+ :document => @pdf)
310
+ line.should be_empty
311
+ end
312
+ it "should tokenize a string using the scan_pattern" do
313
+ tokens = @line_wrap.tokenize("one two three")
314
+ tokens.length.should == 6
315
+ end
316
+ end
317
+
318
+ describe "Core::Text::Formatted::LineWrap#paragraph_finished?" do
319
+ before(:each) do
320
+ create_pdf
321
+ @arranger = Prawn::Text::Formatted::Arranger.new(@pdf)
322
+ @line_wrap = Prawn::Text::Formatted::LineWrap.new
323
+ @one_word_width = 50
324
+ end
325
+ it "should be_false when the last printed line is not the end of the paragraph" do
326
+ array = [{ :text => "hello world" }]
327
+ @arranger.format_array = array
328
+ string = @line_wrap.wrap_line(:arranger => @arranger,
329
+ :width => @one_word_width,
330
+ :document => @pdf)
331
+
332
+ @line_wrap.paragraph_finished?.should == false
333
+ end
334
+ it "should be_true when the last printed line is the last fragment to print" do
335
+ array = [{ :text => "hello world" }]
336
+ @arranger.format_array = array
337
+ string = @line_wrap.wrap_line(:arranger => @arranger,
338
+ :width => @one_word_width,
339
+ :document => @pdf)
340
+ string = @line_wrap.wrap_line(:arranger => @arranger,
341
+ :width => @one_word_width,
342
+ :document => @pdf)
343
+
344
+ @line_wrap.paragraph_finished?.should == true
345
+ end
346
+ it "should be_true when a newline exists on the current line" do
347
+ array = [{ :text => "hello\n world" }]
348
+ @arranger.format_array = array
349
+ string = @line_wrap.wrap_line(:arranger => @arranger,
350
+ :width => @one_word_width,
351
+ :document => @pdf)
352
+
353
+ @line_wrap.paragraph_finished?.should == true
354
+ end
355
+ it "should be_true when a newline exists in the next fragment" do
356
+ array = [{ :text => "hello " },
357
+ { :text => " \n" },
358
+ { :text => "world" }]
359
+ @arranger.format_array = array
360
+ string = @line_wrap.wrap_line(:arranger => @arranger,
361
+ :width => @one_word_width,
362
+ :document => @pdf)
363
+
364
+ @line_wrap.paragraph_finished?.should == true
365
+ end
366
+ end
367
+
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
4
+ require "prawn/measurement_extensions"
5
+
6
+ describe "Measurement units" do
7
+
8
+ it "should convert units to PostScriptPoints" do
9
+ 1.mm.should be_within(0.000000001).of(2.834645669)
10
+ 1.mm.should == (72 / 25.4)
11
+ 2.mm.should == (2 * 72 / 25.4)
12
+ 3.mm.should == 3 * 72 / 25.4
13
+ -3.mm.should == -3 * 72/25.4
14
+ 1.cm.should == 10 * 72 / 25.4
15
+ 1.dm.should == 100 * 72 / 25.4
16
+ 1.m.should == 1000 * 72 / 25.4
17
+
18
+ 1.in.should == 72
19
+ 1.ft.should == 72 * 12
20
+ 1.yd.should == 72 * 12 * 3
21
+ 1.pt.should == 1
22
+ end
23
+
24
+ end
25
+
@@ -0,0 +1,430 @@
1
+ # encoding: utf-8
2
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
3
+
4
+ describe "Outline" do
5
+ before(:each) do
6
+ @pdf = Prawn::Document.new() do
7
+ text "Page 1. This is the first Chapter. "
8
+ start_new_page
9
+ text "Page 2. More in the first Chapter. "
10
+ start_new_page
11
+ outline.define do
12
+ section 'Chapter 1', :destination => 1, :closed => true do
13
+ page :destination => 1, :title => 'Page 1'
14
+ page :destination => 2, :title => 'Page 2'
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ describe "outline encoding" do
21
+ it "should store all outline titles as UTF-16" do
22
+ render_and_find_objects
23
+ @hash.values.each do |obj|
24
+ if obj.is_a?(Hash) && obj[:Title]
25
+ title = obj[:Title].dup
26
+ title.force_encoding(Encoding::UTF_16LE)
27
+ title.valid_encoding?.should == true
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ describe "#generate_outline" do
34
+ before(:each) do
35
+ render_and_find_objects
36
+ end
37
+
38
+ it "should create a root outline dictionary item" do
39
+ @outline_root.should_not be_nil
40
+ end
41
+
42
+ it "should set the first and last top items of the root outline dictionary item" do
43
+ referenced_object(@outline_root[:First]).should == @section_1
44
+ referenced_object(@outline_root[:Last]).should == @section_1
45
+ end
46
+
47
+ describe "#create_outline_item" do
48
+ it "should create outline items for each section and page" do
49
+ [@section_1, @page_1, @page_2].each {|item| item.should_not be_nil}
50
+ end
51
+ end
52
+
53
+ describe "#set_relations, #set_variables_for_block, and #reset_parent" do
54
+ it "should link sibling items" do
55
+ referenced_object(@page_1[:Next]).should == @page_2
56
+ referenced_object(@page_2[:Prev]).should == @page_1
57
+ end
58
+
59
+ it "should link child items to parent item" do
60
+ [@page_1, @page_2].each {|page| referenced_object(page[:Parent]).should == @section_1 }
61
+ end
62
+
63
+ it "should set the first and last child items for parent item" do
64
+ referenced_object(@section_1[:First]).should == @page_1
65
+ referenced_object(@section_1[:Last]).should == @page_2
66
+ end
67
+ end
68
+
69
+ describe "#increase_count" do
70
+
71
+ it "should add the count of all descendant items" do
72
+ @outline_root[:Count].should == 3
73
+ @section_1[:Count].abs.should == 2
74
+ @page_1[:Count].should == 0
75
+ @page_2[:Count].should == 0
76
+ end
77
+
78
+ end
79
+
80
+ describe "closed option" do
81
+
82
+ it "should set the item's integer count to negative" do
83
+ @section_1[:Count].should == -2
84
+ end
85
+
86
+ end
87
+
88
+ end
89
+
90
+ describe "adding a custom destination" do
91
+ before(:each) do
92
+ @pdf.start_new_page
93
+ @pdf.text "Page 3 with a destination"
94
+ @pdf.add_dest('customdest', @pdf.dest_xyz(200, 200))
95
+ pdf = @pdf
96
+ @pdf.outline.update do
97
+ page :destination => pdf.dest_xyz(200, 200), :title => 'Custom Destination'
98
+ end
99
+ render_and_find_objects
100
+ end
101
+
102
+ it "should create an outline item" do
103
+ @custom_dest.should_not be_nil
104
+ end
105
+
106
+ it "should reference the custom destination" do
107
+ referenced_object(@custom_dest[:Dest].first).should == referenced_object(@pages.last)
108
+ end
109
+
110
+ end
111
+
112
+ describe "addding a section later with outline#section" do
113
+ before(:each) do
114
+ @pdf.start_new_page
115
+ @pdf.text "Page 3. An added section "
116
+ @pdf.outline.update do
117
+ section 'Added Section', :destination => 3 do
118
+ page :destination => 3, :title => 'Page 3'
119
+ end
120
+ end
121
+ render_and_find_objects
122
+ end
123
+
124
+ it "should add new outline items to document" do
125
+ [@section_2, @page_3].each { |item| item.should_not be_nil}
126
+ end
127
+
128
+ it "should reset the last items for root outline dictionary" do
129
+ referenced_object(@outline_root[:First]).should == @section_1
130
+ referenced_object(@outline_root[:Last]).should == @section_2
131
+ end
132
+
133
+ it "should reset the next relation for the previous last top level item" do
134
+ referenced_object(@section_1[:Next]).should == @section_2
135
+ end
136
+
137
+ it "should set the previous relation of the addded to section" do
138
+ referenced_object(@section_2[:Prev]).should == @section_1
139
+ end
140
+
141
+ it "should increase the count of root outline dictionary" do
142
+ @outline_root[:Count].should == 5
143
+ end
144
+
145
+ end
146
+
147
+ describe "#outline.add_subsection_to" do
148
+ context "positioned last" do
149
+
150
+ before(:each) do
151
+ @pdf.start_new_page
152
+ @pdf.text "Page 3. An added subsection "
153
+ @pdf.outline.update do
154
+ add_subsection_to 'Chapter 1' do
155
+ section 'Added SubSection', :destination => 3 do
156
+ page :destination => 3, :title => 'Added Page 3'
157
+ end
158
+ end
159
+ end
160
+ render_and_find_objects
161
+ end
162
+
163
+ it "should add new outline items to document" do
164
+ [@subsection, @added_page_3].each { |item| item.should_not be_nil}
165
+ end
166
+
167
+ it "should reset the last item for parent item dictionary" do
168
+ referenced_object(@section_1[:First]).should == @page_1
169
+ referenced_object(@section_1[:Last]).should == @subsection
170
+ end
171
+
172
+ it "should set the prev relation for the new subsection to its parent's old last item" do
173
+ referenced_object(@subsection[:Prev]).should == @page_2
174
+ end
175
+
176
+
177
+ it "the subsection should become the next relation for its parent's old last item" do
178
+ referenced_object(@page_2[:Next]).should == @subsection
179
+ end
180
+
181
+ it "should set the first relation for the new subsection" do
182
+ referenced_object(@subsection[:First]).should == @added_page_3
183
+ end
184
+
185
+ it "should set the correct last relation of the added to section" do
186
+ referenced_object(@subsection[:Last]).should == @added_page_3
187
+ end
188
+
189
+ it "should increase the count of root outline dictionary" do
190
+ @outline_root[:Count].should == 5
191
+ end
192
+
193
+ end
194
+
195
+ context "positioned first" do
196
+
197
+ before(:each) do
198
+ @pdf.start_new_page
199
+ @pdf.text "Page 3. An added subsection "
200
+ @pdf.outline.update do
201
+ add_subsection_to 'Chapter 1', :first do
202
+ section 'Added SubSection', :destination => 3 do
203
+ page :destination => 3, :title => 'Added Page 3'
204
+ end
205
+ end
206
+ end
207
+ render_and_find_objects
208
+ end
209
+
210
+ it "should add new outline items to document" do
211
+ [@subsection, @added_page_3].each { |item| item.should_not be_nil}
212
+ end
213
+
214
+ it "should reset the first item for parent item dictionary" do
215
+ referenced_object(@section_1[:First]).should == @subsection
216
+ referenced_object(@section_1[:Last]).should == @page_2
217
+ end
218
+
219
+ it "should set the next relation for the new subsection to its parent's old first item" do
220
+ referenced_object(@subsection[:Next]).should == @page_1
221
+ end
222
+
223
+ it "the subsection should become the prev relation for its parent's old first item" do
224
+ referenced_object(@page_1[:Prev]).should == @subsection
225
+ end
226
+
227
+ it "should set the first relation for the new subsection" do
228
+ referenced_object(@subsection[:First]).should == @added_page_3
229
+ end
230
+
231
+ it "should set the correct last relation of the added to section" do
232
+ referenced_object(@subsection[:Last]).should == @added_page_3
233
+ end
234
+
235
+ it "should increase the count of root outline dictionary" do
236
+ @outline_root[:Count].should == 5
237
+ end
238
+
239
+ end
240
+
241
+ it "should require an existing title" do
242
+ lambda do
243
+ @pdf.go_to_page 1
244
+ @pdf.start_new_page
245
+ @pdf.text "Inserted Page"
246
+ @pdf.outline.update do
247
+ add_subsection_to 'Wrong page' do
248
+ page page_number, :title => "Inserted Page"
249
+ end
250
+ end
251
+ render_and_find_objects
252
+ end.should raise_error(Prawn::Errors::UnknownOutlineTitle)
253
+ end
254
+ end
255
+
256
+ describe "#outline.insert_section_after" do
257
+ describe "inserting in the middle of another section" do
258
+ before(:each) do
259
+ @pdf.go_to_page 1
260
+ @pdf.start_new_page
261
+ @pdf.text "Inserted Page"
262
+ @pdf.outline.update do
263
+ insert_section_after 'Page 1' do
264
+ page :destination => page_number, :title => "Inserted Page"
265
+ end
266
+ end
267
+ end
268
+
269
+ it "should insert new outline items to document" do
270
+ render_and_find_objects
271
+ @inserted_page.should_not be_nil
272
+ end
273
+
274
+ it "should adjust the count of all ancestors" do
275
+ render_and_find_objects
276
+ @outline_root[:Count].should == 4
277
+ @section_1[:Count].abs.should == 3
278
+ end
279
+
280
+ describe "#adjust_relations" do
281
+
282
+ it "should reset the sibling relations of adjoining items to inserted item" do
283
+ render_and_find_objects
284
+ referenced_object(@page_1[:Next]).should == @inserted_page
285
+ referenced_object(@page_2[:Prev]).should == @inserted_page
286
+ end
287
+
288
+ it "should set the sibling relation of added item to adjoining items" do
289
+ render_and_find_objects
290
+ referenced_object(@inserted_page[:Next]).should == @page_2
291
+ referenced_object(@inserted_page[:Prev]).should == @page_1
292
+ end
293
+
294
+ it "should not affect the first and last relations of parent item" do
295
+ render_and_find_objects
296
+ referenced_object(@section_1[:First]).should == @page_1
297
+ referenced_object(@section_1[:Last]).should == @page_2
298
+ end
299
+
300
+ end
301
+
302
+
303
+ context "when adding another section afterwards" do
304
+ it "should have reset the root position so that a new section is added at the end of root sections" do
305
+ @pdf.start_new_page
306
+ @pdf.text "Another Inserted Page"
307
+ @pdf.outline.update do
308
+ section 'Added Section' do
309
+ page :destination => page_number, :title => "Inserted Page"
310
+ end
311
+ end
312
+ render_and_find_objects
313
+ referenced_object(@outline_root[:Last]).should == @section_2
314
+ referenced_object(@section_1[:Next]).should == @section_2
315
+ end
316
+ end
317
+
318
+ end
319
+
320
+
321
+ describe "inserting at the end of another section" do
322
+
323
+ before(:each) do
324
+ @pdf.go_to_page 2
325
+ @pdf.start_new_page
326
+ @pdf.text "Inserted Page"
327
+ @pdf.outline.update do
328
+ insert_section_after 'Page 2' do
329
+ page :destination => page_number, :title => "Inserted Page"
330
+ end
331
+ end
332
+ render_and_find_objects
333
+ end
334
+
335
+ describe "#adjust_relations" do
336
+
337
+ it "should reset the sibling relations of adjoining item to inserted item" do
338
+ referenced_object(@page_2[:Next]).should == @inserted_page
339
+ end
340
+
341
+ it "should set the sibling relation of added item to adjoining items" do
342
+ referenced_object(@inserted_page[:Next]).should be_nil
343
+ referenced_object(@inserted_page[:Prev]).should == @page_2
344
+ end
345
+
346
+ it "should adjust the last relation of parent item" do
347
+ referenced_object(@section_1[:Last]).should == @inserted_page
348
+ end
349
+
350
+ end
351
+ end
352
+
353
+ it "should require an existing title" do
354
+ lambda do
355
+ @pdf.go_to_page 1
356
+ @pdf.start_new_page
357
+ @pdf.text "Inserted Page"
358
+ @pdf.outline.update do
359
+ insert_section_after 'Wrong page' do
360
+ page :destination => page_number, :title => "Inserted Page"
361
+ end
362
+ end
363
+ render_and_find_objects
364
+ end.should raise_error(Prawn::Errors::UnknownOutlineTitle)
365
+ end
366
+
367
+ end
368
+
369
+ describe "#page" do
370
+ it "should require a title option to be set" do
371
+ lambda do
372
+ @pdf = Prawn::Document.new() do
373
+ text "Page 1. This is the first Chapter. "
374
+ outline.define do
375
+ page :destination => 1, :title => nil
376
+ end
377
+ end
378
+ end.should raise_error(Prawn::Errors::RequiredOption)
379
+ end
380
+ end
381
+ end
382
+
383
+ describe "foreign character encoding" do
384
+ before(:each) do
385
+ pdf = Prawn::Document.new() do
386
+ outline.define do
387
+ section 'La pomme croquée', :destination => 1, :closed => true
388
+ end
389
+ end
390
+ @hash = PDF::Reader::ObjectHash.new(StringIO.new(pdf.render, 'r+'))
391
+ end
392
+
393
+ it "should handle other encodings for the title" do
394
+ object = find_by_title('La pomme croquée')
395
+ object.should_not == nil
396
+ end
397
+ end
398
+
399
+ def render_and_find_objects
400
+ output = StringIO.new(@pdf.render, 'r+')
401
+ @hash = PDF::Reader::ObjectHash.new(output)
402
+ @outline_root = @hash.values.find {|obj| obj.is_a?(Hash) && obj[:Type] == :Outlines}
403
+ @pages = @hash.values.find {|obj| obj.is_a?(Hash) && obj[:Type] == :Pages}[:Kids]
404
+ @section_1 = find_by_title('Chapter 1')
405
+ @page_1 = find_by_title('Page 1')
406
+ @page_2 = find_by_title('Page 2')
407
+ @section_2 = find_by_title('Added Section')
408
+ @page_3 = find_by_title('Page 3')
409
+ @inserted_page = find_by_title('Inserted Page')
410
+ @subsection = find_by_title('Added SubSection')
411
+ @added_page_3 = find_by_title('Added Page 3')
412
+ @custom_dest = find_by_title('Custom Destination')
413
+ end
414
+
415
+ # Outline titles are stored as UTF-16. This method accepts a UTF-8 outline title
416
+ # and returns the PDF Object that contains an outline with that name
417
+ def find_by_title(title)
418
+ @hash.values.find {|obj|
419
+ if obj.is_a?(Hash) && obj[:Title]
420
+ title_codepoints = obj[:Title].unpack("n*")
421
+ title_codepoints.shift
422
+ utf8_title = title_codepoints.pack("U*")
423
+ utf8_title == title ? obj : nil
424
+ end
425
+ }
426
+ end
427
+
428
+ def referenced_object(reference)
429
+ @hash[reference]
430
+ end