prawn-core 0.6.3 → 0.7.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 (43) hide show
  1. data/Rakefile +1 -1
  2. data/examples/general/context_sensitive_headers.rb +37 -0
  3. data/examples/general/float.rb +11 -0
  4. data/examples/general/repeaters.rb +43 -0
  5. data/examples/m17n/chinese_text_wrapping.rb +1 -3
  6. data/examples/text/font_calculations.rb +6 -6
  7. data/examples/text/text_box.rb +80 -17
  8. data/lib/prawn/core.rb +3 -1
  9. data/lib/prawn/document/bounding_box.rb +9 -0
  10. data/lib/prawn/document/column_box.rb +13 -2
  11. data/lib/prawn/document/internals.rb +21 -3
  12. data/lib/prawn/document/snapshot.rb +7 -2
  13. data/lib/prawn/document/span.rb +3 -3
  14. data/lib/prawn/document.rb +78 -19
  15. data/lib/prawn/font/afm.rb +10 -7
  16. data/lib/prawn/font/ttf.rb +6 -4
  17. data/lib/prawn/font.rb +34 -24
  18. data/lib/prawn/graphics/cap_style.rb +5 -2
  19. data/lib/prawn/graphics/color.rb +117 -57
  20. data/lib/prawn/graphics/dash.rb +4 -2
  21. data/lib/prawn/graphics/join_style.rb +6 -3
  22. data/lib/prawn/graphics/transparency.rb +65 -18
  23. data/lib/prawn/images/jpg.rb +1 -1
  24. data/lib/prawn/images/png.rb +1 -1
  25. data/lib/prawn/object_store.rb +30 -1
  26. data/lib/prawn/reference.rb +25 -3
  27. data/lib/prawn/repeater.rb +117 -0
  28. data/lib/prawn/stamp.rb +102 -40
  29. data/lib/prawn/text/box.rb +344 -0
  30. data/lib/prawn/text.rb +255 -0
  31. data/spec/document_spec.rb +125 -4
  32. data/spec/object_store_spec.rb +33 -0
  33. data/spec/repeater_spec.rb +79 -0
  34. data/spec/stamp_spec.rb +8 -0
  35. data/spec/text_box_spec.rb +282 -69
  36. data/spec/text_spec.rb +49 -29
  37. data/spec/transparency_spec.rb +14 -0
  38. data/vendor/pdf-inspector/lib/pdf/inspector/graphics.rb +2 -2
  39. metadata +158 -155
  40. data/examples/general/measurement_units.pdf +0 -4667
  41. data/lib/prawn/document/text/box.rb +0 -90
  42. data/lib/prawn/document/text/wrapping.rb +0 -62
  43. data/lib/prawn/document/text.rb +0 -184
@@ -2,95 +2,220 @@
2
2
 
3
3
  require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
4
4
 
5
- describe "A text box" do
6
5
 
7
- before(:each) do
8
- create_pdf
9
- @x = 100
10
- @y = 125
11
- @width = 50
12
- @height = @pdf.font.height * 10
13
- @text = "Oh hai text box. " * 200
6
+ describe "Text::Box" do
7
+ it "should not fail if height is smaller than 1 line" do
8
+ create_pdf
9
+ @text = "Oh hai text rect. " * 10
10
+ @options = {
11
+ :height => @pdf.font.height * 0.5,
12
+ :document => @pdf
13
+ }
14
+ text_box = Prawn::Text::Box.new(@text, @options)
15
+ text_box.render
16
+ text_box.text.should == ""
14
17
  end
18
+ end
15
19
 
16
- it "should have a truncated text" do
17
- @overflow = :truncate
18
- create_text_box
19
- @box.render
20
- @box.text.should == "Oh hai\ntext box.\n" * 5
20
+ describe "Text::Box#render" do
21
+ it "should draw content to the page" do
22
+ create_pdf
23
+ @text = "Oh hai text rect. " * 10
24
+ @options = { :document => @pdf }
25
+ text_box = Prawn::Text::Box.new(@text, @options)
26
+ text_box.render()
27
+ text = PDF::Inspector::Text.analyze(@pdf.render)
28
+ text.strings.should.not.be.empty
21
29
  end
30
+ end
22
31
 
23
- it "render should return truncated text (NOTE: which may have had its whitespace mangled by wrap/unwrap)" do
24
- @text = "Oh hai text box. " * 25
25
- @overflow = :truncate
26
- create_text_box
27
- excess_text = @box.render
28
- excess_text.should == "Oh hai text box. " * 20
32
+ describe "Text::Box#render(:dry_run => true)" do
33
+ it "should not draw any content to the page" do
34
+ create_pdf
35
+ @text = "Oh hai text rect. " * 10
36
+ @options = { :document => @pdf }
37
+ text_box = Prawn::Text::Box.new(@text, @options)
38
+ text_box.render(:dry_run => true)
39
+ text = PDF::Inspector::Text.analyze(@pdf.render)
40
+ text.strings.should.be.empty
29
41
  end
