prawn-core 0.5.1 → 0.6.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 (58) hide show
  1. data/HACKING +46 -0
  2. data/README +9 -3
  3. data/Rakefile +7 -6
  4. data/examples/bounding_box/stretched_nesting.rb +67 -0
  5. data/examples/general/margin.rb +36 -0
  6. data/examples/general/multi_page_layout.rb +3 -1
  7. data/examples/general/page_numbering.rb +15 -0
  8. data/examples/general/stamp.rb +45 -0
  9. data/examples/graphics/stroke_cap_and_join.rb +45 -0
  10. data/examples/graphics/stroke_dash.rb +42 -0
  11. data/examples/graphics/transparency.rb +26 -0
  12. data/examples/text/text_box_returning_excess.rb +51 -0
  13. data/lib/prawn/byte_string.rb +7 -0
  14. data/lib/prawn/core.rb +7 -8
  15. data/lib/prawn/document/annotations.rb +3 -2
  16. data/lib/prawn/document/bounding_box.rb +15 -10
  17. data/lib/prawn/document/column_box.rb +1 -3
  18. data/lib/prawn/document/destinations.rb +11 -10
  19. data/lib/prawn/document/internals.rb +62 -19
  20. data/lib/prawn/document/snapshot.rb +71 -0
  21. data/lib/prawn/document/text/box.rb +7 -0
  22. data/lib/prawn/document/text/wrapping.rb +3 -0
  23. data/lib/prawn/document/text.rb +9 -2
  24. data/lib/prawn/document.rb +141 -25
  25. data/lib/prawn/errors.rb +12 -0
  26. data/lib/prawn/font/afm.rb +1 -1
  27. data/lib/prawn/font/ttf.rb +5 -5
  28. data/lib/prawn/font.rb +8 -5
  29. data/lib/prawn/graphics/cap_style.rb +35 -0
  30. data/lib/prawn/graphics/dash.rb +69 -0
  31. data/lib/prawn/graphics/join_style.rb +35 -0
  32. data/lib/prawn/graphics/transparency.rb +56 -0
  33. data/lib/prawn/graphics.rb +9 -1
  34. data/lib/prawn/images.rb +4 -4
  35. data/lib/prawn/name_tree.rb +2 -1
  36. data/lib/prawn/object_store.rb +63 -0
  37. data/lib/prawn/pdf_object.rb +4 -0
  38. data/lib/prawn/reference.rb +18 -5
  39. data/lib/prawn/stamp.rb +87 -0
  40. data/spec/bounding_box_spec.rb +9 -0
  41. data/spec/document_spec.rb +58 -5
  42. data/spec/images_spec.rb +1 -1
  43. data/spec/name_tree_spec.rb +14 -5
  44. data/spec/object_store_spec.rb +42 -0
  45. data/spec/pdf_object_spec.rb +5 -0
  46. data/spec/reference_spec.rb +40 -0
  47. data/spec/snapshot_spec.rb +115 -0
  48. data/spec/spec_helper.rb +1 -4
  49. data/spec/stamp_spec.rb +98 -0
  50. data/spec/stroke_styles_spec.rb +152 -0
  51. data/spec/text_box_spec.rb +26 -0
  52. data/spec/text_spec.rb +8 -1
  53. data/spec/transparency_spec.rb +61 -0
  54. data/vendor/pdf-inspector/lib/pdf/inspector/extgstate.rb +18 -0
  55. data/vendor/pdf-inspector/lib/pdf/inspector/graphics.rb +40 -1
  56. data/vendor/pdf-inspector/lib/pdf/inspector/page.rb +12 -3
  57. data/vendor/pdf-inspector/lib/pdf/inspector.rb +2 -1
  58. metadata +26 -2
data/spec/images_spec.rb CHANGED
@@ -24,7 +24,7 @@ describe "the image() function" do
24
24
  it "should return the image info object" do
25
25
  info = @pdf.image(@filename)
26
26
 
27
- assert info.kind_of?(Prawn::Images::JPG)
27
+ info.should.be.kind_of(Prawn::Images::JPG)
28
28
 
29
29
  info.height.should == 453
30
30
  end
@@ -19,7 +19,9 @@ def tree_value(name, value)
19
19
  end
