prawn-core 0.7.2 → 0.8.4

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 (65) hide show
  1. data/Rakefile +1 -1
  2. data/examples/general/background.rb +1 -1
  3. data/examples/general/measurement_units.rb +2 -2
  4. data/examples/general/outlines.rb +50 -0
  5. data/examples/general/repeaters.rb +11 -7
  6. data/examples/general/stamp.rb +6 -6
  7. data/examples/graphics/basic_images.rb +1 -1
  8. data/examples/graphics/curves.rb +1 -1
  9. data/examples/graphics/rounded_polygons.rb +19 -0
  10. data/examples/graphics/rounded_rectangle.rb +20 -0
  11. data/examples/graphics/transformations.rb +52 -0
  12. data/examples/m17n/win_ansi_charset.rb +1 -1
  13. data/examples/text/font_calculations.rb +3 -3
  14. data/examples/text/indent_paragraphs.rb +18 -0
  15. data/examples/text/kerning.rb +4 -4
  16. data/examples/text/rotated.rb +98 -0
  17. data/examples/text/simple_text.rb +3 -3
  18. data/examples/text/simple_text_ttf.rb +1 -1
  19. data/lib/prawn/byte_string.rb +1 -0
  20. data/lib/prawn/core.rb +12 -5
  21. data/lib/prawn/core/object_store.rb +99 -0
  22. data/lib/prawn/core/page.rb +96 -0
  23. data/lib/prawn/core/text.rb +75 -0
  24. data/lib/prawn/document.rb +71 -78
  25. data/lib/prawn/document/annotations.rb +2 -2
  26. data/lib/prawn/document/bounding_box.rb +19 -9
  27. data/lib/prawn/document/column_box.rb +13 -12
  28. data/lib/prawn/document/graphics_state.rb +49 -0
  29. data/lib/prawn/document/internals.rb +5 -40
  30. data/lib/prawn/document/page_geometry.rb +1 -18
  31. data/lib/prawn/document/snapshot.rb +12 -7
  32. data/lib/prawn/errors.rb +18 -0
  33. data/lib/prawn/font.rb +4 -2
  34. data/lib/prawn/font/afm.rb +8 -0
  35. data/lib/prawn/font/dfont.rb +12 -4
  36. data/lib/prawn/font/ttf.rb +9 -0
  37. data/lib/prawn/graphics.rb +66 -9
  38. data/lib/prawn/graphics/color.rb +1 -1
  39. data/lib/prawn/graphics/transformation.rb +156 -0
  40. data/lib/prawn/graphics/transparency.rb +3 -7
  41. data/lib/prawn/images.rb +4 -3
  42. data/lib/prawn/images/png.rb +2 -2
  43. data/lib/prawn/outline.rb +278 -0
  44. data/lib/prawn/pdf_object.rb +5 -3
  45. data/lib/prawn/repeater.rb +25 -13
  46. data/lib/prawn/stamp.rb +6 -29
  47. data/lib/prawn/text.rb +139 -121
  48. data/lib/prawn/text/box.rb +168 -102
  49. data/spec/bounding_box_spec.rb +7 -2
  50. data/spec/document_spec.rb +7 -5
  51. data/spec/font_spec.rb +9 -1
  52. data/spec/graphics_spec.rb +229 -0
  53. data/spec/object_store_spec.rb +5 -5
  54. data/spec/outline_spec.rb +229 -0
  55. data/spec/repeater_spec.rb +18 -1
  56. data/spec/snapshot_spec.rb +7 -7
  57. data/spec/span_spec.rb +6 -2
  58. data/spec/spec_helper.rb +7 -3
  59. data/spec/stamp_spec.rb +13 -0
  60. data/spec/text_at_spec.rb +119 -0
  61. data/spec/text_box_spec.rb +257 -4
  62. data/spec/text_spec.rb +278 -180
  63. data/vendor/pdf-inspector/lib/pdf/inspector/graphics.rb +12 -0
  64. metadata +16 -3
  65. data/lib/prawn/object_store.rb +0 -92
@@ -18,188 +18,218 @@ describe "#height_of" do
18
18
  @pdf.height_of("text", :width => 1)
