prawn-core 0.7.2 → 0.8.4

Sign up to get free protection for your applications and to get access to all the features.
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