20
20
 
21
21
  class RefExposingDocument < Prawn::Document
22
- attr_reader :objects
22
+ def object_store
23
+ @store
24
+ end
23
25
  end
24
26
 
25
27
  describe "Name Tree" do
@@ -43,20 +45,20 @@ describe "Name Tree" do
43
45
  end
44
46
 
45
47
  it "should create a two new references when root is split" do
46
- ref_count = @pdf.objects.length
48
+ ref_count = @pdf.object_store.length
47
49
  node = Prawn::NameTree::Node.new(@pdf, 3)
48
50
  tree_add(node, ["one", 1], ["two", 2], ["three", 3], ["four", 4])
49
- @pdf.objects.length.should.equal ref_count+2
51
+ @pdf.object_store.length.should.equal ref_count+2
50
52
  end
51
53
 
52
54
  it "should create a one new reference when subtree is split" do
53
55
  node = Prawn::NameTree::Node.new(@pdf, 3)
54
56
  tree_add(node, ["one", 1], ["two", 2], ["three", 3], ["four", 4])
55
57
 
56
- ref_count = @pdf.objects.length # save when root is split
58
+ ref_count = @pdf.object_store.length # save when root is split
57
59
  tree_add(node, ["five", 5], ["six", 6], ["seven", 7])
58
60
  tree_dump(node).should == "[[five=5,four=4,one=1],[seven=7,six=6],[three=3,two=2]]"
59
- @pdf.objects.length.should.equal ref_count+1
61
+ @pdf.object_store.length.should.equal ref_count+1
60
62
  end
61
63
 
62
64
  it "should keep tree balanced when subtree split cascades to root" do
@@ -66,6 +68,13 @@ describe "Name Tree" do
66
68
  tree_dump(node).should == "[[[eight=8,five=5],[four=4,one=1]],[[seven=7,six=6],[three=3,two=2]]]"
67
69
  end
68
70
 
71
+ it "should maintain order of already properly ordered nodes" do
72
+ node = Prawn::NameTree::Node.new(@pdf, 3)
73
+ tree_add(node, ["eight", 8], ["five", 5], ["four", 4], ["one", 1])
74
+ tree_add(node, ['seven', 7], ['six', 6], ['three', 3], ['two', 2])
75
+ tree_dump(node).should == "[[[eight=8,five=5],[four=4,one=1]],[[seven=7,six=6],[three=3,two=2]]]"
76
+ end
77
+
69
78
  it "should emit only :Names key with to_hash if root is only node" do
70
79
  node = Prawn::NameTree::Node.new(@pdf, 3)
71
80
  tree_add(node, ["one", 1], ["two", 2], ["three", 3])
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
4
+
5
+ describe "Prawn::ObjectStore" do
6
+ before(:each) do
7
+ @store = Prawn::ObjectStore.new
8
+ end
9
+
10
+ it "should create required roots by default, including info passed to new" do
11
+ store = Prawn::ObjectStore.new(:Test => 3)
12
+ store.size.should == 3 # 3 default roots
13
+ store.info.data[:Test].should == 3
14
+ store.pages.data[:Count].should == 0
15
+ store.root.data[:Pages].should == store.pages
16
+ end
17
+
18
+ it "should add to its objects when ref() is called" do
19
+ count = @store.size
20
+ @store.ref("blah")
21
+ @store.size.should == count + 1
22
+ end
23
+
24
+ it "should accept push with a Prawn::Reference" do
25
+ r = Prawn::Reference(123, "blah")
26
+ @store.push(r)
27
+ @store[r.identifier].should == r
28
+ end
29
+
30
+ it "should accept arbitrary data and use it to create a Prawn::Reference" do
31
+ @store.push(123, "blahblah")
32
+ @store[123].data.should == "blahblah"
33
+ end
34
+
35
+ it "should be Enumerable, yielding in order of submission" do
36
+ # higher IDs to bypass the default roots
37
+ [10, 11, 12].each do |id|
38
+ @store.push(id, "some data #{id}")
39
+ end
40
+ @store.map{|ref| ref.identifier}[-3..-1].should == [10, 11, 12]
41
+ end
42
+ end
@@ -34,6 +34,11 @@ describe "PDF Object Serialization" do
34
34
  s_utf16 = "\xFE\xFF" + s.unpack("U*").pack("n*")