19
19
  end.should.raise(Prawn::Errors::CannotFit)
20
20
  end
21
+
22
+ it "should raise Prawn::Errors::UnknownOption if :indent_paragraphs option is provided" do
23
+ lambda {
24
+ @pdf.height_of("hai", :width => 300,
25
+ :indent_paragraphs => 60)
26
+ }.should.raise(Prawn::Errors::UnknownOption)
27
+ end
28
+
29
+ it "should not raise Prawn::Errors::UnknownOption if :final_gap option is provided" do
30
+ lambda {
31
+ @pdf.height_of("hai", :width => 300,
32
+ :final_gap => true)
33
+ }.should.not.raise(Prawn::Errors::UnknownOption)
34
+ end
35
+
21
36
  end
22
37
 
23
- describe "when drawing text" do
24
-
25
- before(:each) { create_pdf }
26
-
27
- it "should advance down the document based on font_height" do
28
- position = @pdf.y
29
- @pdf.text "Foo"
30
-
31
- @pdf.y.should.be.close(position - @pdf.font.height, 0.0001)
32
-
33
- position = @pdf.y
34
- @pdf.text "Foo\nBar\nBaz"
35
- @pdf.y.should.be.close(position - 3*@pdf.font.height, 0.0001)
36
- end
37
-
38
- it "should advance down the document based on font_height" +
39
- " with size option" do
40
- position = @pdf.y
41
- @pdf.text "Foo", :size => 15
42
-
43
- @pdf.font_size = 15
44
- @pdf.y.should.be.close(position - @pdf.font.height, 0.0001)
45
-
46
- position = @pdf.y
47
- @pdf.text "Foo\nBar\nBaz"
48
- @pdf.y.should.be.close(position - 3*@pdf.font.height, 0.0001)
49
- end
50
-
51
- it "should advance down the document based on font_height" +
52
- " with leading option" do
53
- position = @pdf.y
54
- leading = 2
55
- @pdf.text "Foo", :leading => leading
56
-
57
- @pdf.y.should.be.close(position - @pdf.font.height - leading, 0.0001)
58
-
59
- position = @pdf.y
60
- @pdf.text "Foo\nBar\nBaz"
61
- @pdf.y.should.be.close(position - 3*@pdf.font.height, 0.0001)
62
- end
63
-
64
- it "should advance down the document based on font ascender only "+
65
- "if final_gap is given" do
66
- position = @pdf.y
67
- @pdf.text "Foo", :final_gap => false
68
-
69
- @pdf.y.should.be.close(position - @pdf.font.ascender, 0.0001)
70
-
71
- position = @pdf.y
72
- @pdf.text "Foo\nBar\nBaz", :final_gap => false
73
- @pdf.y.should.be.close(position - 2*@pdf.font.height - @pdf.font.ascender, 0.0001)
74
- end
75
-
76
- it "should not accept :align alongside :at" do
77
- assert_raises(ArgumentError) do
78
- @pdf.text "What could this mean?", :at => [100, 100], :align => :center
79
- end
80
- end
81
-
82
- it "should default to 12 point helvetica" do
83
- @pdf.text "Blah", :at => [100,100]
84
- text = PDF::Inspector::Text.analyze(@pdf.render)
85
- text.font_settings[0][:name].should == :Helvetica
86
- text.font_settings[0][:size].should == 12
87
- text.strings.first.should == "Blah"
88
- end
89
-
90
- it "should allow setting font size" do
91
- @pdf.text "Blah", :at => [100,100], :size => 16
92
- text = PDF::Inspector::Text.analyze(@pdf.render)
93
- text.font_settings[0][:size].should == 16
94
- end
95
-
96
- it "should allow setting a default font size" do
97
- @pdf.font_size = 16
98
- @pdf.text "Blah"
99
- text = PDF::Inspector::Text.analyze(@pdf.render)
100
- text.font_settings[0][:size].should == 16
101
- end
102
-
103
- it "should allow setting font size in DSL style" do
104
- @pdf.font_size 20
105
- @pdf.font_size.should == 20
106
- end
107
-
108
- it "should allow overriding default font for a single instance" do
109
- @pdf.font_size = 16
110
-
111
- @pdf.text "Blah", :size => 11
112
- @pdf.text "Blaz"
113
- text = PDF::Inspector::Text.analyze(@pdf.render)
114
- text.font_settings[0][:size].should == 11
115
- text.font_settings[1][:size].should == 16
116
- end
117
-
118
- it "should allow setting a font size transaction with a block" do
119
- @pdf.font_size 16 do
120
- @pdf.text 'Blah'
121
- end
122
-
123
- @pdf.text 'blah'
124
-
125
- text = PDF::Inspector::Text.analyze(@pdf.render)
126
- text.font_settings[0][:size].should == 16
127
- text.font_settings[1][:size].should == 12
128
- end
129
-
130
- it "should allow manual setting the font size " +
131
- "when in a font size block" do
132
- @pdf.font_size(16) do
133
- @pdf.text 'Foo'
134
- @pdf.text 'Blah', :size => 11
135
- @pdf.text 'Blaz'
136
- end
137
- text = PDF::Inspector::Text.analyze(@pdf.render)
138
- text.font_settings[0][:size].should == 16
139
- text.font_settings[1][:size].should == 11
140
- text.font_settings[2][:size].should == 16
141
- end
142
-
143
- it "should allow registering of built-in font_settings on the fly" do
144
- @pdf.font "Times-Roman"
145
- @pdf.text "Blah", :at => [100,100]
146
- @pdf.font "Courier"
147
- @pdf.text "Blaz", :at => [150,150]
148
- text = PDF::Inspector::Text.analyze(@pdf.render)
149
- text.font_settings[0][:name].should == :"Times-Roman"
150
- text.font_settings[1][:name].should == :Courier
151
- end
152
-
153
- it "should utilise the same default font across multiple pages" do
154
- @pdf.text "Blah", :at => [100,100]
155
- @pdf.start_new_page
156
- @pdf.text "Blaz", :at => [150,150]
157
- text = PDF::Inspector::Text.analyze(@pdf.render)
158
-
159
- text.font_settings.size.should == 2
160
- text.font_settings[0][:name].should == :Helvetica
161
- text.font_settings[1][:name].should == :Helvetica
162
- end
163
-
164
- it "should raise an exception when an unknown font is used" do
165
- lambda { @pdf.font "Pao bu" }.should.raise(Prawn::Errors::UnknownFont)
166
- end
167
-
168
- it "should correctly render a utf-8 string when using a built-in font" do
169
- str = "©" # copyright symbol
170
- @pdf.text str
171
-
172
- # grab the text from the rendered PDF and ensure it matches
173
- text = PDF::Inspector::Text.analyze(@pdf.render)
174
- text.strings.first.should == str
175
- end
176
-
177
- if "spec".respond_to?(:encode!)
178
- # Handle non utf-8 string encodings in a sane way on M17N aware VMs
179
- it "should raise an exception when a utf-8 incompatible string is rendered" do
180
- str = "Blah \xDD"
181
- str.force_encoding("ASCII-8BIT")
182
- lambda { @pdf.text str }.should.raise(ArgumentError)
183
- end
184
- it "should not raise an exception when a shift-jis string is rendered" do
185
- datafile = "#{Prawn::BASEDIR}/data/shift_jis_text.txt"
186
- sjis_str = File.open(datafile, "r:shift_jis") { |f| f.gets }
187
- @pdf.font("#{Prawn::BASEDIR}/data/fonts/gkai00mp.ttf")
188
- lambda { @pdf.text sjis_str }.should.not.raise(ArgumentError)
189
- end
190
- else
191
- # Handle non utf-8 string encodings in a sane way on non-M17N aware VMs
192
- it "should raise an exception when a corrupt utf-8 string is rendered" do
193
- str = "Blah \xDD"
194
- lambda { @pdf.text str }.should.raise(ArgumentError)
195
- end
196
- it "should raise an exception when a shift-jis string is rendered" do
197
- sjis_str = File.read("#{Prawn::BASEDIR}/data/shift_jis_text.txt")
198
- lambda { @pdf.text sjis_str }.should.raise(ArgumentError)
199
- end
200
- end
201
-
202
- it "should call move_past_bottom when printing more text than can fit between the current document.y and bounds.bottom" do
38
+ describe "#text" do
39
+ before(:each) { create_pdf }
40
+
41
+ it "should raise ArgumentError if :at option included" do
42
+ lambda { @pdf.text("hai", :at => [0, 0]) }.should.raise(ArgumentError)
43
+ end
44
+
45
+ it "should advance down the document based on font_height" do
46
+ position = @pdf.y
47
+ @pdf.text "Foo"
48
+
49
+ @pdf.y.should.be.close(position - @pdf.font.height, 0.0001)
50
+
51
+ position = @pdf.y
52
+ @pdf.text "Foo\nBar\nBaz"
53
+ @pdf.y.should.be.close(position - 3*@pdf.font.height, 0.0001)
54
+ end
55
+
56
+ it "should advance down the document based on font_height" +
57
+ " with size option" do
58
+ position = @pdf.y
59
+ @pdf.text "Foo", :size => 15
60
+
61
+ @pdf.font_size = 15
62
+ @pdf.y.should.be.close(position - @pdf.font.height, 0.0001)
63
+
64
+ position = @pdf.y
65
+ @pdf.text "Foo\nBar\nBaz"
66
+ @pdf.y.should.be.close(position - 3*@pdf.font.height, 0.0001)
67
+ end
68
+
69
+ it "should advance down the document based on font_height" +
70
+ " with leading option" do
71
+ position = @pdf.y
72
+ leading = 2
73
+ @pdf.text "Foo", :leading => leading
74
+
75
+ @pdf.y.should.be.close(position - @pdf.font.height - leading, 0.0001)
76
+
77
+ position = @pdf.y
78
+ @pdf.text "Foo\nBar\nBaz"
79
+ @pdf.y.should.be.close(position - 3*@pdf.font.height, 0.0001)
80
+ end
81
+
82
+ it "should advance down the document based on font ascender only "+
83
+ "if final_gap is given" do
84
+ position = @pdf.y
85
+ @pdf.text "Foo", :final_gap => false
86
+
87
+ @pdf.y.should.be.close(position - @pdf.font.ascender, 0.0001)
88
+
89
+ position = @pdf.y
90
+ @pdf.text "Foo\nBar\nBaz", :final_gap => false
91
+ @pdf.y.should.be.close(position - 2*@pdf.font.height - @pdf.font.ascender, 0.0001)
92
+ end
93
+
94
+ it "should default to 12 point helvetica" do
95
+ @pdf.text "Blah"
96
+ text = PDF::Inspector::Text.analyze(@pdf.render)
97
+ text.font_settings[0][:name].should == :Helvetica
98
+ text.font_settings[0][:size].should == 12
99
+ text.strings.first.should == "Blah"
100
+ end
101
+
102
+ it "should allow setting font size" do
103
+ @pdf.text "Blah", :size => 16
104
+ text = PDF::Inspector::Text.analyze(@pdf.render)
105
+ text.font_settings[0][:size].should == 16
106
+ end
107
+
108
+ it "should allow setting a default font size" do
109
+ @pdf.font_size = 16
110
+ @pdf.text "Blah"
111
+ text = PDF::Inspector::Text.analyze(@pdf.render)
112
+ text.font_settings[0][:size].should == 16
113
+ end
114
+
115
+ it "should allow overriding default font for a single instance" do
116
+ @pdf.font_size = 16
117
+
118
+ @pdf.text "Blah", :size => 11
119
+ @pdf.text "Blaz"
120
+ text = PDF::Inspector::Text.analyze(@pdf.render)
121
+ text.font_settings[0][:size].should == 11
122
+ text.font_settings[1][:size].should == 16
123
+ end
124
+
125
+ it "should allow setting a font size transaction with a block" do
126
+ @pdf.font_size 16 do
127
+ @pdf.text 'Blah'
128
+ end
129
+
130
+ @pdf.text 'blah'
131
+
132
+ text = PDF::Inspector::Text.analyze(@pdf.render)
133
+ text.font_settings[0][:size].should == 16
134
+ text.font_settings[1][:size].should == 12
135
+ end
136
+
137
+ it "should allow manual setting the font size " +
138
+ "when in a font size block" do
139
+ @pdf.font_size(16) do
140
+ @pdf.text 'Foo'
141
+ @pdf.text 'Blah', :size => 11
142
+ @pdf.text 'Blaz'
143
+ end
144
+ text = PDF::Inspector::Text.analyze(@pdf.render)
145
+ text.font_settings[0][:size].should == 16
146
+ text.font_settings[1][:size].should == 11
147
+ text.font_settings[2][:size].should == 16
148
+ end
149
+
150
+ it "should allow registering of built-in font_settings on the fly" do
151
+ @pdf.font "Times-Roman"
152
+ @pdf.text "Blah"
153
+ @pdf.font "Courier"
154
+ @pdf.text "Blaz"
155
+ text = PDF::Inspector::Text.analyze(@pdf.render)
156
+ text.font_settings[0][:name].should == :"Times-Roman"
157
+ text.font_settings[1][:name].should == :Courier
158
+ end
159
+
160
+ it "should utilise the same default font across multiple pages" do
161
+ @pdf.text "Blah"
162
+ @pdf.start_new_page
163
+ @pdf.text "Blaz"
164
+ text = PDF::Inspector::Text.analyze(@pdf.render)
165
+
166
+ text.font_settings.size.should == 2
167
+ text.font_settings[0][:name].should == :Helvetica
168
+ text.font_settings[1][:name].should == :Helvetica
169
+ end
170
+
171
+ it "should raise an exception when an unknown font is used" do
172
+ lambda { @pdf.font "Pao bu" }.should.raise(Prawn::Errors::UnknownFont)
173
+ end
174
+
175
+ it "should correctly render a utf-8 string when using a built-in font" do
176
+ str = "©" # copyright symbol
177
+ @pdf.text str
178
+
179
+ # grab the text from the rendered PDF and ensure it matches
180
+ text = PDF::Inspector::Text.analyze(@pdf.render)
181
+ text.strings.first.should == str
182
+ end
183
+
184
+ it "should correctly render a string with higher bit characters across" +
185
+ " a page break when using a built-in font" do
186
+ str = "©"
187
+ @pdf.move_cursor_to(@pdf.font.height)
188
+ @pdf.text(str + "\n" + str)
189
+
190
+ # grab the text from the rendered PDF and ensure it matches
191
+ text = PDF::Inspector::Text.analyze(@pdf.render)
192
+ text.strings[1].should == str.strip
193
+ end
194
+
195
+ it "should correctly render a string with higher bit characters across" +
196
+ " a page break when using a built-in font and :indent_paragraphs option" do
197
+ str = "©"
198
+ @pdf.move_cursor_to(@pdf.font.height)
199
+ @pdf.text(str + "\n" + str, :indent_paragraphs => 20)
200
+
201
+ # grab the text from the rendered PDF and ensure it matches
202
+ text = PDF::Inspector::Text.analyze(@pdf.render)
203
+ text.strings[1].should == str.strip
204
+ end
205
+
206
+ if "spec".respond_to?(:encode!)
207
+ # Handle non utf-8 string encodings in a sane way on M17N aware VMs
208
+ it "should raise an exception when a utf-8 incompatible string is rendered" do
209
+ str = "Blah \xDD"
210
+ str.force_encoding("ASCII-8BIT")
211
+ lambda { @pdf.text str }.should.raise(ArgumentError)
212
+ end
213
+ it "should not raise an exception when a shift-jis string is rendered" do
214
+ datafile = "#{Prawn::BASEDIR}/data/shift_jis_text.txt"
215
+ sjis_str = File.open(datafile, "r:shift_jis") { |f| f.gets }
216
+ @pdf.font("#{Prawn::BASEDIR}/data/fonts/gkai00mp.ttf")
217
+ lambda { @pdf.text sjis_str }.should.not.raise(ArgumentError)
218
+ end
219
+ else
220
+ # Handle non utf-8 string encodings in a sane way on non-M17N aware VMs
221
+ it "should raise an exception when a corrupt utf-8 string is rendered" do
222
+ str = "Blah \xDD"
223
+ lambda { @pdf.text str }.should.raise(ArgumentError)
224
+ end
225
+ it "should raise an exception when a shift-jis string is rendered" do
226
+ sjis_str = File.read("#{Prawn::BASEDIR}/data/shift_jis_text.txt")
227
+ lambda { @pdf.text sjis_str }.should.raise(ArgumentError)
228
+ end
229
+ end
230
+
231
+ it "should call move_past_bottom when printing more text than can fit" +
232
+ " between the current document.y and bounds.bottom" do
203
233
  @pdf.y = @pdf.font.height
