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.
Files changed (120) hide show
  1. data/COPYING +340 -0
  2. data/LICENSE +56 -0
  3. data/README +30 -0
  4. data/Rakefile +83 -0
  5. data/data/fonts/Activa.ttf +0 -0
  6. data/data/fonts/Chalkboard.ttf +0 -0
  7. data/data/fonts/Courier-Bold.afm +342 -0
  8. data/data/fonts/Courier-BoldOblique.afm +342 -0
  9. data/data/fonts/Courier-Oblique.afm +342 -0
  10. data/data/fonts/Courier.afm +342 -0
  11. data/data/fonts/DejaVuSans.ttf +0 -0
  12. data/data/fonts/Dustismo_Roman.ttf +0 -0
  13. data/data/fonts/Helvetica-Bold.afm +2827 -0
  14. data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  15. data/data/fonts/Helvetica-Oblique.afm +3051 -0
  16. data/data/fonts/Helvetica.afm +3051 -0
  17. data/data/fonts/MustRead.html +19 -0
  18. data/data/fonts/Symbol.afm +213 -0
  19. data/data/fonts/Times-Bold.afm +2588 -0
  20. data/data/fonts/Times-BoldItalic.afm +2384 -0
  21. data/data/fonts/Times-Italic.afm +2667 -0
  22. data/data/fonts/Times-Roman.afm +2419 -0
  23. data/data/fonts/ZapfDingbats.afm +225 -0
  24. data/data/fonts/comicsans.ttf +0 -0
  25. data/data/fonts/gkai00mp.ttf +0 -0
  26. data/data/images/dice.png +0 -0
  27. data/data/images/pigs.jpg +0 -0
  28. data/data/images/ruport.png +0 -0
  29. data/data/images/ruport_data.dat +0 -0
  30. data/data/images/ruport_transparent.png +0 -0
  31. data/data/images/stef.jpg +0 -0
  32. data/data/shift_jis_text.txt +1 -0
  33. data/examples/addressbook.csv +6 -0
  34. data/examples/alignment.rb +16 -0
  35. data/examples/bounding_boxes.pdf +62 -0
  36. data/examples/bounding_boxes.rb +30 -0
  37. data/examples/canvas.pdf +81 -0
  38. data/examples/canvas.rb +12 -0
  39. data/examples/cell.rb +27 -0
  40. data/examples/currency.csv +1834 -0
  41. data/examples/curves.rb +10 -0
  42. data/examples/fancy_table.rb +48 -0
  43. data/examples/font_size.rb +19 -0
  44. data/examples/hexagon.rb +14 -0
  45. data/examples/image.pdf +0 -0
  46. data/examples/image.rb +23 -0
  47. data/examples/image2.rb +13 -0
  48. data/examples/inline_styles.pdf +117 -0
  49. data/examples/kerning.rb +27 -0
  50. data/examples/line.rb +31 -0
  51. data/examples/multi_page_layout.rb +14 -0
  52. data/examples/on_page_start.rb +17 -0
  53. data/examples/page_geometry.rb +28 -0
  54. data/examples/polygons.rb +16 -0
  55. data/examples/ruport_formatter.rb +47 -0
  56. data/examples/ruport_helpers.rb +17 -0
  57. data/examples/russian_boxes.rb +34 -0
  58. data/examples/simple_text.rb +15 -0
  59. data/examples/simple_text_ttf.rb +16 -0
  60. data/examples/sjis.rb +19 -0
  61. data/examples/table.rb +45 -0
  62. data/examples/table_bench.rb +92 -0
  63. data/examples/text_flow.rb +65 -0
  64. data/examples/utf8.rb +12 -0
  65. data/lib/prawn.rb +33 -0
  66. data/lib/prawn/compatibility.rb +33 -0
  67. data/lib/prawn/document.rb +334 -0
  68. data/lib/prawn/document/bounding_box.rb +253 -0
  69. data/lib/prawn/document/page_geometry.rb +78 -0
  70. data/lib/prawn/document/table.rb +253 -0
  71. data/lib/prawn/document/text.rb +346 -0
  72. data/lib/prawn/errors.rb +33 -0
  73. data/lib/prawn/font.rb +5 -0
  74. data/lib/prawn/font/cmap.rb +59 -0
  75. data/lib/prawn/font/metrics.rb +414 -0
  76. data/lib/prawn/font/wrapping.rb +45 -0
  77. data/lib/prawn/graphics.rb +285 -0
  78. data/lib/prawn/graphics/cell.rb +226 -0
  79. data/lib/prawn/images.rb +241 -0
  80. data/lib/prawn/images/jpg.rb +43 -0
  81. data/lib/prawn/images/png.rb +178 -0
  82. data/lib/prawn/pdf_object.rb +64 -0
  83. data/lib/prawn/reference.rb +47 -0
  84. data/spec/bounding_box_spec.rb +120 -0
  85. data/spec/box_calculation_spec.rb +17 -0
  86. data/spec/document_spec.rb +152 -0
  87. data/spec/graphics_spec.rb +250 -0
  88. data/spec/images_spec.rb +42 -0
  89. data/spec/jpg_spec.rb +25 -0
  90. data/spec/metrics_spec.rb +60 -0
  91. data/spec/pdf_object_spec.rb +102 -0
  92. data/spec/png_spec.rb +35 -0
  93. data/spec/reference_spec.rb +29 -0
  94. data/spec/spec_helper.rb +29 -0
  95. data/spec/table_spec.rb +145 -0
  96. data/spec/text_spec.rb +190 -0
  97. data/vendor/font_ttf/ttf.rb +20 -0
  98. data/vendor/font_ttf/ttf/datatypes.rb +189 -0
  99. data/vendor/font_ttf/ttf/encodings.rb +140 -0
  100. data/vendor/font_ttf/ttf/exceptions.rb +28 -0
  101. data/vendor/font_ttf/ttf/file.rb +290 -0
  102. data/vendor/font_ttf/ttf/fontchunk.rb +77 -0
  103. data/vendor/font_ttf/ttf/table/cmap.rb +408 -0
  104. data/vendor/font_ttf/ttf/table/cvt.rb +49 -0
  105. data/vendor/font_ttf/ttf/table/fpgm.rb +48 -0
  106. data/vendor/font_ttf/ttf/table/gasp.rb +88 -0
  107. data/vendor/font_ttf/ttf/table/glyf.rb +452 -0
  108. data/vendor/font_ttf/ttf/table/head.rb +86 -0
  109. data/vendor/font_ttf/ttf/table/hhea.rb +96 -0
  110. data/vendor/font_ttf/ttf/table/hmtx.rb +98 -0
  111. data/vendor/font_ttf/ttf/table/kern.rb +186 -0
  112. data/vendor/font_ttf/ttf/table/loca.rb +75 -0
  113. data/vendor/font_ttf/ttf/table/maxp.rb +81 -0
  114. data/vendor/font_ttf/ttf/table/name.rb +222 -0
  115. data/vendor/font_ttf/ttf/table/os2.rb +172 -0
  116. data/vendor/font_ttf/ttf/table/post.rb +120 -0
  117. data/vendor/font_ttf/ttf/table/prep.rb +27 -0
  118. data/vendor/font_ttf/ttf/table/vhea.rb +45 -0
  119. data/vendor/font_ttf/ttf/table/vmtx.rb +36 -0
  120. 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