35
35
  PDF::Inspector.parse(Prawn::PdfObject(s, false)).should == s_utf16
36
36
  end
37
+
38
+ it "should pass through bytes regardless of content stream status for ByteString" do
39
+ Prawn::PdfObject(Prawn::ByteString.new("\xDE\xAD\xBE\xEF")).upcase.
40
+ should == "<DEADBEEF>"
41
+ end
37
42
 
38
43
  it "should escape parens when converting from Ruby string to PDF" do
39
44
  s = 'I )(can has a string'
@@ -39,4 +39,44 @@ describe "A Reference object" do
39
39
  "compressed stream expected to be smaller than source but wasn't"
40
40
  cref.data[:Filter].should == :FlateDecode
41
41
  end
42
+
43
+ it "should copy the data and stream from another ref on #replace" do
44
+ from = Prawn::Reference(3, {:foo => 'bar'})
45
+ from << "has a stream too"
46
+
47
+ to = Prawn::Reference(4, {:foo => 'baz'})
48
+ to.replace from
49
+
50
+ # should preserve identifier but copy data and stream
51
+ to.identifier.should == 4
52
+ to.data.should == from.data
53
+ to.stream.should == from.stream
54
+ end
55
+
56
+ it "should copy a compressed stream from a compressed ref on #replace" do
57
+ from = Prawn::Reference(5, {:foo => 'bar'})
58
+ from << "has a stream too " * 20
59
+ from.compress_stream
60
+
61
+ to = Prawn::Reference(6, {:foo => 'baz'})
62
+ to.replace from
63
+
64
+ to.identifier.should == 6
65
+ to.data.should == from.data
66
+ to.stream.should == from.stream
67
+ to.compressed?.should == true
68
+ end
69
+
70
+ describe "generated via Prawn::Document" do
71
+ it "should return a proper reference on ref!" do
72
+ pdf = Prawn::Document.new
73
+ pdf.ref!({}).is_a?(Prawn::Reference).should == true
74
+ end
75
+
76
+ it "should return an identifier on ref" do
77
+ pdf = Prawn::Document.new
78
+ r = pdf.ref({})
79
+ r.is_a?(Integer).should == true
80
+ end
81
+ end
42
82
  end