42
+ end
30
43
 
31
- it "render should attempt to preserve double newlines in excess text before returning it" do
32
- line = "Oh hai text box. "
33
- @text = line * 10 + "\n\n" + line * 10
34
- @overflow = :truncate
35
- create_text_box
36
- excess_text = @box.render
37
- excess_text.should == line * 5 + "\n\n" + line * 10
44
+ describe "Text::Box default height" do
45
+ it "should be the height from the bottom bound to document.y" do
46
+ create_pdf
47
+ target_height = @pdf.y - @pdf.bounds.bottom
48
+ @text = "Oh hai\n" * 60
49
+ @options = { :document => @pdf }
50
+ text_box = Prawn::Text::Box.new(@text, @options)
51
+ text_box.render
52
+ text_box.height.should.be.close(target_height, @pdf.font.height)
38
53
  end
54
+ end
39
55
 
40
- it "render should attempt to preserve single newlines in excess text before returning it" do
41
- line = "Oh hai text box. "
42
- @text = line * 10 + "\n" + line * 10
43
- @overflow = :truncate
44
- create_text_box
45
- excess_text = @box.render
46
- excess_text.should == line * 5 + "\n" + line * 10
56
+ describe "Text::Box default at" do
57
+ it "should be the left corner of the bounds, and the current document.y" do
58
+ create_pdf
59
+ target_at = [@pdf.bounds.left, @pdf.y]
60
+ @text = "Oh hai text rect. " * 100
61
+ @options = { :document => @pdf }
62
+ text_box = Prawn::Text::Box.new(@text, @options)
63
+ text_box.render
64
+ text_box.at.should == target_at
47
65
  end
66
+ end
48
67
 
49
- it "should have a height equal to @height" do
50
- @overflow = :truncate
51
- create_text_box
52
- @box.render
53
- @box.height.should == @height
68
+ describe "Text::Box with text than can fit in the box" do
69
+ before(:each) do
70
+ create_pdf
71
+ @text = "Oh hai text rect. " * 10
72
+ @options = {
73
+ :width => 162.0,
74
+ :height => 162.0,
75
+ :document => @pdf
76
+ }
77
+ end
78
+
79
+ it "printed text should match requested text, except for trailing or leading white space and that spaces may be replaced by newlines" do
80
+ text_box = Prawn::Text::Box.new(@text, @options)
81
+ text_box.render
82
+ text_box.text.gsub("\n", " ").should == @text.strip
83
+ end
84
+
85
+ it "render should return an empty string because no text remains unprinted" do
86
+ text_box = Prawn::Text::Box.new(@text, @options)
87
+ text_box.render.should == ""
54
88
  end
55
89
 
56
- it "should add ... to for overflow :ellipses" do
57
- @overflow = :ellipses
58
- @height = @pdf.font.height * 2
59
- @text = "Oh hai text box.\n" * 4
60
- create_text_box
61
- @box.render
62
- @box.text.should == "Oh hai\ntext bo..."
90
+ it "should be truncated when the leading is set high enough to prevent all the lines from being printed" do
91
+ @options[:leading] = 40
92
+ text_box = Prawn::Text::Box.new(@text, @options)
93
+ text_box.render
94
+ text_box.text.gsub("\n", " ").should.not == @text.strip
63
95
  end
96
+ end
64
97
 
