pdf-wrapper 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ メインページ
Binary file
@@ -0,0 +1,451 @@
1
+ # -* coding: UTF-8 -*-
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
4
+
5
+ require 'pdf/wrapper'
6
+ require 'tempfile'
7
+ require 'rubygems'
8
+ require 'pdf/reader'
9
+
10
+ # make some private methods of PDF::Wrapper public for testing
11
+ class PDF::Wrapper
12
+ public :build_pango_layout
13
+ public :load_librsvg
14
+ public :load_libpixbuf
15
+ public :load_libpango
16
+ public :load_libpoppler
17
+ public :default_text_options
18
+ public :detect_image_type
19
+ public :validate_color
20
+ end
21
+
22
+ # a helper class for couting the number of pages in a PDF
23
+ class PageReceiver
24
+ attr_accessor :page_count
25
+
26
+ def initialize
27
+ @page_count = 0
28
+ end
29
+
30
+ # Called when page parsing ends
31
+ def end_page
32
+ @page_count += 1
33
+ end
34
+ end
35
+
36
+ class PageTextReceiver
37
+ attr_accessor :content
38
+
39
+ def initialize
40
+ @content = []
41
+ end
42
+
43
+ # Called when page parsing starts
44
+ def begin_page(arg = nil)
45
+ @content << ""
46
+ end
47
+
48
+ def show_text(string, *params)
49
+ @content.last << string.strip
50
+ end
51
+
52
+ # there's a few text callbacks, so make sure we process them all
53
+ alias :super_show_text :show_text
54
+ alias :move_to_next_line_and_show_text :show_text
55
+ alias :set_spacing_next_line_show_text :show_text
56
+
57
+ def show_text_with_positioning(*params)
58
+ params = params.first
59
+ params.each { |str| show_text(str) if str.kind_of?(String) }
60
+ end
61
+
62
+ end
63
+
64
+ context "The PDF::Wrapper class" do
65
+
66
+ setup do
67
+ #@file = File.new(File.dirname(__FILE__) + "/data/cairo-basic.pdf")
68
+ @shortstr = "Chunky Bacon"
69
+ @medstr = "This is a medium length string\nthat is also multi line. one two three four."
70
+ end
71
+
72
+ specify "should load external libs correctly" do
73
+ pdf = PDF::Wrapper.new
74
+
75
+ # lib gdkpixbuf
76
+ ::Object.const_defined?(:Gdk).should eql(false)
77
+ pdf.load_libpixbuf
78
+ ::Object.const_defined?(:Gdk).should eql(true)
79
+ ::Gdk.const_defined?(:Pixbuf).should eql(true)
80
+
81
+ # pango
82
+ ::Object.const_defined?(:Pango).should eql(false)
83
+ pdf.load_libpango
84
+ ::Object.const_defined?(:Pango).should eql(true)
85
+
86
+ # libpoppler
87
+ ::Object.const_defined?(:Poppler).should eql(false)
88
+ pdf.load_libpoppler
89
+ ::Object.const_defined?(:Poppler).should eql(true)
90
+
91
+ # librsvg
92
+ ::Object.const_defined?(:RSVG).should eql(false)
93
+ pdf.load_librsvg
94
+ ::Object.const_defined?(:RSVG).should eql(true)
95
+
96
+ end
97
+
98
+ specify "should initilize with the correct default paper size and orientation" do
99
+ pdf = PDF::Wrapper.new
100
+ pdf.page_width.should eql(PDF::Wrapper::PAGE_SIZES[:A4].first)
101
+ pdf.page_height.should eql(PDF::Wrapper::PAGE_SIZES[:A4].last)
102
+ end
103
+
104
+ specify "should initilize with the correct custom paper size" do
105
+ pdf = PDF::Wrapper.new(:paper => :A0)
106
+ pdf.page_width.should eql(PDF::Wrapper::PAGE_SIZES[:A0].first)
107
+ pdf.page_height.should eql(PDF::Wrapper::PAGE_SIZES[:A0].last)
108
+ end
109
+
110
+ specify "should initilize with the correct custom orientation" do
111
+ pdf = PDF::Wrapper.new(:paper => :A4, :orientation => :landscape)
112
+ pdf.page_width.should eql(PDF::Wrapper::PAGE_SIZES[:A4].last)
113
+ pdf.page_height.should eql(PDF::Wrapper::PAGE_SIZES[:A4].first)
114
+ end
115
+
116
+ specify "should raise an exception if an invalid orientation is requested" do
117
+ lambda {pdf = PDF::Wrapper.new(:paper => :A4, :orientation => :fake)}.should raise_error(ArgumentError)
118
+ end
119
+
120
+ specify "should store sensible default text options" do
121
+ pdf = PDF::Wrapper.new
122
+ pdf.default_text_options.should be_a_kind_of(Hash)
123
+ end
124
+
125
+ specify "should initilize with the correct default margins (5% of the page)" do
126
+ pdf = PDF::Wrapper.new
127
+ pdf.margin_left.should eql((PDF::Wrapper::PAGE_SIZES[:A4].first * 0.05).ceil)
128
+ pdf.margin_right.should eql((PDF::Wrapper::PAGE_SIZES[:A4].first * 0.05).ceil)
129
+ pdf.margin_top.should eql((PDF::Wrapper::PAGE_SIZES[:A4].last * 0.05).ceil)
130
+ pdf.margin_bottom.should eql((PDF::Wrapper::PAGE_SIZES[:A4].last * 0.05).ceil)
131
+ end
132
+
133
+ specify "should initilize with the correct default text and colour settings" do
134
+ pdf = PDF::Wrapper.new
135
+ pdf.instance_variable_get("@default_color").should eql([0.0,0.0,0.0,1.0])
136
+ pdf.instance_variable_get("@default_font").should eql("Sans Serif")
137
+ pdf.instance_variable_get("@default_font_size").should eql(16)
138
+ end
139
+
140
+ specify "should initialize with the cursor at the top left of the body of the page" do
141
+ pdf = PDF::Wrapper.new
142
+ x,y = pdf.current_point
143
+ x.to_i.should eql(pdf.margin_left)
144
+ y.to_i.should eql(pdf.margin_top)
145
+ end
146
+
147
+ specify "should calculate the absolute coordinates for the margins correctly" do
148
+ pdf = PDF::Wrapper.new
149
+ pdf.absolute_left_margin.should eql(pdf.margin_left)
150
+ pdf.absolute_right_margin.should eql(pdf.page_width - pdf.margin_right)
151
+ pdf.absolute_top_margin.should eql(pdf.margin_top)
152
+ pdf.absolute_bottom_margin.should eql(pdf.page_height - pdf.margin_bottom)
153
+ end
154
+
155
+ specify "should calculate various useful page coordinates correctly" do
156
+ pdf = PDF::Wrapper.new
157
+ pdf.absolute_x_middle.should eql(PDF::Wrapper::PAGE_SIZES[:A4].first / 2)
158
+ pdf.absolute_y_middle.should eql(PDF::Wrapper::PAGE_SIZES[:A4].last / 2)
159
+ pdf.body_width.should eql(pdf.page_width - pdf.margin_left - pdf.margin_right)
160
+ pdf.body_height.should eql(pdf.page_height - pdf.margin_top - pdf.margin_bottom)
161
+ pdf.margin_x_middle.should eql(pdf.margin_left + (pdf.body_width/ 2))
162
+ pdf.margin_y_middle.should eql(pdf.margin_top + (pdf.body_height/ 2))
163
+ pdf.points_to_bottom_margin(300).should eql(pdf.absolute_bottom_margin - 300)
164
+ pdf.points_to_right_margin(300).should eql(pdf.absolute_right_margin - 300)
165
+ end
166
+
167
+ specify "should be able to move the cursor to any arbitary point on the canvas" do
168
+ pdf = PDF::Wrapper.new
169
+ pdf.move_to(100,100)
170
+ x,y = pdf.current_point
171
+ x.to_i.should eql(100)
172
+ y.to_i.should eql(100)
173
+ end
174
+
175
+ specify "should raise an exception if the user tries to move the cursor off the canvas" do
176
+ pdf = PDF::Wrapper.new
177
+ lambda {pdf.move_to(PDF::Wrapper::PAGE_SIZES[:A4].first + 10,100)}.should raise_error(ArgumentError)
178
+ lambda {pdf.move_to(100, PDF::Wrapper::PAGE_SIZES[:A4].last + 10)}.should raise_error(ArgumentError)
179
+ end
180
+
181
+ specify "should add additional pages at the users request" do
182
+ pdf = PDF::Wrapper.new
183
+ pdf.move_to(100,100)
184
+ pdf.start_new_page
185
+ x,y = pdf.current_point
186
+ x.to_i.should eql(pdf.margin_left)
187
+ y.to_i.should eql(pdf.margin_top)
188
+
189
+ # verify the output
190
+ receiver = PageReceiver.new
191
+ reader = PDF::Reader.string(pdf.render, receiver)
192
+ receiver.page_count.should eql(2)
193
+ end
194
+
195
+ specify "should be able to draw a single line onto the canvas" do
196
+ x0 = y0 = 100
197
+ x1 = y1 = 200
198
+ pdf = PDF::Wrapper.new
199
+ pdf.line(x0,y0,x1,y1)
200
+
201
+ receiver = PDF::Reader::RegisterReceiver.new
202
+ reader = PDF::Reader.string(pdf.render, receiver)
203
+
204
+ # the begin_new_subpath command specifies the start of the line, append line specifies the end
205
+ receiver.count(:begin_new_subpath).should eql(1)
206
+ receiver.count(:append_line).should eql(1)
207
+ receiver.first_occurance_of(:begin_new_subpath)[:args].should eql([x0.to_f, y0.to_f])
208
+ receiver.first_occurance_of(:append_line)[:args].should eql([x1.to_f, y1.to_f])
209
+ end
210
+
211
+ specify "should be able to draw an empty rectangle onto the canvas" do
212
+ x = y = 100
213
+ w = h = 200
214
+ pdf = PDF::Wrapper.new
215
+ pdf.rectangle(x,y,w,h)
216
+
217
+ receiver = PDF::Reader::RegisterReceiver.new
218
+ reader = PDF::Reader.string(pdf.render, receiver)
219
+
220
+ # the begin_new_subpath command specifies the start of the line, append line specifies the end
221
+ callbacks = receiver.series(:begin_new_subpath, :append_line,:append_line,:append_line, :close_subpath)
222
+ callbacks.shift[:args].should eql([x.to_f, y.to_f])
223
+ callbacks.shift[:args].should eql([(x+w).to_f, y.to_f])
224
+ callbacks.shift[:args].should eql([(x+w).to_f, (y+h).to_f])
225
+ callbacks.shift[:args].should eql([x.to_f, (y+h).to_f])
226
+ end
227
+
228
+ specify "should leave the cursor in the bottom left of a layout when new text is added" do
229
+ pdf = PDF::Wrapper.new
230
+ x, y = pdf.current_point
231
+ str = "Chunky Bacon!!"
232
+ opts = {:font_size => 16, :font => "Sans Serif", :alignment => :left, :justify => false }
233
+ height = pdf.text_height(str, pdf.page_width, opts)
234
+ pdf.text(str,opts)
235
+ newx, newy = pdf.current_point
236
+
237
+ newx.should eql(x)
238
+ # the top of our text box, plus its height
239
+ newy.should eql(y + height)
240
+ end
241
+
242
+ specify "should be able to draw a filled rectangle onto the canvas"
243
+ =begin
244
+ do
245
+ x = y = 100
246
+ w = h = 200
247
+ pdf = PDF::Wrapper.new
248
+ pdf.rectangle(x,y,w,h, :fill_color => :red)
249
+
250
+ receiver = PDF::Reader::RegisterReceiver.new
251
+ reader = PDF::Reader.string(pdf.render, receiver)
252
+
253
+ # TODO: test for the appropriate pattern of callbacks
254
+ end
255
+ =end
256
+
257
+ specify "should be able to draw an empty rounded rectangle onto the canvas"
258
+ =begin
259
+ do
260
+ x = y = 100
261
+ w = h = 200
262
+ r = 5
263
+ pdf = PDF::Wrapper.new
264
+ pdf.rounded_rectangle(x,y,w,h,r)
265
+
266
+ receiver = PDF::Reader::RegisterReceiver.new
267
+ reader = PDF::Reader.string(pdf.render, receiver)
268
+
269
+ # TODO: test for the appropriate pattern of callbacks
270
+ end
271
+ =end
272
+
273
+ specify "should be able to draw a filled rounded rectangle onto the canvas"
274
+ =begin
275
+ do
276
+ x = y = 100
277
+ w = h = 200
278
+ r = 5
279
+ pdf = PDF::Wrapper.new
280
+ pdf.rounded_rectangle(x,y,w,h,r, :fill_color => :red)
281
+
282
+ receiver = PDF::Reader::RegisterReceiver.new
283
+ reader = PDF::Reader.string(pdf.render, receiver)
284
+
285
+ # TODO: test for the appropriate pattern of callbacks
286
+ end
287
+ =end
288
+
289
+ specify "should be able to draw an empty circle onto the canvas"
290
+ =begin
291
+ do
292
+ x = 100
293
+ y = 200
294
+ r = 5
295
+ pdf = PDF::Wrapper.new
296
+ pdf.circle(x,y,r)
297
+
298
+ receiver = PDF::Reader::RegisterReceiver.new
299
+ reader = PDF::Reader.string(pdf.render, receiver)
300
+
301
+ # TODO: test for the appropriate pattern of callbacks
302
+ end
303
+ =end
304
+
305
+ specify "should be able to draw a filled circle onto the canvas"
306
+ =begin
307
+ do
308
+ x = 100
309
+ y = 200
310
+ r = 5
311
+ pdf = PDF::Wrapper.new
312
+ pdf.circle(x,y,r, :fill_color => :red)
313
+
314
+ receiver = PDF::Reader::RegisterReceiver.new
315
+ reader = PDF::Reader.string(pdf.render, receiver)
316
+
317
+ # TODO: test for the appropriate pattern of callbacks
318
+ end
319
+ =end
320
+
321
+ specify "should be able to add ascii text to the canvas"
322
+ =begin
323
+ do
324
+ msg = "Chunky Bacon"
325
+ pdf = PDF::Wrapper.new
326
+ pdf.text msg
327
+
328
+ receiver = PageTextReceiver.new
329
+ reader = PDF::Reader.string(pdf.render, receiver)
330
+
331
+ # TODO: test for the appropriate text on the page. Need to fix unicode spport in pdf-reader first
332
+ #puts receiver.content.inspect
333
+ end
334
+ =end
335
+
336
+ specify "should be able to add unicode text to the canvas"
337
+ =begin
338
+ do
339
+ msg = "メインページ"
340
+ pdf = PDF::Wrapper.new
341
+ pdf.text msg
342
+
343
+ receiver = PageTextReceiver.new
344
+ reader = PDF::Reader.string(pdf.render, receiver)
345
+
346
+ # TODO: test for the appropriate text on the page. Need to fix unicode spport in pdf-reader first
347
+ #puts receiver.content.inspect
348
+ end
349
+ =end
350
+
351
+ specify "should be able to add text to the canvas in a bounding box using the cell method"
352
+ =begin
353
+ do
354
+ msg = "メインページ"
355
+ pdf = PDF::Wrapper.new
356
+ pdf.text msg
357
+
358
+ receiver = PageTextReceiver.new
359
+ reader = PDF::Reader.string(pdf.render, receiver)
360
+
361
+ # TODO: test for the appropriate text on the page. Need to fix unicode spport in pdf-reader first
362
+ #puts receiver.content.inspect
363
+ end
364
+ =end
365
+
366
+ specify "should be able to render to a file" do
367
+ # generate a PDF
368
+ msg = "Chunky Bacon"
369
+ pdf = PDF::Wrapper.new
370
+ pdf.text msg
371
+
372
+ # write the PDF to a temp file
373
+ tmp = Tempfile.open("siftr")
374
+ tmp.close
375
+ pdf.render_to_file(tmp.path)
376
+
377
+ # ensure an actual PDF was written out
378
+ File.open(tmp.path, "r") do |f|
379
+ f.read(4).should eql("%PDF")
380
+ end
381
+
382
+ # remove our temp file
383
+ tmp.unlink
384
+ end
385
+
386
+ specify "should be able to detect the filetype of an image" do
387
+ pdf = PDF::Wrapper.new
388
+ pdf.detect_image_type(File.dirname(__FILE__) + "/data/google.png").should eql(:png)
389
+ pdf.detect_image_type(File.dirname(__FILE__) + "/data/graph.svg").should eql(:svg)
390
+ pdf.detect_image_type(File.dirname(__FILE__) + "/data/shipsail.jpg").should eql(nil)
391
+ end
392
+
393
+ specify "should be able to calculate the height of a string of text" do
394
+ pdf = PDF::Wrapper.new
395
+ opts = {:font_size => 16, :font => "Sans Serif", :alignment => :left, :justify => false }
396
+ pdf.text_height(@medstr, pdf.body_width, opts).should eql(49)
397
+ end
398
+
399
+ specify "should be able to draw a table on the canvas"
400
+
401
+ specify "should leave the cursor in the bottom left when adding a table" do
402
+ pdf = PDF::Wrapper.new
403
+ data = [%w{head1 head2},%w{data1 data2}]
404
+ pdf.table(data, :left => pdf.margin_left)
405
+ x,y = pdf.current_point
406
+ x.to_i.should eql(pdf.margin_left)
407
+ end
408
+
409
+ specify "should default to using as much available space when adding a table that isn't left aligned with the left margin" do
410
+ pdf = PDF::Wrapper.new
411
+ data = [%w{head1 head2},%w{data1 data2}]
412
+ pdf.table(data, :left => 100)
413
+ x,y = pdf.current_point
414
+ x.to_i.should eql(100)
415
+ end
416
+
417
+ specify "should raise an exception if build_pango_layout is passed anything other than a string" do
418
+ pdf = PDF::Wrapper.new
419
+ lambda { pdf.build_pango_layout(10) }.should raise_error(ArgumentError)
420
+
421
+ end
422
+
423
+ if RUBY_VERSION >= "1.9"
424
+ specify "should accept non UTF-8 strings to build_pango_layout and convert them on the fly" do
425
+ pdf = PDF::Wrapper.new
426
+
427
+ # all three of these files have the same content, but in different encodings
428
+ iso2022_str = File.open(File.dirname(__FILE__) + "/data/shift_jis.txt", "r:ISO-2022-JP") { |f| f.read }.strip!
429
+ shiftjis_str = File.open(File.dirname(__FILE__) + "/data/iso-2022-jp.txt", "r:Shift_JIS") { |f| f.read }.strip!
430
+ utf8_str = File.open(File.dirname(__FILE__) + "/data/utf8.txt", "r:UTF-8") { |f| f.read }.strip!
431
+
432
+ pdf.build_pango_layout(shiftjis_str)
433
+ pdf.build_pango_layout(iso2022_str)
434
+
435
+ # TODO: improve this spec using mocks. Atm, I'm assume that if build_pango_layout didn't raise an exception when
436
+ # passed in the non UTF-8 strings, then all worked fine. yuck.
437
+ end
438
+
439
+ specify "should raise an error when a string that isn't convertable to UTF-8 is passed into build_pango_layout()"
440
+ end
441
+
442
+ specify "should be able to determine if a requested colour is valid or not" do
443
+ pdf = PDF::Wrapper.new
444
+ pdf.validate_color(:black).should be_true
445
+ pdf.validate_color([1,0,0]).should be_true
446
+ pdf.validate_color([1,0,0,0.5]).should be_true
447
+ lambda { pdf.validate_color(:ponies)}.should raise_error(ArgumentError)
448
+ lambda { pdf.validate_color([1])}.should raise_error(ArgumentError)
449
+ lambda { pdf.validate_color([1000, 255, 0])}.should raise_error(ArgumentError)
450
+ end
451
+ end