@@ -0,0 +1,115 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
4
+
5
+ describe "Prawn::Document#transaction" do
6
+
7
+ it "should properly commit if no error is raised" do
8
+ pdf = Prawn::Document.new do
9
+ transaction do
10
+ text "This is shown"
11
+ end
12
+ end
13
+ text = PDF::Inspector::Text.analyze(pdf.render)
14
+ text.strings.should == ["This is shown"]
15
+ end
16
+
17
+ it "should not display text if transaction is rolled back" do
18
+ pdf = Prawn::Document.new do
19
+ transaction do
20
+ text "This is not shown"
21
+ rollback
22
+ end
23
+ end
24
+ text = PDF::Inspector::Text.analyze(pdf.render)
25
+ text.strings.should == []
26
+ end
27
+
28
+ it "should return true/false value indicating success of the transaction" do
29
+ Prawn::Document.new do
30
+ success = transaction { }
31
+ success.should == true
32
+
33
+ success = transaction { rollback }
34
+ success.should == false
35
+ end
36
+ end
37
+
38
+ it "should support nested transactions" do
39
+ pdf = Prawn::Document.new do
40
+ transaction do
41
+ text "This is shown"
42
+ transaction do
43
+ text "and this is not"
44
+ rollback
45
+ end
46
+ text "and this is"
47
+ end
48
+ end
49
+ text = PDF::Inspector::Text.analyze(pdf.render)
50
+ text.strings.should == ["This is shown", "and this is"]
51
+ end
52
+
53
+ it "should allow rollback of multiple pages" do
54
+ pdf = Prawn::Document.new do
55
+ transaction do
56
+ 5.times { start_new_page }
57
+ text "way out there and will never be shown"
58
+ rollback
59
+ end
60
+ text "This is the real text, only one page"
61
+ end
62
+
63
+ pages = PDF::Inspector::Page.analyze(pdf.render).pages
64
+ pages.size.should == 1
65
+ end
66
+
67
+ # Because the Pages object, when restored, points to the snapshotted pages
68
+ # by identifier, we have to restore the snapshot into the same page objects,
69
+ # or else old pages will appear in the post-rollback document.
70
+ it "should restore the pages into the same objects" do
71
+ Prawn::Document.new do
72
+ old_page_object_id = current_page.identifier
73
+ old_page_content_id = page_content.identifier
74
+
75
+ transaction do
76
+ start_new_page
77
+ rollback
78
+ end
79
+
80
+ current_page.identifier.should == old_page_object_id
81
+ page_content.identifier.should == old_page_content_id
82
+ end
83
+
84
+
85
+ end
86
+
87
+
88
+ describe "with a stamp dictionary present" do
89
+
90
+ it "should properly commit if no error is raised" do
91
+ pdf = Prawn::Document.new do
92
+ create_stamp("test_stamp") { text "This is shown", :at => [0,0] }
93
+ transaction do
94
+ stamp("test_stamp")
95
+ end
96
+ end
97
+ pdf.render.should =~ /\/Stamp1 Do/
98
+ end
99
+
100
+ it "should properly rollback when #rollback is called" do
101
+ pdf = Prawn::Document.new do
102
+ create_stamp("test_stamp") { text "This is not shown", :at => [0,0] }
103
+
104
+ transaction do
105
+ stamp("test_stamp")
106
+ rollback
107
+ end
108
+ end
109
+ pdf.render.should.not =~ /\/Stamp1 Do/
110
+ end
111
+
112
+ end
113
+
114
+ end
115
+
data/spec/spec_helper.rb CHANGED
@@ -16,8 +16,5 @@ require "pdf/reader"
16
16
  require "pdf/inspector"
17
17
 
18
18
  def create_pdf(klass=Prawn::Document)
19
- @pdf = klass.new(:left_margin => 0,
20
- :right_margin => 0,
21
- :top_margin => 0,
22
- :bottom_margin => 0)
19
+ @pdf = klass.new(:margin => 0)
23
20
  end