204
234
  @pdf.text "Hello"
205
235
  @pdf.text "World"
@@ -208,4 +238,72 @@ describe "when drawing text" do
208
238
  pages[0][:strings].should == ["Hello"]
209
239
  pages[1][:strings].should == ["World"]
210
240
  end
241
+
242
+ it "should be able to use a custom word-wrap object" do
243
+ hello = "hello " * 25
244
+ world = "world " * 25
245
+ @pdf.text(hello + "\n" + world, :line_wrap => TestWordWrap.new)
246
+ text = PDF::Inspector::Text.analyze(@pdf.render)
247
+ text.strings[0].should == hello.strip
248
+ text.strings[1].should == world.strip
249
+ end
250
+
251
+ it "should be able to globally set the custom word-wrap object" do
252
+ hello = "hello " * 25
253
+ world = "world " * 25
254
+ @pdf.default_line_wrap = TestWordWrap.new
255
+ @pdf.text(hello + "\n" + world)
256
+ text = PDF::Inspector::Text.analyze(@pdf.render)
257
+ text.strings[0].should == hello.strip
258
+ text.strings[1].should == world.strip
259
+ end
260
+
261
+ describe "with :indent_paragraphs option" do
262
+ it "should indent the paragraphs" do
263
+ hello = "hello " * 50
264
+ hello2 = "hello " * 50
265
+ @pdf.text(hello + "\n" + hello2, :indent_paragraphs => 60)
266
+ text = PDF::Inspector::Text.analyze(@pdf.render)
267
+ text.strings[0].should == ("hello " * 19).strip
268
+ text.strings[1].should == ("hello " * 21).strip
269
+ text.strings[3].should == ("hello " * 19).strip
270
+ text.strings[4].should == ("hello " * 21).strip
271
+ end
272
+ describe "when wrap to new page, and first line of new page" +
273
+ " is not the start of a new paragraph, that line should" +
274
+ " not be indented" do
275
+ it "should indent the paragraphs" do
276
+ hello = "hello " * 50
277
+ hello2 = "hello " * 50
278
+ @pdf.move_cursor_to(@pdf.font.height)
279
+ @pdf.text(hello + "\n" + hello2, :indent_paragraphs => 60)
280
+ text = PDF::Inspector::Text.analyze(@pdf.render)
281
+ text.strings[0].should == ("hello " * 19).strip
282
+ text.strings[1].should == ("hello " * 21).strip
283
+ text.strings[3].should == ("hello " * 19).strip
284
+ text.strings[4].should == ("hello " * 21).strip
285
+ end
286
+ end
287
+ describe "when wrap to new page, and first line of new page" +
288
+ " is the start of a new paragraph, that line should" +
289
+ " be indented" do
290
+ it "should indent the paragraphs" do
291
+ hello = "hello " * 50
292
+ hello2 = "hello " * 50
293
+ @pdf.move_cursor_to(@pdf.font.height * 3)
294
+ @pdf.text(hello + "\n" + hello2, :indent_paragraphs => 60)
295
+ text = PDF::Inspector::Text.analyze(@pdf.render)
296
+ text.strings[0].should == ("hello " * 19).strip
297
+ text.strings[1].should == ("hello " * 21).strip
298
+ text.strings[3].should == ("hello " * 19).strip
299
+ text.strings[4].should == ("hello " * 21).strip
300
+ end
301
+ end
302
+ end
303
+ end
304
+
305
+ class TestWordWrap
306
+ def wrap_line(line, options)
307
+ line
308
+ end
211
309
  end