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.
- data/HACKING +46 -0
- data/README +9 -3
- data/Rakefile +7 -6
- data/examples/bounding_box/stretched_nesting.rb +67 -0
- data/examples/general/margin.rb +36 -0
- data/examples/general/multi_page_layout.rb +3 -1
- data/examples/general/page_numbering.rb +15 -0
- data/examples/general/stamp.rb +45 -0
- data/examples/graphics/stroke_cap_and_join.rb +45 -0
- data/examples/graphics/stroke_dash.rb +42 -0
- data/examples/graphics/transparency.rb +26 -0
- data/examples/text/text_box_returning_excess.rb +51 -0
- data/lib/prawn/byte_string.rb +7 -0
- data/lib/prawn/core.rb +7 -8
- data/lib/prawn/document/annotations.rb +3 -2
- data/lib/prawn/document/bounding_box.rb +15 -10
- data/lib/prawn/document/column_box.rb +1 -3
- data/lib/prawn/document/destinations.rb +11 -10
- data/lib/prawn/document/internals.rb +62 -19
- data/lib/prawn/document/snapshot.rb +71 -0
- data/lib/prawn/document/text/box.rb +7 -0
- data/lib/prawn/document/text/wrapping.rb +3 -0
- data/lib/prawn/document/text.rb +9 -2
- data/lib/prawn/document.rb +141 -25
- data/lib/prawn/errors.rb +12 -0
- data/lib/prawn/font/afm.rb +1 -1
- data/lib/prawn/font/ttf.rb +5 -5
- data/lib/prawn/font.rb +8 -5
- data/lib/prawn/graphics/cap_style.rb +35 -0
- data/lib/prawn/graphics/dash.rb +69 -0
- data/lib/prawn/graphics/join_style.rb +35 -0
- data/lib/prawn/graphics/transparency.rb +56 -0
- data/lib/prawn/graphics.rb +9 -1
- data/lib/prawn/images.rb +4 -4
- data/lib/prawn/name_tree.rb +2 -1
- data/lib/prawn/object_store.rb +63 -0
- data/lib/prawn/pdf_object.rb +4 -0
- data/lib/prawn/reference.rb +18 -5
- data/lib/prawn/stamp.rb +87 -0
- data/spec/bounding_box_spec.rb +9 -0
- data/spec/document_spec.rb +58 -5
- data/spec/images_spec.rb +1 -1
- data/spec/name_tree_spec.rb +14 -5
- data/spec/object_store_spec.rb +42 -0
- data/spec/pdf_object_spec.rb +5 -0
- data/spec/reference_spec.rb +40 -0
- data/spec/snapshot_spec.rb +115 -0
- data/spec/spec_helper.rb +1 -4
- data/spec/stamp_spec.rb +98 -0
- data/spec/stroke_styles_spec.rb +152 -0
- data/spec/text_box_spec.rb +26 -0
- data/spec/text_spec.rb +8 -1
- data/spec/transparency_spec.rb +61 -0
- data/vendor/pdf-inspector/lib/pdf/inspector/extgstate.rb +18 -0
- data/vendor/pdf-inspector/lib/pdf/inspector/graphics.rb +40 -1
- data/vendor/pdf-inspector/lib/pdf/inspector/page.rb +12 -3
- data/vendor/pdf-inspector/lib/pdf/inspector.rb +2 -1
- metadata +26 -2
data/spec/images_spec.rb
CHANGED
data/spec/name_tree_spec.rb
CHANGED
@@ -19,7 +19,9 @@ def tree_value(name, value)
|
|
19
19
|
end
|
20
20
|
|
21
21
|
class RefExposingDocument < Prawn::Document
|
22
|
-
|
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.
|
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.
|
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.
|
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.
|
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
|
data/spec/pdf_object_spec.rb
CHANGED
@@ -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'
|
data/spec/reference_spec.rb
CHANGED
@@ -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
data/spec/stamp_spec.rb
ADDED
@@ -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
|
data/spec/text_box_spec.rb
CHANGED
@@ -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
|
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
|