65
- it "should not fail if height is smaller than 1 line" do
66
- @text = "a b c d e fgi"
67
- @width = 30
68
- @height = @pdf.font.height * 0.5
69
- @overflow = :ellipses
70
- create_text_box
71
- @box.render
72
- @box.text.should == ""
73
- end
74
-
75
- it "should keep text intact for overflow :expand" do
76
- @overflow = :expand
77
- @text = "Oh hai text box.\n" * 4
78
- @height = 0
79
- create_text_box
80
- @box.render
81
- @box.text.should == "Oh hai\ntext box.\n" * 4
82
- @box.height.should == 0
98
+ describe "Text::Box with text than can fit in the box with :ellipses overflow and :valign => :bottom" do
99
+ it "should not print ellipses" do
100
+ create_pdf
101
+ @text = "Oh hai text rect. " * 10
102
+ @options = {
103
+ :width => 162.0,
104
+ :height => 162.0,
105
+ :overflow => :ellipses,
106
+ :valign => :bottom,
107
+ :document => @pdf
108
+ }
109
+ @text_box = Prawn::Text::Box.new(@text, @options)
110
+ @text_box.render
111
+ @text_box.text.should.not =~ /\.\.\./
112
+ end
113
+ end
114
+
115
+ describe "Text::Box with more text than can fit in the box" do
116
+ before(:each) do
117
+ create_pdf
118
+ @text = "Oh hai text rect. " * 30
119
+ @bounding_height = 162.0
120
+ @options = {
121
+ :width => 162.0,
122
+ :height => @bounding_height,
123
+ :document => @pdf
124
+ }
125
+ end
126
+
127
+ context "truncated overflow" do
128
+ before(:each) do
129
+ @options[:overflow] = :truncate
130
+ @text_box = Prawn::Text::Box.new(@text, @options)
131
+ end
132
+ it "should not display ellipses" do
133
+ @text_box.render
134
+ @text_box.text.should.not =~ /\.\.\./
135
+ end
136
+ it "should be truncated" do
137
+ @text_box.render
138
+ @text_box.text.gsub("\n", " ").should.not == @text.strip
139
+ end
140
+ it "render should not return an empty string because some text remains unprinted" do
141
+ @text_box.render.should.not == ""
142
+ end
143
+ it "#height should be no taller than the specified height" do
144
+ @text_box.render
145
+ @text_box.height.should.be <= @bounding_height
146
+ end
147
+ it "#height should be within one font height of the specified height" do
148
+ @text_box.render
149
+ @bounding_height.should.be.close(@text_box.height, @pdf.font.height)
150
+ end
83
151
  end
84
152
 
85
- def create_text_box
86
- @box = Prawn::Document::Text::Box.new(@text,
87
- :width => @width, :height => @height,
88
- :overflow => @overflow,
89
- :at => [@x, @y],
90
- :for => @pdf)
153
+ context "ellipses overflow" do
154
+ before(:each) do
155
+ @options[:overflow] = :ellipses
156
+ @text_box = Prawn::Text::Box.new(@text, @options)
157
+ end
158
+ it "should display ellipses" do
159
+ @text_box.render
160
+ @text_box.text.should =~ /\.\.\./
161
+ end
162
+ it "render should not return an empty string because some text remains unprinted" do
163
+ @text_box.render.should.not == ""
164
+ end
165
+ end
166
+
167
+ context "expand overflow" do
168
+ before(:each) do
169
+ @options[:overflow] = :expand
170
+ @text_box = Prawn::Text::Box.new(@text, @options)
171
+ end
172
+ it "height should expand to encompass all the text (but not exceed the height of the page)" do
173
+ @text_box.render
174
+ @text_box.height.should > @bounding_height
175
+ end
176
+ it "should display the entire string (as long as there was space remaining on the page to print all the text)" do
177
+ @text_box.render
178
+ @text_box.text.gsub("\n", " ").should == @text.strip
179
+ end
180
+ it "render should return an empty string because no text remains unprinted(as long as there was space remaining on the page to print all the text)" do
181
+ @text_box.render.should == ""
182
+ end
183
+ end
184
+
185
+ context "shrink_to_fit overflow" do
186
+ before(:each) do
187
+ @options[:overflow] = :shrink_to_fit
188
+ @options[:min_font_size] = 2
189
+ @text_box = Prawn::Text::Box.new(@text, @options)
190
+ end
191
+ it "should display the entire text" do
192
+ @text_box.render
193
+ @text_box.text.gsub("\n", " ").should == @text.strip
194
+ end
195
+ it "render should return an empty string because no text remains unprinted" do
196
+ @text_box.render.should == ""
197
+ end
198
+ end
199
+ end
200
+
201
+ describe "Text::Box with a solid block of Chinese characters" do
202
+ it "printed text should match requested text, except for newlines" do
203
+ create_pdf
204
+ @text = "写中国字" * 10
205
+ @options = {
206
+ :width => 162.0,
207
+ :height => 162.0,
208
+ :document => @pdf
209
+ }
210
+ @pdf.font "#{Prawn::BASEDIR}/data/fonts/gkai00mp.ttf"
211
+ @options[:overflow] = :truncate
212
+ text_box = Prawn::Text::Box.new(@text, @options)
213
+ text_box.render
214
+ text_box.text.gsub("\n", "").should == @text.strip
91
215
  end
92
216
  end
93
217
 
218
+
94
219
  describe "drawing bounding boxes" do
95
220
 
96
221
  before(:each) { create_pdf }