@@ -0,0 +1,98 @@
1
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
2
+
3
+ describe "Document with a stamp" do
4
+ it "should raise NameTaken error when attempt to create stamp "+
5
+ "with same name as an existing stamp" do
6
+ create_pdf
7
+ @pdf.create_stamp("MyStamp")
8
+ lambda {
9
+ @pdf.create_stamp("MyStamp")
10
+ }.should.raise(Prawn::Errors::NameTaken)
11
+ end
12
+
13
+ it "should raise InvalidName error when attempt to create "+
14
+ "stamp with a blank name" do
15
+ create_pdf
16
+ lambda {
17
+ @pdf.create_stamp("")
18
+ }.should.raise(Prawn::Errors::InvalidName)
19
+ end
20
+
21
+ it "a new XObject should be defined for each stamp created" do
22
+ create_pdf
23
+ @pdf.create_stamp("MyStamp")
24
+ @pdf.create_stamp("AnotherStamp")
25
+ @pdf.stamp("MyStamp")
26
+ @pdf.stamp("AnotherStamp")
27
+
28
+ inspector = PDF::Inspector::XObject.analyze(@pdf.render)
29
+ xobjects = inspector.page_xobjects.last
30
+ xobjects.length.should == 2
31
+ end
32
+
33
+ it "calling stamp with a name that does not match an existing stamp "+
34
+ "should raise UndefinedObjectName" do
35
+ create_pdf
36
+ @pdf.create_stamp("MyStamp")
37
+ lambda {
38
+ @pdf.stamp("OtherStamp")
39
+ }.should.raise(Prawn::Errors::UndefinedObjectName)
40
+ end
41
+
42
+ it "stamp should be drawn into the document each time stamp is called" do
43
+ create_pdf
44
+ @pdf.create_stamp("MyStamp")
45
+ @pdf.stamp("MyStamp")
46
+ @pdf.stamp("MyStamp")
47
+ @pdf.stamp("MyStamp")
48
+ # I had modified PDF::Inspector::XObject to receive the
49
+ # invoke_xobject message and count the number of times it was
50
+ # called, but it was only called once, so I reverted checking the
51
+ # output with a regular expression
52
+ @pdf.render.should =~ /(\/Stamp1 Do.*?){3}/m
53
+ end
54
+
55
+ it "resources added during stamp creation should be added to the "+
56
+ "stamp XObject, not the page" do
57
+ create_pdf
58
+ @pdf.create_stamp("MyStamp") do
59
+ @pdf.transparent(0.5) { @pdf.circle_at([100, 100], :radius => 10)}
60
+ end
61
+ @pdf.stamp("MyStamp")
62
+
63
+ # Inspector::XObject does not give information about resources, so
64
+ # resorting to string matching
65
+
66
+ output = @pdf.render
67
+ objects = output.split("endobj")
68
+ objects.each do |object|
69
+ if object =~ /\/Type \/Page$/
70
+ object.should.not =~ /\/ExtGState/
71
+ elsif object =~ /\/Type \/XObject$/
72
+ object.should =~ /\/ExtGState/
73
+ end
74
+ end
75
+ end
76
+
77
+ it "if ProcSet changes are made, they should be added to the Page "+
78
+ "object, not the stamp XObject" do
79
+ create_pdf
80
+ @pdf.create_stamp("MyStamp") do
81
+ @pdf.text("hello")
82
+ end
83
+ @pdf.stamp("MyStamp")
84
+
85
+ # Inspector::XObject does not give information about ProcSet, so
86
+ # resorting to string matching
87
+
88
+ output = @pdf.render
89
+ objects = output.split("endobj")
90
+ objects.each do |object|
91
+ if object =~ /\/Type \/Page$/
92
+ object.should =~ /\/ProcSet/
93
+ elsif object =~ /\/Type \/XObject$/
94
+ object.should.not =~ /\/ProcSet/
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,152 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
4
+
5
+ describe "When stroking with default settings" do
6
+ before(:each) { create_pdf }
7
+ it "cap_style should be :butt" do
8
+ @pdf.cap_style.should == :butt
9
+ end
10
+
11
+ it "join_style should be :miter" do
12
+ @pdf.join_style.should == :miter
13
+ end
14
+
15
+ it "dashed? should be false" do
16
+ @pdf.should.not.be.dashed
17
+ end
18
+ end
19
+
20
+ describe "Cap styles" do
21
+ before(:each) { create_pdf }
22
+
23
+ it "should be able to use assignment operator" do
24
+ @pdf.cap_style = :round
25
+ @pdf.cap_style.should == :round
26
+ end
27
+
28
+ describe "#cap_style(:butt)" do
29
+ it "rendered PDF should include butt style cap" do
30
+ @pdf.cap_style(:butt)
31
+ cap_style = PDF::Inspector::Graphics::CapStyle.analyze(@pdf.render).cap_style
32
+ cap_style.should == 0
33
+ end
34
+ end
35
+
36
+ describe "#cap_style(:round)" do
37
+ it "rendered PDF should include round style cap" do
38
+ @pdf.cap_style(:round)
39
+ cap_style = PDF::Inspector::Graphics::CapStyle.analyze(@pdf.render).cap_style
40
+ cap_style.should == 1
41
+ end
42
+ end
43
+
44
+ describe "#cap_style(:projecting_square)" do
45
+ it "rendered PDF should include projecting_square style cap" do
46
+ @pdf.cap_style(:projecting_square)
47
+ cap_style = PDF::Inspector::Graphics::CapStyle.analyze(@pdf.render).cap_style
48
+ cap_style.should == 2
49
+ end
50
+ end
51
+ end
52
+
53
+ describe "Join styles" do
54
+ before(:each) { create_pdf }
55
+
56
+ it "should be able to use assignment operator" do
57
+ @pdf.join_style = :round
58
+ @pdf.join_style.should == :round
59
+ end
60
+
61
+ describe "#join_style(:miter)" do
62
+ it "rendered PDF should include miter style join" do
63
+ @pdf.join_style(:miter)
64
+ join_style = PDF::Inspector::Graphics::JoinStyle.analyze(@pdf.render).join_style
65
+ join_style.should == 0
66
+ end
67
+ end
68
+
69
+ describe "#join_style(:round)" do
70
+ it "rendered PDF should include round style join" do
71
+ @pdf.join_style(:round)
72
+ join_style = PDF::Inspector::Graphics::JoinStyle.analyze(@pdf.render).join_style
73
+ join_style.should == 1
74
+ end
75
+ end
76
+
77
+ describe "#join_style(:bevel)" do
78
+ it "rendered PDF should include bevel style join" do
79
+ @pdf.join_style(:bevel)
80
+ join_style = PDF::Inspector::Graphics::JoinStyle.analyze(@pdf.render).join_style
81
+ join_style.should == 2
82
+ end
83
+ end
84
+ end
85
+
86
+ describe "Dashes" do
87
+ before(:each) { create_pdf }
88
+
89
+ it "should be able to use assignment operator" do
90
+ @pdf.dash = 2
91
+ @pdf.should.be.dashed
92
+ end
93
+
94
+ describe "setting a dash" do
95
+ it "dashed? should be true" do
96
+ @pdf.dash(2)
97
+ @pdf.should.be.dashed
98
+ end
99
+ it "rendered PDF should include a stroked dash" do
100
+ @pdf.dash(2)
101
+ dashes = PDF::Inspector::Graphics::Dash.analyze(@pdf.render)
102
+ dashes.stroke_dash.should == [[2, 2], 0]
103
+ end
104
+ end
105
+
106
+ describe "setting a dash by passing a single argument" do
107
+ it "space between dashes should be the same length as the dash in the rendered PDF" do
108
+ @pdf.dash(2)
109
+ dashes = PDF::Inspector::Graphics::Dash.analyze(@pdf.render)
110
+ dashes.stroke_dash.should == [[2, 2], 0]
111
+ end
112
+ end
113
+
114
+ describe "with a space option that differs from the first argument" do
115
+ it "space between dashes in the rendered PDF should be different length than the length of the dash" do
116
+ @pdf.dash(2, :space => 3)
117
+ dashes = PDF::Inspector::Graphics::Dash.analyze(@pdf.render)
118
+ dashes.stroke_dash.should == [[2, 3], 0]
119
+ end
120
+ end
121
+
122
+ describe "with a non-zero phase option" do
123
+ it "rendered PDF should include a non-zero phase" do
124
+ @pdf.dash(2, :phase => 1)
125
+ dashes = PDF::Inspector::Graphics::Dash.analyze(@pdf.render)
126
+ dashes.stroke_dash.should == [[2, 2], 1]
127
+ end
128
+ end
129
+
130
+ describe "clearing stroke dash" do
131
+ it "should restore solid line" do
132
+ @pdf.dash(2)
133
+ @pdf.undash
134
+ dashes = PDF::Inspector::Graphics::Dash.analyze(@pdf.render)
135
+ dashes.stroke_dash.should == [[], 0]
136
+ end
137
+ end
138
+
139
+ it "should reset the stroke dash on each new page if it has been defined" do
140
+ @pdf.start_new_page
141
+ @pdf.dash(2)
142
+ dashes = PDF::Inspector::Graphics::Dash.analyze(@pdf.render)
143
+ dashes.stroke_dash_count.should == 1
144
+
145
+ @pdf.start_new_page
146
+ dashes = PDF::Inspector::Graphics::Dash.analyze(@pdf.render)
147
+ dashes.stroke_dash_count.should == 2
148
+ dashes.stroke_dash.should == [[], 0]
149
+ @pdf.dash.should == { :dash => nil, :space => nil, :phase => 0 }
150
+ end
151
+
152
+ end
@@ -20,6 +20,32 @@ describe "A text box" do
20
20
  @box.text.should == "Oh hai\ntext box.\n" * 5
