prawn 0.1.0
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/COPYING +340 -0
- data/LICENSE +56 -0
- data/README +30 -0
- data/Rakefile +83 -0
- data/data/fonts/Activa.ttf +0 -0
- data/data/fonts/Chalkboard.ttf +0 -0
- data/data/fonts/Courier-Bold.afm +342 -0
- data/data/fonts/Courier-BoldOblique.afm +342 -0
- data/data/fonts/Courier-Oblique.afm +342 -0
- data/data/fonts/Courier.afm +342 -0
- data/data/fonts/DejaVuSans.ttf +0 -0
- data/data/fonts/Dustismo_Roman.ttf +0 -0
- data/data/fonts/Helvetica-Bold.afm +2827 -0
- data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
- data/data/fonts/Helvetica-Oblique.afm +3051 -0
- data/data/fonts/Helvetica.afm +3051 -0
- data/data/fonts/MustRead.html +19 -0
- data/data/fonts/Symbol.afm +213 -0
- data/data/fonts/Times-Bold.afm +2588 -0
- data/data/fonts/Times-BoldItalic.afm +2384 -0
- data/data/fonts/Times-Italic.afm +2667 -0
- data/data/fonts/Times-Roman.afm +2419 -0
- data/data/fonts/ZapfDingbats.afm +225 -0
- data/data/fonts/comicsans.ttf +0 -0
- data/data/fonts/gkai00mp.ttf +0 -0
- data/data/images/dice.png +0 -0
- data/data/images/pigs.jpg +0 -0
- data/data/images/ruport.png +0 -0
- data/data/images/ruport_data.dat +0 -0
- data/data/images/ruport_transparent.png +0 -0
- data/data/images/stef.jpg +0 -0
- data/data/shift_jis_text.txt +1 -0
- data/examples/addressbook.csv +6 -0
- data/examples/alignment.rb +16 -0
- data/examples/bounding_boxes.pdf +62 -0
- data/examples/bounding_boxes.rb +30 -0
- data/examples/canvas.pdf +81 -0
- data/examples/canvas.rb +12 -0
- data/examples/cell.rb +27 -0
- data/examples/currency.csv +1834 -0
- data/examples/curves.rb +10 -0
- data/examples/fancy_table.rb +48 -0
- data/examples/font_size.rb +19 -0
- data/examples/hexagon.rb +14 -0
- data/examples/image.pdf +0 -0
- data/examples/image.rb +23 -0
- data/examples/image2.rb +13 -0
- data/examples/inline_styles.pdf +117 -0
- data/examples/kerning.rb +27 -0
- data/examples/line.rb +31 -0
- data/examples/multi_page_layout.rb +14 -0
- data/examples/on_page_start.rb +17 -0
- data/examples/page_geometry.rb +28 -0
- data/examples/polygons.rb +16 -0
- data/examples/ruport_formatter.rb +47 -0
- data/examples/ruport_helpers.rb +17 -0
- data/examples/russian_boxes.rb +34 -0
- data/examples/simple_text.rb +15 -0
- data/examples/simple_text_ttf.rb +16 -0
- data/examples/sjis.rb +19 -0
- data/examples/table.rb +45 -0
- data/examples/table_bench.rb +92 -0
- data/examples/text_flow.rb +65 -0
- data/examples/utf8.rb +12 -0
- data/lib/prawn.rb +33 -0
- data/lib/prawn/compatibility.rb +33 -0
- data/lib/prawn/document.rb +334 -0
- data/lib/prawn/document/bounding_box.rb +253 -0
- data/lib/prawn/document/page_geometry.rb +78 -0
- data/lib/prawn/document/table.rb +253 -0
- data/lib/prawn/document/text.rb +346 -0
- data/lib/prawn/errors.rb +33 -0
- data/lib/prawn/font.rb +5 -0
- data/lib/prawn/font/cmap.rb +59 -0
- data/lib/prawn/font/metrics.rb +414 -0
- data/lib/prawn/font/wrapping.rb +45 -0
- data/lib/prawn/graphics.rb +285 -0
- data/lib/prawn/graphics/cell.rb +226 -0
- data/lib/prawn/images.rb +241 -0
- data/lib/prawn/images/jpg.rb +43 -0
- data/lib/prawn/images/png.rb +178 -0
- data/lib/prawn/pdf_object.rb +64 -0
- data/lib/prawn/reference.rb +47 -0
- data/spec/bounding_box_spec.rb +120 -0
- data/spec/box_calculation_spec.rb +17 -0
- data/spec/document_spec.rb +152 -0
- data/spec/graphics_spec.rb +250 -0
- data/spec/images_spec.rb +42 -0
- data/spec/jpg_spec.rb +25 -0
- data/spec/metrics_spec.rb +60 -0
- data/spec/pdf_object_spec.rb +102 -0
- data/spec/png_spec.rb +35 -0
- data/spec/reference_spec.rb +29 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/table_spec.rb +145 -0
- data/spec/text_spec.rb +190 -0
- data/vendor/font_ttf/ttf.rb +20 -0
- data/vendor/font_ttf/ttf/datatypes.rb +189 -0
- data/vendor/font_ttf/ttf/encodings.rb +140 -0
- data/vendor/font_ttf/ttf/exceptions.rb +28 -0
- data/vendor/font_ttf/ttf/file.rb +290 -0
- data/vendor/font_ttf/ttf/fontchunk.rb +77 -0
- data/vendor/font_ttf/ttf/table/cmap.rb +408 -0
- data/vendor/font_ttf/ttf/table/cvt.rb +49 -0
- data/vendor/font_ttf/ttf/table/fpgm.rb +48 -0
- data/vendor/font_ttf/ttf/table/gasp.rb +88 -0
- data/vendor/font_ttf/ttf/table/glyf.rb +452 -0
- data/vendor/font_ttf/ttf/table/head.rb +86 -0
- data/vendor/font_ttf/ttf/table/hhea.rb +96 -0
- data/vendor/font_ttf/ttf/table/hmtx.rb +98 -0
- data/vendor/font_ttf/ttf/table/kern.rb +186 -0
- data/vendor/font_ttf/ttf/table/loca.rb +75 -0
- data/vendor/font_ttf/ttf/table/maxp.rb +81 -0
- data/vendor/font_ttf/ttf/table/name.rb +222 -0
- data/vendor/font_ttf/ttf/table/os2.rb +172 -0
- data/vendor/font_ttf/ttf/table/post.rb +120 -0
- data/vendor/font_ttf/ttf/table/prep.rb +27 -0
- data/vendor/font_ttf/ttf/table/vhea.rb +45 -0
- data/vendor/font_ttf/ttf/table/vmtx.rb +36 -0
- metadata +180 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
#
|
|
3
|
+
# pdf_object.rb : Handles Ruby to PDF object serialization
|
|
4
|
+
#
|
|
5
|
+
# Copyright April 2008, Gregory Brown. All Rights Reserved.
|
|
6
|
+
#
|
|
7
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
|
8
|
+
|
|
9
|
+
module Prawn
|
|
10
|
+
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
# Serializes Ruby objects to their PDF equivalents. Most primitive objects
|
|
14
|
+
# will work as expected, but please note that Name objects are represented
|
|
15
|
+
# by Ruby Symbol objects and Dictionary objects are represented by Ruby hashes
|
|
16
|
+
# (keyed by symbols)
|
|
17
|
+
#
|
|
18
|
+
# Examples:
|
|
19
|
+
#
|
|
20
|
+
# PdfObject(true) #=> "true"
|
|
21
|
+
# PdfObject(false) #=> "false"
|
|
22
|
+
# PdfObject(1.2124) #=> "1.2124"
|
|
23
|
+
# PdfObject("foo bar") #=> "(foo bar)"
|
|
24
|
+
# PdfObject(:Symbol) #=> "/Symbol"
|
|
25
|
+
# PdfObject(["foo",:bar, [1,2]]) #=> "[foo /bar [1 2]]"
|
|
26
|
+
#
|
|
27
|
+
def PdfObject(obj, in_content_stream = false) #:nodoc:
|
|
28
|
+
case(obj)
|
|
29
|
+
when NilClass then "null"
|
|
30
|
+
when TrueClass then "true"
|
|
31
|
+
when FalseClass then "false"
|
|
32
|
+
when Numeric then String(obj)
|
|
33
|
+
when Array
|
|
34
|
+
"[" << obj.map { |e| PdfObject(e, in_content_stream) }.join(' ') << "]"
|
|
35
|
+
when String
|
|
36
|
+
obj = "\xFE\xFF" + obj.unpack("U*").pack("n*") unless in_content_stream
|
|
37
|
+
"<" << obj.unpack("H*").first << ">"
|
|
38
|
+
when Symbol
|
|
39
|
+
if (obj = obj.to_s) =~ /\s/
|
|
40
|
+
raise Prawn::Errors::FailedObjectConversion,
|
|
41
|
+
"A PDF Name cannot contain whitespace"
|
|
42
|
+
else
|
|
43
|
+
"/" << obj
|
|
44
|
+
end
|
|
45
|
+
when Hash
|
|
46
|
+
output = "<< "
|
|
47
|
+
obj.each do |k,v|
|
|
48
|
+
unless String === k || Symbol === k
|
|
49
|
+
raise Prawn::Errors::FailedObjectConversion,
|
|
50
|
+
"A PDF Dictionary must be keyed by names"
|
|
51
|
+
end
|
|
52
|
+
output << PdfObject(k.to_sym, in_content_stream) << " " <<
|
|
53
|
+
PdfObject(v, in_content_stream) << "\n"
|
|
54
|
+
end
|
|
55
|
+
output << ">>"
|
|
56
|
+
when Prawn::Reference
|
|
57
|
+
obj.to_s
|
|
58
|
+
else
|
|
59
|
+
raise Prawn::Errors::FailedObjectConversion,
|
|
60
|
+
"This object cannot be serialized to PDF"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
# reference.rb : Implementation of PDF indirect objects
|
|
4
|
+
#
|
|
5
|
+
# Copyright April 2008, Gregory Brown. All Rights Reserved.
|
|
6
|
+
#
|
|
7
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
|
8
|
+
|
|
9
|
+
module Prawn
|
|
10
|
+
|
|
11
|
+
class Reference #:nodoc:
|
|
12
|
+
|
|
13
|
+
attr_accessor :gen, :data, :offset
|
|
14
|
+
attr_reader :identifier, :stream
|
|
15
|
+
|
|
16
|
+
def initialize(id,data)
|
|
17
|
+
@identifier = id
|
|
18
|
+
@gen = 0
|
|
19
|
+
@data = data
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def object
|
|
23
|
+
output = "#{@identifier} #{gen} obj\n" <<
|
|
24
|
+
Prawn::PdfObject(data) << "\n"
|
|
25
|
+
if @stream
|
|
26
|
+
output << "stream\n" << @stream << "\nendstream\n"
|
|
27
|
+
end
|
|
28
|
+
output << "endobj\n"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def <<(data)
|
|
32
|
+
(@stream ||= "") << data
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def to_s
|
|
36
|
+
"#{@identifier} #{gen} R"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
module_function
|
|
42
|
+
|
|
43
|
+
def Reference(*args) #:nodoc:
|
|
44
|
+
Reference.new(*args)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
|
|
4
|
+
|
|
5
|
+
describe "A bounding box" do
|
|
6
|
+
|
|
7
|
+
before(:each) do
|
|
8
|
+
@x = 100
|
|
9
|
+
@y = 125
|
|
10
|
+
@width = 50
|
|
11
|
+
@height = 75
|
|
12
|
+
@box = Prawn::Document::BoundingBox.new(nil, [@x,@y], :width => @width,
|
|
13
|
+
:height => @height )
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "should have an anchor at (x, y - height)" do
|
|
17
|
+
@box.anchor.should == [@x,@y-@height]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should have a left boundary of 0" do
|
|
21
|
+
@box.left.should == 0
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "should have a right boundary equal to the width" do
|
|
25
|
+
@box.right.should == @width
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "should have a top boundary of height" do
|
|
29
|
+
@box.top.should == @height
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should have a bottom boundary of 0" do
|
|
33
|
+
@box.bottom.should == 0
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "should have a top-left of [0,height]" do
|
|
37
|
+
@box.top_left.should == [0,@height]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "should have a top-right of [width,height]" do
|
|
41
|
+
@box.top_right.should == [@width,@height]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "should have a bottom-left of [0,0]" do
|
|
45
|
+
@box.bottom_left.should == [0,0]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "should have a bottom-right of [width,0]" do
|
|
49
|
+
@box.bottom_right.should == [@width,0]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "should have an absolute left boundary of x" do
|
|
53
|
+
@box.absolute_left.should == @x
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "should have an absolute right boundary of x + width" do
|
|
57
|
+
@box.absolute_right.should == @x + @width
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "should have an absolute top boundary of y" do
|
|
61
|
+
@box.absolute_top.should == @y
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "should have an absolute bottom boundary of y - height" do
|
|
65
|
+
@box.absolute_bottom.should == @y - @height
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "should have an absolute bottom-left of [x,y-height]" do
|
|
69
|
+
@box.absolute_bottom_left.should == [@x, @y - @height]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "should have an absolute bottom-right of [x+width,y-height]" do
|
|
73
|
+
@box.absolute_bottom_right.should == [@x + @width , @y - @height]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "should have an absolute top-left of [x,y]" do
|
|
77
|
+
@box.absolute_top_left.should == [@x, @y]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "should have an absolute top-right of [x+width,y]" do
|
|
81
|
+
@box.absolute_top_right.should == [@x + @width, @y]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
describe "drawing bounding boxes" do
|
|
88
|
+
|
|
89
|
+
it "should restore the margin box when bounding box exits" do
|
|
90
|
+
@pdf = Prawn::Document.new
|
|
91
|
+
margin_box = @pdf.bounds
|
|
92
|
+
|
|
93
|
+
@pdf.bounding_box [100,500] do
|
|
94
|
+
#nothing
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
@pdf.bounds.should == margin_box
|
|
98
|
+
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it "should restore the parent bounding box when calls are nested" do
|
|
102
|
+
@pdf = Prawn::Document.new
|
|
103
|
+
@pdf.bounding_box [100,500], :width => 300, :height => 300 do
|
|
104
|
+
|
|
105
|
+
@pdf.bounds.absolute_top.should == 500 + @pdf.margin_box.absolute_bottom
|
|
106
|
+
@pdf.bounds.absolute_left.should == 100 + @pdf.margin_box.absolute_left
|
|
107
|
+
|
|
108
|
+
parent_box = @pdf.bounds
|
|
109
|
+
|
|
110
|
+
@pdf.bounding_box [50,200], :width => 100, :height => 100 do
|
|
111
|
+
@pdf.bounds.absolute_top.should == 200 + parent_box.absolute_bottom
|
|
112
|
+
@pdf.bounds.absolute_left.should == 50 + parent_box.absolute_left
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
@pdf.bounds.absolute_top.should == 500 + @pdf.margin_box.absolute_bottom
|
|
116
|
+
@pdf.bounds.absolute_left.should == 100 + @pdf.margin_box.absolute_left
|
|
117
|
+
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
|
|
4
|
+
|
|
5
|
+
describe "A bounding box" do
|
|
6
|
+
|
|
7
|
+
before(:each) { create_pdf }
|
|
8
|
+
|
|
9
|
+
it "should calculate a height if none is specified" do
|
|
10
|
+
@pdf.bounding_box([100, 500], :width => 100) do
|
|
11
|
+
@pdf.text "The rain in Spain falls mainly on the plains."
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
@pdf.y.should be_close(458.384, 0.001)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
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 creating multi-page documents" do
|
|
6
|
+
|
|
7
|
+
class PageCounter
|
|
8
|
+
attr_accessor :pages
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@pages = 0
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Called when page parsing ends
|
|
15
|
+
def end_page
|
|
16
|
+
@pages += 1
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
before(:each) { create_pdf }
|
|
22
|
+
|
|
23
|
+
it "should initialize with a single page" do
|
|
24
|
+
page_counter = count_pages
|
|
25
|
+
|
|
26
|
+
page_counter.pages.should == 1
|
|
27
|
+
@pdf.page_count.should == 1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "should provide an accurate page_count" do
|
|
31
|
+
3.times { @pdf.start_new_page }
|
|
32
|
+
page_counter = count_pages
|
|
33
|
+
|
|
34
|
+
page_counter.pages.should == 4
|
|
35
|
+
@pdf.page_count.should == 4
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def count_pages
|
|
39
|
+
output = @pdf.render
|
|
40
|
+
obs = PageCounter.new
|
|
41
|
+
PDF::Reader.string(output,obs)
|
|
42
|
+
return obs
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe "When beginning each new page" do
|
|
48
|
+
|
|
49
|
+
it "should execute the lambda specified by on_page_start" do
|
|
50
|
+
on_start = mock("page_start_proc")
|
|
51
|
+
|
|
52
|
+
on_start.should_receive(:[]).exactly(3).times
|
|
53
|
+
|
|
54
|
+
pdf = Prawn::Document.new(:on_page_start => on_start)
|
|
55
|
+
pdf.start_new_page
|
|
56
|
+
pdf.start_new_page
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
describe "When ending each page" do
|
|
63
|
+
|
|
64
|
+
it "should execute the lambda specified by on_page_end" do
|
|
65
|
+
|
|
66
|
+
on_end = mock("page_end_proc")
|
|
67
|
+
|
|
68
|
+
on_end.should_receive(:[]).exactly(3).times
|
|
69
|
+
|
|
70
|
+
pdf = Prawn::Document.new(:on_page_end => on_end)
|
|
71
|
+
pdf.start_new_page
|
|
72
|
+
pdf.start_new_page
|
|
73
|
+
pdf.render
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
class PageDetails
|
|
79
|
+
|
|
80
|
+
def begin_page(params)
|
|
81
|
+
@geometry = params[:MediaBox]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def size
|
|
85
|
+
@geometry[-2..-1]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def detect_page_details
|
|
91
|
+
output = @pdf.render
|
|
92
|
+
obs = PageDetails.new
|
|
93
|
+
PDF::Reader.string(output,obs)
|
|
94
|
+
return obs
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe "When setting page size" do
|
|
98
|
+
it "should default to LETTER" do
|
|
99
|
+
@pdf = Prawn::Document.new
|
|
100
|
+
page = detect_page_details
|
|
101
|
+
page.size.should == Prawn::Document::PageGeometry::SIZES["LETTER"]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
(Prawn::Document::PageGeometry::SIZES.keys - ["LETTER"]).each do |k|
|
|
105
|
+
it "should provide #{k} geometry" do
|
|
106
|
+
@pdf = Prawn::Document.new(:page_size => k)
|
|
107
|
+
page = detect_page_details
|
|
108
|
+
page.size.should == Prawn::Document::PageGeometry::SIZES[k]
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it "should allow custom page size" do
|
|
113
|
+
@pdf = Prawn::Document.new(:page_size => [1920, 1080] )
|
|
114
|
+
page = detect_page_details
|
|
115
|
+
page.size.should == [1920, 1080]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
describe "When setting page layout" do
|
|
121
|
+
it "should reverse coordinates for landscape" do
|
|
122
|
+
@pdf = Prawn::Document.new(:page_size => "A4", :page_layout => :landscape)
|
|
123
|
+
page = detect_page_details
|
|
124
|
+
page.size.should == Prawn::Document::PageGeometry::SIZES["A4"].reverse
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
describe "The mask() feature" do
|
|
129
|
+
it "should allow transactional restoration of attributes" do
|
|
130
|
+
@pdf = Prawn::Document.new
|
|
131
|
+
y, line_width = @pdf.y, @pdf.line_width
|
|
132
|
+
@pdf.mask(:y, :line_width) do
|
|
133
|
+
@pdf.y = y + 1
|
|
134
|
+
@pdf.line_width = line_width + 1
|
|
135
|
+
@pdf.y.should_not == y
|
|
136
|
+
@pdf.line_width.should_not == line_width
|
|
137
|
+
end
|
|
138
|
+
@pdf.y.should == y
|
|
139
|
+
@pdf.line_width.should == line_width
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
describe "The render() feature" do
|
|
144
|
+
if "spec".respond_to?(:encode!)
|
|
145
|
+
it "should return a 8 bit encoded string on a m17n aware VM" do
|
|
146
|
+
@pdf = Prawn::Document.new(:page_size => "A4", :page_layout => :landscape)
|
|
147
|
+
@pdf.line [100,100], [200,200]
|
|
148
|
+
str = @pdf.render
|
|
149
|
+
str.encoding.to_s.should eql("ASCII-8BIT")
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
|
|
4
|
+
|
|
5
|
+
class LineDrawingObserver
|
|
6
|
+
attr_accessor :points, :strokes
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@points = []
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def append_line(*params)
|
|
13
|
+
@points << params
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def begin_new_subpath(*params)
|
|
17
|
+
@points << params
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe "When drawing a line" do
|
|
23
|
+
|
|
24
|
+
before(:each) { create_pdf }
|
|
25
|
+
|
|
26
|
+
it "should draw a line from (100,600) to (100,500)" do
|
|
27
|
+
@pdf.line([100,600],[100,500])
|
|
28
|
+
|
|
29
|
+
line_drawing = observer(LineDrawingObserver)
|
|
30
|
+
|
|
31
|
+
line_drawing.points.should == [[100,600],[100,500]]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "should draw two lines (100,600) to (100,500) and stroke each line" +
|
|
35
|
+
"and (75,100) to (50,125)" do
|
|
36
|
+
@pdf.line(100,600,100,500)
|
|
37
|
+
@pdf.line(75,100,50,125)
|
|
38
|
+
|
|
39
|
+
line_drawing = observer(LineDrawingObserver)
|
|
40
|
+
|
|
41
|
+
line_drawing.points.should ==
|
|
42
|
+
[[100.0, 600.0], [100.0, 500.0], [75.0, 100.0], [50.0, 125.0]]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class LineWidthReader
|
|
46
|
+
attr_accessor :width
|
|
47
|
+
def set_line_width(params)
|
|
48
|
+
@width = params
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "should properly set line width" do
|
|
53
|
+
create_pdf
|
|
54
|
+
@pdf.line_width = 10
|
|
55
|
+
line = observer(LineWidthReader)
|
|
56
|
+
line.width.should == 10
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
describe "When drawing a polygon" do
|
|
62
|
+
|
|
63
|
+
before(:each) { create_pdf }
|
|
64
|
+
|
|
65
|
+
it "should draw each line passed to polygon()" do
|
|
66
|
+
@pdf.polygon([100,500],[100,400],[200,400])
|
|
67
|
+
|
|
68
|
+
line_drawing = observer(LineDrawingObserver)
|
|
69
|
+
line_drawing.points.should == [[100,500],[100,400],[200,400],[100,500]]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
class RectangleDrawingObserver
|
|
75
|
+
|
|
76
|
+
attr_reader :point, :width, :height
|
|
77
|
+
|
|
78
|
+
def append_rectangle(*params)
|
|
79
|
+
@point = params[0..1]
|
|
80
|
+
@width = params[2]
|
|
81
|
+
@height = params[3]
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
describe "When drawing a rectangle" do
|
|
86
|
+
|
|
87
|
+
before(:each) { create_pdf }
|
|
88
|
+
|
|
89
|
+
it "should use a point, width, and height for coords" do
|
|
90
|
+
@pdf.rectangle [200,200], 50, 100
|
|
91
|
+
|
|
92
|
+
rectangle = observer(RectangleDrawingObserver)
|
|
93
|
+
# PDF uses bottom left corner
|
|
94
|
+
rectangle.point.should == [200,100]
|
|
95
|
+
rectangle.width.should == 50
|
|
96
|
+
rectangle.height.should == 100
|
|
97
|
+
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
class CurveObserver
|
|
103
|
+
|
|
104
|
+
attr_reader :coords
|
|
105
|
+
|
|
106
|
+
def initialize
|
|
107
|
+
@coords = []
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def begin_new_subpath(*params)
|
|
111
|
+
@coords += params
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def append_curved_segment(*params)
|
|
115
|
+
@coords += params
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
describe "When drawing a curve" do
|
|
121
|
+
|
|
122
|
+
before(:each) { create_pdf }
|
|
123
|
+
|
|
124
|
+
it "should draw a bezier curve from 50,50 to 100,100" do
|
|
125
|
+
@pdf.move_to [50,50]
|
|
126
|
+
@pdf.curve_to [100,100],:bounds => [[20,90], [90,70]]
|
|
127
|
+
curve = observer(CurveObserver)
|
|
128
|
+
curve.coords.should == [50.0, 50.0, 20.0, 90.0, 90.0, 70.0, 100.0, 100.0]
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it "should draw a bezier curve from 100,100 to 50,50" do
|
|
132
|
+
@pdf.curve [100,100], [50,50], :bounds => [[20,90], [90,75]]
|
|
133
|
+
curve = observer(CurveObserver)
|
|
134
|
+
curve.coords.should == [100.0, 100.0, 20.0, 90.0, 90.0, 75.0, 50.0, 50.0]
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
describe "When drawing an ellipse" do
|
|
140
|
+
before(:each) do
|
|
141
|
+
create_pdf
|
|
142
|
+
@pdf.ellipse_at [100,100], 25, 50
|
|
143
|
+
@curve = observer(CurveObserver)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "should move the pointer to the center of the ellipse after drawing" do
|
|
147
|
+
@curve.coords[-2..-1].should == [100,100]
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
describe "When drawing a circle" do
|
|
153
|
+
before(:each) do
|
|
154
|
+
create_pdf
|
|
155
|
+
@pdf.circle_at [100,100], :radius => 25
|
|
156
|
+
@pdf.ellipse_at [100,100], 25, 25
|
|
157
|
+
@curve = observer(CurveObserver)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "should stroke the same path as the equivalent ellipse" do
|
|
161
|
+
middle = @curve.coords.length / 2
|
|
162
|
+
@curve.coords[0...middle].should == @curve.coords[middle..-1]
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
class ColorObserver
|
|
167
|
+
attr_reader :stroke_color, :fill_color, :stroke_color_count,
|
|
168
|
+
:fill_color_count
|
|
169
|
+
|
|
170
|
+
def initialize
|
|
171
|
+
@stroke_color_count = 0
|
|
172
|
+
@fill_color_count = 0
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def set_rgb_color_for_stroking(*params)
|
|
176
|
+
@stroke_color_count += 1
|
|
177
|
+
@stroke_color = params
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def set_rgb_color_for_nonstroking(*params)
|
|
181
|
+
@fill_color_count += 1
|
|
182
|
+
@fill_color = params
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
describe "When setting colors" do
|
|
187
|
+
|
|
188
|
+
before(:each) { create_pdf }
|
|
189
|
+
|
|
190
|
+
it "should set stroke colors" do
|
|
191
|
+
@pdf.stroke_color "ffcccc"
|
|
192
|
+
colors = observer(ColorObserver)
|
|
193
|
+
# 100% red, 80% green, 80% blue
|
|
194
|
+
colors.stroke_color.should == [1.0, 0.8, 0.8]
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
it "should set fill colors" do
|
|
198
|
+
@pdf.fill_color "ccff00"
|
|
199
|
+
colors = observer(ColorObserver)
|
|
200
|
+
# 80% red, 100% green, 0% blue
|
|
201
|
+
colors.fill_color.should == [0.8,1.0,0]
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it "should reset the colors on each new page if they have been defined" do
|
|
205
|
+
@pdf.fill_color "ccff00"
|
|
206
|
+
colors = observer(ColorObserver)
|
|
207
|
+
|
|
208
|
+
colors.fill_color_count.should == 2
|
|
209
|
+
colors.stroke_color_count.should == 1
|
|
210
|
+
@pdf.start_new_page
|
|
211
|
+
@pdf.stroke_color "ff00cc"
|
|
212
|
+
|
|
213
|
+
colors = observer(ColorObserver)
|
|
214
|
+
colors.fill_color_count.should == 3
|
|
215
|
+
colors.stroke_color_count.should == 3
|
|
216
|
+
|
|
217
|
+
@pdf.start_new_page
|
|
218
|
+
colors = observer(ColorObserver)
|
|
219
|
+
colors.fill_color_count.should == 4
|
|
220
|
+
colors.stroke_color_count.should == 4
|
|
221
|
+
|
|
222
|
+
colors.fill_color.should == [0.8,1.0,0.0]
|
|
223
|
+
colors.stroke_color.should == [1.0,0.0,0.8]
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
describe "When using painting shortcuts" do
|
|
230
|
+
before(:each) { create_pdf }
|
|
231
|
+
|
|
232
|
+
it "should convert stroke_some_method(args) into some_method(args); stroke" do
|
|
233
|
+
@pdf.should_receive(:line_to).with([100,100])
|
|
234
|
+
@pdf.should_receive(:stroke)
|
|
235
|
+
|
|
236
|
+
@pdf.stroke_line_to [100,100]
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
it "should convert fill_some_method(args) into some_method(args); fill" do
|
|
240
|
+
@pdf.should_receive(:line_to).with([100,100])
|
|
241
|
+
@pdf.should_receive(:fill)
|
|
242
|
+
|
|
243
|
+
@pdf.fill_line_to [100,100]
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
it "should not break method_missing" do
|
|
247
|
+
lambda { @pdf.i_have_a_pretty_girlfriend_named_jia }.
|
|
248
|
+
should raise_error(NoMethodError)
|
|
249
|
+
end
|
|
250
|
+
end
|