@@ -107,3 +232,91 @@ describe "drawing bounding boxes" do
107
232
  end
108
233
 
109
234
 
235
+ describe 'Text::Box wrapping' do
236
+
237
+
238
+ it "should wrap text" do
239
+ text = "Please wrap this text about HERE. More text that should be wrapped"
240
+ expect = "Please wrap this text about\nHERE. More text that should be\nwrapped"
241
+
242
+ @pdf = Prawn::Document.new
243
+ @pdf.font "Courier"
244
+ @text_box = Prawn::Text::Box.new(text,
245
+ :width => 220,
246
+ :overflow => :expand,
247
+ :document => @pdf)
248
+ @text_box.render
249
+ @text_box.text.should == expect
250
+ end
251
+
252
+ it "should respect end of line when wrapping text" do
253
+ text = "Please wrap only before\nTHIS word. Don't wrap this"
254
+ expect = text
255
+
256
+ @pdf = Prawn::Document.new
257
+ @pdf.font "Courier"
258
+ @text_box = Prawn::Text::Box.new(text,
259
+ :width => 220,
260
+ :overflow => :expand,
261
+ :document => @pdf)
262
+ @text_box.render
263
+ @text_box.text.should == expect
264
+ end
265
+
266
+ it "should respect multiple newlines when wrapping text" do
267
+ text = "Please wrap only before THIS\n\nword. Don't wrap this"
268
+ expect= "Please wrap only before\nTHIS\n\nword. Don't wrap this"
269
+
270
+ @pdf = Prawn::Document.new
271
+ @pdf.font "Courier"
272
+ @text_box = Prawn::Text::Box.new(text,
273
+ :width => 200,
274
+ :overflow => :expand,
275
+ :document => @pdf)
276
+ @text_box.render
277
+ @text_box.text.should == expect
278
+ end
279
+
280
+ it "should respect multiple newlines when wrapping text when those newlines coincide with a line break" do
281
+ text = "Please wrap only before\n\nTHIS word. Don't wrap this"
282
+ expect = text
283
+
284
+ @pdf = Prawn::Document.new
285
+ @pdf.font "Courier"
286
+ @text_box = Prawn::Text::Box.new(text,
287
+ :width => 220,
288
+ :overflow => :expand,
289
+ :document => @pdf)
290
+ @text_box.render
291
+ @text_box.text.should == expect
292
+ end
293
+
294
+ it "should respect initial newlines" do
295
+ text = "\nThis should be on line 2"
296
+ expect = text
297
+
298
+ @pdf = Prawn::Document.new
299
+ @pdf.font "Courier"
300
+ @text_box = Prawn::Text::Box.new(text,
301
+ :width => 220,
302
+ :overflow => :expand,
303
+ :document => @pdf)
304
+ @text_box.render
305
+ @text_box.text.should == expect
306
+ end
307
+
308
+ it "should wrap lines comprised of a single word of the bounds when wrapping text" do
309
+ text = "You_can_wrap_this_text_HERE"
310
+ expect = "You_can_wrap_this_text_HE\nRE"
311
+
312
+ @pdf = Prawn::Document.new
313
+ @pdf.font "Courier"
314
+ @text_box = Prawn::Text::Box.new(text,
315
+ :width => 180,
316
+ :overflow => :expand,
317
+ :document => @pdf)
318
+ @text_box.render
319
+ @text_box.text.should == expect
320
+ end
321
+
322
+ end
data/spec/text_spec.rb CHANGED
@@ -1,10 +1,22 @@
1
1
  # encoding: utf-8
2
2
 
3
- require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
3
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
4
+
5
+ describe "#height_of" do
6
+ before(:each) { create_pdf }
7
+
8
+ it "should return the height that would be required to print a" +
9
+ "particular string of text" do
10
+ original_y = @pdf.y
11
+ @pdf.text("Foo")
12
+ new_y = @pdf.y
13
+ @pdf.height_of("Foo", :width => 300).should.be.close(original_y - new_y, 0.0001)
14
+ end
15
+ end
4
16
 
5
17
  describe "when drawing text" do
6
18
 
7
- before(:each) { create_pdf }
19
+ before(:each) { create_pdf }
8
20
 
9
21
  it "should advance down the document based on font_height" do
10
22
  position = @pdf.y
@@ -16,6 +28,32 @@ describe "when drawing text" do
16
28
  @pdf.text "Foo\nBar\nBaz"
17
29
  @pdf.y.should.be.close(position - 3*@pdf.font.height, 0.0001)
18
30
  end