21
21
  end
22
22
 
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
29
+ end
30
+
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
38
+ end
39
+
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
47
+ end
48
+
23
49
  it "should have a height equal to @height" do
24
50
  @overflow = :truncate
25
51
  create_text_box
data/spec/text_spec.rb CHANGED
@@ -17,7 +17,8 @@ describe "when drawing text" do
17
17
  @pdf.y.should.be.close(position - 3*@pdf.font.height, 0.0001)
18
18
  end
19
19
 
20
- it "should advance down the document based on font ascender only if final_gap is given" do
20
+ it "should advance down the document based on font ascender only "+
21
+ "if final_gap is given" do
21
22
  position = @pdf.y
22
23
  @pdf.text "Foo", :final_gap => false
23
24
 
@@ -28,6 +29,12 @@ describe "when drawing text" do
28
29
  @pdf.y.should.be.close(position - 2*@pdf.font.height - @pdf.font.ascender, 0.0001)
29
30
  end
30
31
 
32
+ it "should not accept :align alongside :at" do
33
+ assert_raises(ArgumentError) do
34
+ @pdf.text "What could this mean?", :at => [100, 100], :align => :center
35
+ end
36
+ end
37
+
31
38
  it "should default to 12 point helvetica" do
32
39
  @pdf.text "Blah", :at => [100,100]
33
40
  text = PDF::Inspector::Text.analyze(@pdf.render)
@@ -0,0 +1,61 @@
1
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
2
+
3
+ describe "Document with transparency" do
4
+ it "the PDF version should be at least 1.4" do
5
+ create_pdf
6
+ @pdf.transparent(0.5)
7
+ str = @pdf.render
8
+ str[0,8].should == "%PDF-1.4"
9
+ end
10
+
11
+ it "a new extended graphics state should be created for "+
12
+ "each unique transparency setting" do
13
+ create_pdf
14
+ @pdf.transparent(0.5, 0.2)
15
+ @pdf.transparent(0.5, 0.75)
16
+ extgstates = PDF::Inspector::ExtGState.analyze(@pdf.render).extgstates
17
+ extgstates.length.should == 2
18
+ end
19
+
20
+ it "a new extended graphics state should not be created for "+
21
+ "each duplicate transparency setting" do
22
+ create_pdf
23
+ @pdf.transparent(0.5, 0.75)
24
+ @pdf.transparent(0.5, 0.75)
25
+ extgstates = PDF::Inspector::ExtGState.analyze(@pdf.render).extgstates
26
+ extgstates.length.should == 1
27
+ end
28
+
29
+ it "setting the transparency with only one parameter sets the transparency"+
30
+ " for both the fill and the stroke" do
31
+ create_pdf
32
+ @pdf.transparent(0.5)
33
+ extgstate = PDF::Inspector::ExtGState.analyze(@pdf.render).extgstates[0]
34
+ extgstate[:opacity].should == 0.5
35
+ extgstate[:stroke_opacity].should == 0.5
36
+ end
37
+
38
+ it "setting the transparency with a numerical parameter and "+
39
+ "a :stroke should set the fill transparency to the numerical parameter "+
40
+ "and the stroke transparency to the option" do
41
+ create_pdf
42
+ @pdf.transparent(0.5, 0.2)
43
+ extgstate = PDF::Inspector::ExtGState.analyze(@pdf.render).extgstates[0]
44
+ extgstate[:opacity].should == 0.5
45
+ extgstate[:stroke_opacity].should == 0.2
46
+ end
47
+
48
+ describe "with more than one page" do
49
+ it "the extended graphic state resource should be added to both pages" do
50
+ create_pdf
51
+ @pdf.transparent(0.5, 0.2)
52
+ @pdf.start_new_page
53
+ @pdf.transparent(0.5, 0.2)
54
+ extgstates = PDF::Inspector::ExtGState.analyze(@pdf.render).extgstates
55
+ extgstate = extgstates[0]
56
+ extgstates.length.should == 2
57
+ extgstate[:opacity].should == 0.5
58
+ extgstate[:stroke_opacity].should == 0.2
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,18 @@
1
+ module PDF
2
+ class Inspector
3
+ class ExtGState < Inspector
4
+ attr_accessor :extgstates
5
+
6
+ def initialize
7
+ @extgstates = []
8
+ end
9
+
10
+ def resource_extgstate(*params)
11
+ @extgstates << {
12
+ :opacity => params[1][:ca],
13
+ :stroke_opacity => params[1][:CA]
14
+ }
15
+ end
16
+ end
17
+ end
18
+ end