31
+
32
+ it "should advance down the document based on font_height" +
33
+ " with size option" do
34
+ position = @pdf.y
35
+ @pdf.text "Foo", :size => 15
36
+
37
+ @pdf.font_size = 15
38
+ @pdf.y.should.be.close(position - @pdf.font.height, 0.0001)
39
+
40
+ position = @pdf.y
41
+ @pdf.text "Foo\nBar\nBaz"
42
+ @pdf.y.should.be.close(position - 3*@pdf.font.height, 0.0001)
43
+ end
44
+
45
+ it "should advance down the document based on font_height" +
46
+ " with leading option" do
47
+ position = @pdf.y
48
+ leading = 2
49
+ @pdf.text "Foo", :leading => leading
50
+
51
+ @pdf.y.should.be.close(position - @pdf.font.height - leading, 0.0001)
52
+
53
+ position = @pdf.y
54
+ @pdf.text "Foo\nBar\nBaz"
55
+ @pdf.y.should.be.close(position - 3*@pdf.font.height, 0.0001)
56
+ end
19
57
 
20
58
  it "should advance down the document based on font ascender only "+
21
59
  "if final_gap is given" do
@@ -153,33 +191,15 @@ describe "when drawing text" do
153
191
  sjis_str = File.read("#{Prawn::BASEDIR}/data/shift_jis_text.txt")
154
192
  lambda { @pdf.text sjis_str }.should.raise(ArgumentError)
155
193
  end
156
- end
157
-
158
- it "should wrap text" do
159
- @pdf = Prawn::Document.new
160
- @pdf.font "Courier"
161
-
162
- text = "Please wrap this text about HERE. More text that should be wrapped"
163
- expect = "Please wrap this text about\nHERE. More text that should be\nwrapped"
164
-
165
- @pdf.naive_wrap(text, 220, @pdf.font_size).should == expect
166
- end
194
+ end
167
195
 
168
- it "should respect end of line when wrapping text" do
169
- @pdf = Prawn::Document.new
170
- @pdf.font "Courier"
171
- text = "Please wrap only before\nTHIS word. Don't wrap this"
172
- @pdf.naive_wrap(text, 220, @pdf.font_size).should == text
196
+ it "should call move_past_bottom when printing more text than can fit between the current document.y and bounds.bottom" do
197
+ @pdf.y = @pdf.font.height
198
+ @pdf.text "Hello"
199
+ @pdf.text "World"
200
+ pages = PDF::Inspector::Page.analyze(@pdf.render).pages
201
+ pages.size.should == 2
202
+ pages[0][:strings].should == ["Hello"]
203
+ pages[1][:strings].should == ["World"]
173
204
  end
174
-
175
- it "should respect end of line when wrapping text and mode is set to 'character'" do
176
- @pdf = Prawn::Document.new
177
- @pdf.font "Courier"
178
-
179
- text = "You can wrap this text HERE"
180
- expect = "You can wrap this text HE\nRE"
181
-
182
- @pdf.naive_wrap(text, 180, @pdf.font_size, :mode => :character).should == expect
183
- end
184
-
185
205
  end
@@ -44,6 +44,20 @@ describe "Document with transparency" do
44
44
  extgstate[:opacity].should == 0.5
45
45
  extgstate[:stroke_opacity].should == 0.2
46
46
  end
47
+
48
+ it "should enforce the valid range of 0.0 to 1.0" do
49
+ create_pdf
50
+ @pdf.transparent(-0.5, -0.2)
51
+ extgstate = PDF::Inspector::ExtGState.analyze(@pdf.render).extgstates[0]
52
+ extgstate[:opacity].should == 0.0
53
+ extgstate[:stroke_opacity].should == 0.0
54
+
55
+ create_pdf
56
+ @pdf.transparent(2.0, 3.0)
57
+ extgstate = PDF::Inspector::ExtGState.analyze(@pdf.render).extgstates[0]
58
+ extgstate[:opacity].should == 1.0
59
+ extgstate[:stroke_opacity].should == 1.0
60
+ end
47
61
 
48
62
  describe "with more than one page" do
49
63
  it "the extended graphic state resource should be added to both pages" do
@@ -64,12 +64,12 @@ module PDF
64
64
  @fill_color_count = 0
65
65
  end
66
66
 
67
- def set_rgb_color_for_stroking(*params)
67
+ def set_color_for_stroking_and_special(*params)
68
68
  @stroke_color_count += 1
69
69
  @stroke_color = params
70
70
  end
71
71
 
72
- def set_rgb_color_for_nonstroking(*params)
72
+ def set_color_for_nonstroking_and_special(*params)
73
73
  @fill_color_count += 1
74
74
  @fill_color = params
75
75
  end