prawn 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,16 +1,18 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
- require 'spec/rake/spectask'
3
+ require 'rake/testtask'
4
4
  require "rake/rdoctask"
5
5
  require "rake/gempackagetask"
6
6
 
7
- PRAWN_VERSION = "0.1.1"
7
+ PRAWN_VERSION = "0.1.2"
8
8
 
9
- task :default => [:spec]
10
-
11
- desc "Run all specs"
12
- Spec::Rake::SpecTask.new('spec') do |t|
13
- t.spec_files = FileList['spec/**/*.rb']
9
+ task :default => [:test]
10
+
11
+ desc "Run all tests, test-spec and mocha required"
12
+ Rake::TestTask.new do |test|
13
+ test.libs << "spec"
14
+ test.test_files = Dir[ "spec/*_spec.rb" ]
15
+ test.verbose = true
14
16
  end
15
17
 
16
18
  desc "Show library's code statistics"
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1 @@
1
+ x^E�A�1DQ$ !p$DB$D������3�_���,(�d�_ˣ��j�wa����W9$촙w�աɳ�Km��Wg��۹#���M�h��D�#C�_[A���ş�z��r<:�<l�5V�(�L����c�2w?��s3 Q���b ��;��O����ݼUD�W�
Binary file
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ # PNG files come in different flavours - 5 of them. This example embeds
4
+ # one of each type as proof that they all work.
5
+
6
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
7
+ require "prawn"
8
+
9
+ images = [
10
+ ["Type 0", "#{Prawn::BASEDIR}/data/images/web-links.png"],
11
+ ["Type 2", "#{Prawn::BASEDIR}/data/images/ruport.png"],
12
+ ["Type 3", "#{Prawn::BASEDIR}/data/images/rails.png"],
13
+ ["Type 4", "#{Prawn::BASEDIR}/data/images/page_white_text.png"],
14
+ ["Type 6", "#{Prawn::BASEDIR}/data/images/dice.png"],
15
+ ]
16
+
17
+ Prawn::Document.generate("png_types.pdf", :page_size => "A5") do
18
+ images.each do |header, file|
19
+ start_new_page unless header.include?("0")
20
+ text header
21
+ image file, :at => [50,450]
22
+ end
23
+ end
@@ -23,11 +23,12 @@ end
23
23
 
24
24
  require 'ttf'
25
25
 
26
- module Prawn
26
+ module Prawn
27
27
  file = __FILE__
28
28
  file = File.readlink(file) if File.symlink?(file)
29
29
  dir = File.dirname(file)
30
30
 
31
31
  # The base source directory for Prawn as installed on the system
32
- BASEDIR = File.expand_path(File.join(dir, '..'))
32
+ BASEDIR = File.expand_path(File.join(dir, '..'))
33
+ VERSION = "0.1.2"
33
34
  end
@@ -59,6 +59,7 @@ module Prawn
59
59
  # <tt>:top_margin</tt>:: Sets the top margin in points [ 0.5 inch]
60
60
  # <tt>:bottom_margin</tt>:: Sets the bottom margin in points [0.5 inch]
61
61
  # <tt>:skip_page_creation</tt>:: Creates a document without starting the first page [false]
62
+ # <tt>:compress</tt>:: Compresses content streams before rendering them [false]
62
63
  #
63
64
  #
64
65
  # # New document, US Letter paper, portrait orientation
@@ -77,9 +78,10 @@ module Prawn
77
78
  @pages = ref(:Type => :Pages, :Count => 0, :Kids => [])
78
79
  @root = ref(:Type => :Catalog, :Pages => @pages)
79
80
  @page_start_proc = options[:on_page_start]
80
- @page_stop_proc = options[:on_page_end]
81
+ @page_stop_proc = options[:on_page_stop]
81
82
  @page_size = options[:page_size] || "LETTER"
82
83
  @page_layout = options[:page_layout] || :portrait
84
+ @compress = options[:compress] || false
83
85
 
84
86
  @margins = { :left => options[:left_margin] || 36,
85
87
  :right => options[:right_margin] || 36,
@@ -239,6 +241,11 @@ module Prawn
239
241
  yield
240
242
  fields.each { |f| send("#{f}=", stored[f]) }
241
243
  end
244
+
245
+ def compression_enabled?
246
+ @compress
247
+ end
248
+
242
249
 
243
250
  private
244
251
 
@@ -286,6 +293,7 @@ module Prawn
286
293
  def finish_page_content
287
294
  @page_stop_proc[self] if @page_stop_proc
288
295
  add_content "Q"
296
+ @page_content.compress_stream if compression_enabled?
289
297
  @page_content.data[:Length] = @page_content.stream.size
290
298
  end
291
299
 
@@ -37,16 +37,35 @@ module Prawn
37
37
  # text will be kerned by default. You can disable this feature by passing
38
38
  # <tt>:kerning => false</tt>.
39
39
  #
40
+ # == Encoding
41
+ #
40
42
  # Note that strings passed to this function should be encoded as UTF-8.
41
- # If you get unexpected characters appearing in your rendered
42
- # document, check this.
43
+ # If you get unexpected characters appearing in your rendered document,
44
+ # check this.
45
+ #
46
+ # If the current font is a built-in one, although the string must be
47
+ # encoded as UTF-8, only characters that are available in ISO-8859-1
48
+ # are allowed.
43
49
  #
44
50
  # If an empty box is rendered to your PDF instead of the character you
45
51
  # wanted it usually means the current font doesn't include that character.
46
52
  #
47
53
  def text(text,options={})
54
+ # ensure a valid font is selected
55
+ font "Helvetica" unless fonts[@font]
56
+
57
+ # we'll be messing with the strings encoding, don't change the users
58
+ # original string
59
+ text = text.dup
60
+
48
61
  # check the string is encoded sanely
49
- normalize_encoding(text)
62
+ # - UTF-8 for TTF fonts
63
+ # - ISO-8859-1 for Built-In fonts
64
+ if using_builtin_font?
65
+ normalize_builtin_encoding(text)
66
+ else
67
+ normalize_ttf_encoding(text)
68
+ end
50
69
 
51
70
  if options.key?(:kerning)
52
71
  options[:kerning] = false unless font_metrics.has_kerning_data?
@@ -54,9 +73,6 @@ module Prawn
54
73
  options[:kerning] = true if font_metrics.has_kerning_data?
55
74
  end
56
75
 
57
- # ensure a valid font is selected
58
- font "Helvetica" unless fonts[@font]
59
-
60
76
  return wrapped_text(text,options) unless options[:at]
61
77
 
62
78
  x,y = translate(options[:at])
@@ -151,7 +167,8 @@ module Prawn
151
167
  alias_method :font_size=, :font_size!
152
168
 
153
169
  private
154
-
170
+
171
+
155
172
  # The current font_size being used in the document.
156
173
  #
157
174
  def current_font_size
@@ -281,11 +298,25 @@ module Prawn
281
298
  return basename
282
299
  end
283
300
 
284
- def normalize_encoding(text)
301
+ # built-in fonts only work with latin encoding, so translate the string
302
+ def normalize_builtin_encoding(text)
303
+ if text.respond_to?(:encode!)
304
+ text.encode!("ISO-8859-1")
305
+ else
306
+ require 'iconv'
307
+ text.replace Iconv.conv('ISO-8859-1', 'utf-8', text)
308
+ end
309
+ rescue
310
+ raise Prawn::Errors::IncompatibleStringEncoding, "When using a " +
311
+ "builtin font, only characters that exist in " +
312
+ "WinAnsi/ISO-8859-1 are allowed."
313
+ end
314
+
315
+ def normalize_ttf_encoding(text)
285
316
  # TODO: if the current font is a built in one, we can't use the utf-8
286
317
  # string provided by the user. We should convert it to WinAnsi or
287
318
  # MacRoman or some such.
288
- if text.respond_to?(:"encode!")
319
+ if text.respond_to?(:encode!)
289
320
  # if we're running under a M17n aware VM, ensure the string provided is
290
321
  # UTF-8 (by converting it if necessary)
291
322
  begin
@@ -316,7 +347,7 @@ module Prawn
316
347
  fonts[name] ||= ref(:Type => :Font,
317
348
  :Subtype => :Type1,
318
349
  :BaseFont => name.to_sym,
319
- :Encoding => :MacRomanEncoding)
350
+ :Encoding => :WinAnsiEncoding)
320
351
  return name
321
352
  end
322
353
 
@@ -341,6 +372,9 @@ module Prawn
341
372
  @fonts ||= {}
342
373
  end
343
374
 
375
+ def using_builtin_font?
376
+ fonts[@font].data[:Subtype] == :Type1
377
+ end
344
378
  end
345
379
  end
346
380
  end
@@ -89,6 +89,8 @@ module Prawn
89
89
  Float(bbox[3] - bbox[1]) * font_size / 1000.0
90
90
  end
91
91
 
92
+ # calculates the width of the supplied string.
93
+ # String *must* be encoded as iso-8859-1
92
94
  def string_width(string, font_size, options = {})
93
95
  scale = font_size / 1000.0
94
96
 
@@ -101,14 +103,18 @@ module Prawn
101
103
  end
102
104
  end
103
105
  else
104
- string.unpack("U*").inject(0) do |s,r|
106
+ string.unpack("C*").inject(0) do |s,r|
105
107
  s + latin_glyphs_table[r]
106
108
  end * scale
107
109
  end
108
110
  end
109
111
 
112
+ # converts a string into an array with spacing offsets
113
+ # bewteen characters that need to be kerned
114
+ #
115
+ # String *must* be encoded as iso-8859-1
110
116
  def kern(string)
111
- kerned = string.unpack("U*").inject([]) do |a,r|
117
+ kerned = string.unpack("C*").inject([]) do |a,r|
112
118
  if a.last.is_a? Array
113
119
  if kern = latin_kern_pairs_table[[a.last.last, r]]
114
120
  a << kern << [r]
@@ -122,7 +128,8 @@ module Prawn
122
128
  end
123
129
 
124
130
  kerned.map { |r|
125
- i = r.is_a?(Array) ? r.pack("U*") : r
131
+ i = r.is_a?(Array) ? r.pack("C*") : r
132
+ i.force_encoding("ISO-8859-1") if i.respond_to?(:force_encoding)
126
133
  i.is_a?(Numeric) ? -i : i
127
134
  }
128
135
  end
@@ -178,6 +185,10 @@ module Prawn
178
185
  false
179
186
  end
180
187
 
188
+ # perform any changes to the string that need to happen
189
+ # before it is rendered to the canvas
190
+ #
191
+ # String *must* be encoded as iso-8859-1
181
192
  def convert_text(text, options={})
182
193
  options[:kerning] ? kern(text) : text
183
194
  end
@@ -145,22 +145,12 @@ module Prawn
145
145
  raise ArgumentError, 'PNG uses more than 8 bits'
146
146
  end
147
147
 
148
- case png.color_type
149
- when 0
150
- ncolor = 1
148
+ case png.pixel_bytes
149
+ when 1
151
150
  color = :DeviceGray
152
- when 2
153
- ncolor = 3
154
- color = :DeviceRGB
155
151
  when 3
156
- ncolor = 1
157
- color = :DeviceRGB
158
- when 6
159
- ncolor = 3
160
- color = :DeviceRGB
161
- else
162
- raise ArgumentError, "PNG has unsupported color type"
163
- end
152
+ color = :DeviceRGB
153
+ end
164
154
 
165
155
  # build the image dict
166
156
  obj = ref(:Type => :XObject,
@@ -175,7 +165,7 @@ module Prawn
175
165
 
176
166
  unless png.alpha_channel
177
167
  obj.data[:DecodeParms] = {:Predictor => 15,
178
- :Colors => ncolor,
168
+ :Colors => png.pixel_bytes,
179
169
  :Columns => png.width}
180
170
  end
181
171
 
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # encoding: ASCII-8BIT
2
2
 
3
3
  # png.rb : Extracts the data from a PNG that is needed for embedding
4
4
  #
@@ -84,11 +84,22 @@ module Prawn
84
84
  end
85
85
 
86
86
  # if our img_data contains alpha channel data, split it out
87
- unfilter_image_data if @color_type == 6
87
+ unfilter_image_data if alpha_channel?
88
+ end
89
+
90
+ def pixel_bytes
91
+ case @color_type
92
+ when 0, 4 then 1
93
+ when 1, 2, 6 then 3
94
+ end
88
95
  end
89
96
 
90
97
  private
91
98
 
99
+ def alpha_channel?
100
+ @color_type == 4 || @color_type == 6
101
+ end
102
+
92
103
  def paeth(a, b, c) # left, above, upper left
93
104
  p = a + b - c
94
105
  pa = (p - a).abs
@@ -104,46 +115,49 @@ module Prawn
104
115
  data = Zlib::Inflate.inflate(@img_data).unpack 'C*'
105
116
  @img_data = ""
106
117
  @alpha_channel = ""
107
- scanline_length = 4 * @width + 1 # for filter
118
+
119
+ # each pixel has the color bytes, plus a byte of alpha channel
120
+ pixel_length = pixel_bytes + 1
121
+ scanline_length = pixel_length * @width + 1 # for filter
108
122
  row = 0
109
123
  pixels = []
110
124
  until data.empty? do
111
125
  row_data = data.slice! 0, scanline_length
112
126
  filter = row_data.shift
113
127
  case filter
114
- when 0 then # None
115
- when 1 then # Sub
128
+ when 0 # None
129
+ when 1 # Sub
116
130
  row_data.each_with_index do |byte, index|
117
- left = index < 4 ? 0 : row_data[index - 4]
131
+ left = index < pixel_length ? 0 : row_data[index - pixel_length]
118
132
  row_data[index] = (byte + left) % 256
119
133
  #p [byte, left, row_data[index]]
120
134
  end
121
- when 2 then # Up
135
+ when 2 # Up
122
136
  row_data.each_with_index do |byte, index|
123
- col = index / 4
124
- upper = row == 0 ? 0 : pixels[row-1][col][index % 4]
137
+ col = index / pixel_length
138
+ upper = row == 0 ? 0 : pixels[row-1][col][index % pixel_length]
125
139
  row_data[index] = (upper + byte) % 256
126
140
  end
127
- when 3 then # Average
141
+ when 3 # Average
128
142
  row_data.each_with_index do |byte, index|
129
- col = index / 4
130
- upper = row == 0 ? 0 : pixels[row-1][col][index % 4]
131
- left = index < 4 ? 0 : row_data[index - 4]
143
+ col = index / pixel_length
144
+ upper = row == 0 ? 0 : pixels[row-1][col][index % pixel_length]
145
+ left = index < pixel_length ? 0 : row_data[index - pixel_length]
132
146
 
133
147
  row_data[index] = (byte + ((left + upper)/2).floor) % 256
134
148
  end
135
- when 4 then # Paeth
149
+ when 4 # Paeth
136
150
  left = upper = upper_left = nil
137
151
  row_data.each_with_index do |byte, index|
138
- col = index / 4
152
+ col = index / pixel_length
139
153
 
140
- left = index < 4 ? 0 : row_data[index - 4]
154
+ left = index < pixel_length ? 0 : row_data[index - pixel_length]
141
155
  if row == 0 then
142
156
  upper = upper_left = 0
143
157
  else
144
- upper = pixels[row-1][col][index % 4]
158
+ upper = pixels[row-1][col][index % pixel_length]
145
159
  upper_left = col == 0 ? 0 :
146
- pixels[row-1][col-1][index % 4]
160
+ pixels[row-1][col-1][index % pixel_length]
147
161
  end
148
162
 
149
163
  paeth = paeth left, upper, upper_left
@@ -155,7 +169,7 @@ module Prawn
155
169
  end
156
170
 
157
171
  pixels << []
158
- row_data.each_slice 4 do |slice|
172
+ row_data.each_slice pixel_length do |slice|
159
173
  pixels.last << slice
160
174
  end
161
175
  row += 1
@@ -164,8 +178,8 @@ module Prawn
164
178
  # convert the pixel data to seperate strings for colours and alpha
165
179
  pixels.each do |row|
166
180
  row.each do |pixel|
167
- @img_data << pixel[0,3].pack("C*")
168
- @alpha_channel << pixel[3]
181
+ @img_data << pixel[0,pixel_bytes].pack("C*")
182
+ @alpha_channel << pixel.last
169
183
  end
170
184
  end
171
185
 
@@ -6,6 +6,8 @@
6
6
  #
7
7
  # This is free software. Please see the LICENSE and COPYING files for details.
8
8
 
9
+ require 'zlib'
10
+
9
11
  module Prawn
10
12
 
11
13
  class Reference #:nodoc:
@@ -17,6 +19,7 @@ module Prawn
17
19
  @identifier = id
18
20
  @gen = 0
19
21
  @data = data
22
+ @compressed = false
20
23
  end
21
24
 
22
25
  def object
@@ -29,13 +32,19 @@ module Prawn
29
32
  end
30
33
 
31
34
  def <<(data)
35
+ raise 'Cannot add data to a stream that is compressed' if @compressed
32
36
  (@stream ||= "") << data
33
37
  end
34
38
 
35
39
  def to_s
36
40
  "#{@identifier} #{gen} R"
37
41
  end
38
-
42
+
43
+ def compress_stream
44
+ @stream = Zlib::Deflate.deflate(@stream)
45
+ @data[:Filter] = :FlateDecode
46
+ @compressed = true
47
+ end
39
48
  end
40
49
 
41
50
  module_function
@@ -11,7 +11,7 @@ describe "A bounding box" do
11
11
  @pdf.text "The rain in Spain falls mainly on the plains."
12
12
  end
13
13
 
14
- @pdf.y.should be_close(458.384, 0.001)
14
+ @pdf.y.should.be.close 458.384, 0.001
15
15
  end
16
16
 
17
17
  end
@@ -49,7 +49,7 @@ describe "When beginning each new page" do
49
49
  it "should execute the lambda specified by on_page_start" do
50
50
  on_start = mock("page_start_proc")
51
51
 
52
- on_start.should_receive(:[]).exactly(3).times
52
+ on_start.expects(:[]).times(3)
53
53
 
54
54
  pdf = Prawn::Document.new(:on_page_start => on_start)
55
55
  pdf.start_new_page
@@ -65,14 +65,38 @@ describe "When ending each page" do
65
65
 
66
66
  on_end = mock("page_end_proc")
67
67
 
68
- on_end.should_receive(:[]).exactly(3).times
68
+ on_end.expects(:[]).times(3)
69
69
 
70
- pdf = Prawn::Document.new(:on_page_end => on_end)
70
+ pdf = Prawn::Document.new(:on_page_stop => on_end)
71
71
  pdf.start_new_page
72
72
  pdf.start_new_page
73
73
  pdf.render
74
74
  end
75
75
 
76
+ it "should not compress the page content stream if compression is disabled" do
77
+
78
+ pdf = Prawn::Document.new(:compress => false)
79
+ content_stub = pdf.ref({})
80
+ content_stub.stubs(:compress_stream).returns(true)
81
+ content_stub.expects(:compress_stream).never
82
+
83
+ pdf.instance_variable_set("@page_content", content_stub)
84
+ pdf.text "Hi There" * 20
85
+ pdf.render
86
+ end
87
+
88
+ it "should compress the page content stream if compression is enabled" do
89
+
90
+ pdf = Prawn::Document.new(:compress => true)
91
+ content_stub = pdf.ref({})
92
+ content_stub.stubs(:compress_stream).returns(true)
93
+ content_stub.expects(:compress_stream).once
94
+
95
+ pdf.instance_variable_set("@page_content", content_stub)
96
+ pdf.text "Hi There" * 20
97
+ pdf.render
98
+ end
99
+
76
100
  end
77
101
 
78
102
  class PageDetails
@@ -132,8 +156,8 @@ describe "The mask() feature" do
132
156
  @pdf.mask(:y, :line_width) do
133
157
  @pdf.y = y + 1
134
158
  @pdf.line_width = line_width + 1
135
- @pdf.y.should_not == y
136
- @pdf.line_width.should_not == line_width
159
+ @pdf.y.should.not == y
160
+ @pdf.line_width.should.not == line_width
137
161
  end
138
162
  @pdf.y.should == y
139
163
  @pdf.line_width.should == line_width
@@ -146,7 +170,7 @@ describe "The render() feature" do
146
170
  @pdf = Prawn::Document.new(:page_size => "A4", :page_layout => :landscape)
147
171
  @pdf.line [100,100], [200,200]
148
172
  str = @pdf.render
149
- str.encoding.to_s.should eql("ASCII-8BIT")
173
+ str.encoding.to_s.should == "ASCII-8BIT"
150
174
  end
151
175
  end
152
176
  end
@@ -230,21 +230,21 @@ describe "When using painting shortcuts" do
230
230
  before(:each) { create_pdf }
231
231
 
232
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)
233
+ @pdf.expects(:line_to).with([100,100])
234
+ @pdf.expects(:stroke)
235
235
 
236
236
  @pdf.stroke_line_to [100,100]
237
237
  end
238
238
 
239
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)
240
+ @pdf.expects(:line_to).with([100,100])
241
+ @pdf.expects(:fill)
242
242
 
243
243
  @pdf.fill_line_to [100,100]
244
244
  end
245
245
 
246
246
  it "should not break method_missing" do
247
247
  lambda { @pdf.i_have_a_pretty_girlfriend_named_jia }.
248
- should raise_error(NoMethodError)
248
+ should.raise(NoMethodError)
249
249
  end
250
250
  end
@@ -33,10 +33,10 @@ describe "the image() function" do
33
33
  images = observer(ImageObserver)
34
34
 
35
35
  # there should be 2 images in the page resources
36
- images.page_xobjects.first.size.should eql(2)
36
+ images.page_xobjects.first.size.should == 2
37
37
 
38
38
  # but only 1 image xobject
39
- @output.scan(/\/Type \/XObject/).size.should eql(1)
39
+ @output.scan(/\/Type \/XObject/).size.should == 1
40
40
  end
41
41
  end
42
42
 
@@ -16,10 +16,10 @@ describe "When reading a JPEG file" do
16
16
  it "should read the basic attributes correctly" do
17
17
  jpg = Prawn::Images::JPG.new(@img_data)
18
18
 
19
- jpg.width.should eql(604)
20
- jpg.height.should eql(453)
21
- jpg.bits.should eql(8)
22
- jpg.channels.should eql(3)
19
+ jpg.width.should == 604
20
+ jpg.height.should == 453
21
+ jpg.bits.should == 8
22
+ jpg.channels.should == 3
23
23
  end
24
24
  end
25
25
 
@@ -1,4 +1,4 @@
1
- # encoding: ASCII-8BIT
1
+ # encoding: utf-8
2
2
 
3
3
  require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
4
4
 
@@ -6,23 +6,24 @@ describe "adobe font metrics" do
6
6
 
7
7
  setup do
8
8
  @times = Prawn::Font::Metrics["Times-Roman"]
9
+ @iconv = Iconv.new('ISO-8859-1', 'utf-8')
9
10
  end
10
11
 
11
12
  it "should calculate string width taking into account accented characters" do
12
- @times.string_width("é", 12).should == @times.string_width("e", 12)
13
+ @times.string_width(@iconv.iconv("é"), 12).should == @times.string_width("e", 12)
13
14
  end
14
15
 
15
16
  it "should calculate string width taking into account kerning pairs" do
16
- @times.string_width("To", 12).should == 13.332
17
- @times.string_width("To", 12, :kerning => true).should == 12.372
18
- @times.string_width("Tö", 12, :kerning => true).should == 12.372
17
+ @times.string_width(@iconv.iconv("To"), 12).should == 13.332
18
+ @times.string_width(@iconv.iconv("To"), 12, :kerning => true).should == 12.372
19
+ @times.string_width(@iconv.iconv("Tö"), 12, :kerning => true).should == 12.372
19
20
  end
20
21
 
21
22
  it "should kern a string" do
22
- @times.kern("To").should == ["T", 80, "o"]
23
- @times.kern("Télé").should == ["T", 70, "\303\251l\303\251"]
24
- @times.kern("Technology").should == ["T", 70, "echnology"]
25
- @times.kern("Technology...").should == ["T", 70, "echnology", 65, "..."]
23
+ @times.kern(@iconv.iconv("To")).should == ["T", 80, "o"]
24
+ @times.kern(@iconv.iconv("Télé")).should == ["T", 70, @iconv.iconv("élé")]
25
+ @times.kern(@iconv.iconv("Technology")).should == ["T", 70, "echnology"]
26
+ @times.kern(@iconv.iconv("Technology...")).should == ["T", 70, "echnology", 65, "..."]
26
27
  end
27
28
 
28
29
  end
@@ -57,4 +58,4 @@ describe "ttf font metrics" do
57
58
  "\000\021\000\021\000\021"]
58
59
  end
59
60
 
60
- end
61
+ end
@@ -48,7 +48,7 @@ describe "PDF Object Serialization" do
48
48
 
49
49
  it "should not convert a whitespace containing Ruby symbol to a PDF name" do
50
50
  lambda { Prawn::PdfObject(:"My Symbol With Spaces") }.
51
- should raise_error(Prawn::Errors::FailedObjectConversion)
51
+ should.raise(Prawn::Errors::FailedObjectConversion)
52
52
  end
53
53
 
54
54
  it "should convert a Ruby array to PDF Array when inside a content stream" do
@@ -91,7 +91,7 @@ describe "PDF Object Serialization" do
91
91
 
92
92
  it "should not allow keys other than strings or symbols for PDF dicts" do
93
93
  lambda { Prawn::PdfObject(:foo => :bar, :baz => :bang, 1 => 4) }.
94
- should raise_error(Prawn::Errors::FailedObjectConversion)
94
+ should.raise(Prawn::Errors::FailedObjectConversion)
95
95
  end
96
96
 
97
97
  it "should convert a Prawn::Reference to a PDF indirect object reference" do
@@ -1,35 +1,161 @@
1
- # encoding: utf-8
1
+ # encoding: ASCII-8BIT
2
2
 
3
3
  # Spec'ing the PNG class. Not complete yet - still needs to check the
4
4
  # contents of palette and transparency to ensure they're correct.
5
5
  # Need to find files that have these sections first.
6
+ #
7
+ # see http://www.w3.org/TR/PNG/ for a detailed description of the PNG spec,
8
+ # particuarly Table 11.1 for the different color types
6
9
 
7
- require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
10
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
8
11
 
9
- describe "When reading an RGB PNG file" do
12
+ describe "When reading a greyscale PNG file (color type 0)" do
13
+
14
+ before(:each) do
15
+ @filename = "#{Prawn::BASEDIR}/data/images/web-links.png"
16
+ @data_filename = "#{Prawn::BASEDIR}/data/images/web-links.dat"
17
+ @img_data = File.open(@filename, "rb") { |f| f.read }
18
+ end
19
+
20
+ it "should read the attributes from the header chunk correctly" do
21
+ png = Prawn::Images::PNG.new(@img_data)
22
+
23
+ png.width.should == 21
24
+ png.height.should == 14
25
+ png.bits.should == 8
26
+ png.color_type.should == 0
27
+ png.compression_method.should == 0
28
+ png.filter_method.should == 0
29
+ png.interlace_method.should == 0
30
+ end
31
+
32
+ it "should read the image data chunk correctly" do
33
+ png = Prawn::Images::PNG.new(@img_data)
34
+ data = File.open(@data_filename, "rb") { |f| f.read }
35
+ png.img_data.should == data
36
+ end
37
+ end
38
+
39
+ describe "When reading an RGB PNG file (color type 2)" do
10
40
 
11
41
  before(:each) do
12
42
  @filename = "#{Prawn::BASEDIR}/data/images/ruport.png"
13
43
  @data_filename = "#{Prawn::BASEDIR}/data/images/ruport_data.dat"
14
44
  @img_data = File.open(@filename, "rb") { |f| f.read }
15
45
  end
16
-
46
+
47
+ it "should read the attributes from the header chunk correctly" do
48
+ png = Prawn::Images::PNG.new(@img_data)
49
+
50
+ png.width.should == 258
51
+ png.height.should == 105
52
+ png.bits.should == 8
53
+ png.color_type.should == 2
54
+ png.compression_method.should == 0
55
+ png.filter_method.should == 0
56
+ png.interlace_method.should == 0
57
+ end
58
+
59
+ it "should read the image data chunk correctly" do
60
+ png = Prawn::Images::PNG.new(@img_data)
61
+ data = File.open(@data_filename, "rb") { |f| f.read }
62
+ png.img_data.should == data
63
+ end
64
+ end
65
+
66
+ # TODO: describe "When reading an indexed color PNG file wiih transparency (color type 3)"
67
+
68
+ describe "When reading an indexed color PNG file (color type 3)" do
69
+
70
+ before(:each) do
71
+ @filename = "#{Prawn::BASEDIR}/data/images/rails.png"
72
+ @data_filename = "#{Prawn::BASEDIR}/data/images/rails.dat"
73
+ @img_data = File.open(@filename, "rb") { |f| f.read }
74
+ end
75
+
17
76
  it "should read the attributes from the header chunk correctly" do
18
77
  png = Prawn::Images::PNG.new(@img_data)
19
-
20
- png.width.should eql(258)
21
- png.height.should eql(105)
22
- png.bits.should eql(8)
23
- png.color_type.should eql(2)
24
- png.compression_method.should eql(0)
25
- png.filter_method.should eql(0)
26
- png.interlace_method.should eql(0)
78
+
79
+ png.width.should == 50
80
+ png.height.should == 64
81
+ png.bits.should == 8
82
+ png.color_type.should == 3
83
+ png.compression_method.should == 0
84
+ png.filter_method.should == 0
85
+ png.interlace_method.should == 0
27
86
  end
28
87
 
29
88
  it "should read the image data chunk correctly" do
30
89
  png = Prawn::Images::PNG.new(@img_data)
31
90
  data = File.open(@data_filename, "rb") { |f| f.read }
32
- png.img_data.should eql(data)
33
- end
91
+ png.img_data.should == data
92
+ end
93
+ end
94
+
95
+ describe "When reading a greyscale+alpha PNG file (color type 4)" do
96
+
97
+ before(:each) do
98
+ @filename = "#{Prawn::BASEDIR}/data/images/page_white_text.png"
99
+ @data_filename = "#{Prawn::BASEDIR}/data/images/page_white_text.dat"
100
+ @alpha_data_filename = "#{Prawn::BASEDIR}/data/images/page_white_text.alpha"
101
+ @img_data = File.open(@filename, "rb") { |f| f.read }
102
+ end
103
+
104
+ it "should read the attributes from the header chunk correctly" do
105
+ png = Prawn::Images::PNG.new(@img_data)
106
+
107
+ png.width.should == 16
108
+ png.height.should == 16
109
+ png.bits.should == 8
110
+ png.color_type.should == 4
111
+ png.compression_method.should == 0
112
+ png.filter_method.should == 0
113
+ png.interlace_method.should == 0
114
+ end
115
+
116
+ it "should correctly return the raw image data (with no alpha channel) from the image data chunk" do
117
+ png = Prawn::Images::PNG.new(@img_data)
118
+ data = File.open(@data_filename, rb_flag) { |f| f.read }
119
+ png.img_data.should == data
120
+ end
121
+
122
+ it "should correctly extract the alpha channel data from the image data chunk" do
123
+ png = Prawn::Images::PNG.new(@img_data)
124
+ data = File.open(@alpha_data_filename, rb_flag) { |f| f.read }
125
+ png.alpha_channel.should == data
126
+ end
34
127
  end
35
128
 
129
+ describe "When reading an RGB+alpha PNG file (color type 6)" do
130
+
131
+ before(:each) do
132
+ @filename = "#{Prawn::BASEDIR}/data/images/dice.png"
133
+ @data_filename = "#{Prawn::BASEDIR}/data/images/dice.dat"
134
+ @alpha_data_filename = "#{Prawn::BASEDIR}/data/images/dice.alpha"
135
+ @img_data = File.open(@filename, "rb") { |f| f.read }
136
+ end
137
+
138
+ it "should read the attributes from the header chunk correctly" do
139
+ png = Prawn::Images::PNG.new(@img_data)
140
+
141
+ png.width.should == 320
142
+ png.height.should == 240
143
+ png.bits.should == 8
144
+ png.color_type.should == 6
145
+ png.compression_method.should == 0
146
+ png.filter_method.should == 0
147
+ png.interlace_method.should == 0
148
+ end
149
+
150
+ it "should correctly return the raw image data (with no alpha channel) from the image data chunk" do
151
+ png = Prawn::Images::PNG.new(@img_data)
152
+ data = File.open(@data_filename, rb_flag) { |f| f.read }
153
+ png.img_data.should == data
154
+ end
155
+
156
+ it "should correctly extract the alpha channel data from the image data chunk" do
157
+ png = Prawn::Images::PNG.new(@img_data)
158
+ data = File.open(@alpha_data_filename, rb_flag) { |f| f.read }
159
+ png.alpha_channel.should == data
160
+ end
161
+ end
@@ -26,4 +26,17 @@ describe "A Reference object" do
26
26
  "\nBT\n/F1 12 Tf\n72 712 Td\n( A stream ) Tj\nET" +
27
27
  "\nendstream\nendobj\n"
28
28
  end
29
+
30
+ it "should compress a stream upon request" do
31
+ ref = Prawn::Reference(2,{})
32
+ ref << "Hi There " * 20
33
+
34
+ cref = Prawn::Reference(2,{})
35
+ cref << "Hi There " * 20
36
+ cref.compress_stream
37
+
38
+ assert cref.stream.size < ref.stream.size,
39
+ "compressed stream expected to be smaller than source but wasn't"
40
+ cref.data[:Filter].should == :FlateDecode
41
+ end
29
42
  end
@@ -3,12 +3,19 @@
3
3
  puts "Prawn specs: Running on Ruby Version: #{RUBY_VERSION}"
4
4
 
5
5
  require "rubygems"
6
- require "spec"
6
+ require "test/spec"
7
+ require "mocha"
7
8
  $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
8
9
  require "prawn"
9
10
  gem 'pdf-reader', ">=0.7.3"
10
11
  require "pdf/reader"
11
12
 
13
+ module Prawn
14
+ class Document
15
+ public :ref
16
+ end
17
+ end
18
+
12
19
  def create_pdf
13
20
  @pdf = Prawn::Document.new(:left_margin => 0,
14
21
  :right_margin => 0,
@@ -26,4 +33,8 @@ end
26
33
  def parse_pdf_object(obj)
27
34
  PDF::Reader::Parser.new(
28
35
  PDF::Reader::Buffer.new(sio = StringIO.new(obj)), nil).parse_token
36
+ end
37
+
38
+ def rb_flag
39
+ ruby_18 { "rb" } || ruby_19 { "rb:ASCII-8BIT" }
29
40
  end
@@ -72,7 +72,7 @@ describe "A table's width" do
72
72
 
73
73
  font_height = pdf.font_metrics.font_height(12)
74
74
 
75
- table_height.should be_close(num_rows*font_height + 2*vpad*num_rows + vpad, 0.001)
75
+ table_height.should.be.close(num_rows*font_height + 2*vpad*num_rows + vpad, 0.001)
76
76
  end
77
77
 
78
78
  end
@@ -139,7 +139,7 @@ describe "A table's content" do
139
139
  data = [["foo","bar"],["baz",""]]
140
140
  @pdf = Prawn::Document.new
141
141
  @pdf.table(data)
142
- }.should_not raise_error
142
+ }.should.not.raise
143
143
  end
144
144
 
145
145
  end
@@ -71,12 +71,12 @@ describe "when drawing text" do
71
71
  position = @pdf.y
72
72
  @pdf.text "Foo"
73
73
 
74
- @pdf.y.should be_close(position - @pdf.font_metrics.font_height(12),
74
+ @pdf.y.should.be.close(position - @pdf.font_metrics.font_height(12),
75
75
  0.0001)
76
76
 
77
77
  position = @pdf.y
78
78
  @pdf.text "Foo\nBar\nBaz"
79
- @pdf.y.should be_close(position - 3*@pdf.font_metrics.font_height(12),
79
+ @pdf.y.should.be.close(position - 3*@pdf.font_metrics.font_height(12),
80
80
  0.0001)
81
81
  end
82
82
 
@@ -154,13 +154,22 @@ describe "when drawing text" do
154
154
  @pdf.text "Blaz", :at => [150,150]
155
155
  text = observer(FontObserver)
156
156
 
157
- text.page_fonts.size.should eql(2)
158
- text.page_fonts[0][0].should eql(:Helvetica)
159
- text.page_fonts[1][0].should eql(:Helvetica)
157
+ text.page_fonts.size.should == 2
158
+ text.page_fonts[0][0].should == :Helvetica
159
+ text.page_fonts[1][0].should == :Helvetica
160
160
  end
161
161
 
162
162
  it "should raise an exception when an unknown font is used" do
163
- lambda { @pdf.font "Pao bu" }.should raise_error(Prawn::Errors::UnknownFont)
163
+ lambda { @pdf.font "Pao bu" }.should.raise(Prawn::Errors::UnknownFont)
164
+ end
165
+
166
+ it "should correctly render a utf-8 string when using a built-in font" do
167
+ str = "©" # copyright symbol
168
+ @pdf.text str
169
+
170
+ # grab the text from the rendered PDF and ensure it matches
171
+ text = observer(TextObserver)
172
+ text.string.should == str
164
173
  end
165
174
 
166
175
  if "spec".respond_to?(:encode!)
@@ -168,22 +177,23 @@ describe "when drawing text" do
168
177
  it "should raise an exception when a utf-8 incompatible string is rendered" do
169
178
  str = "Blah \xDD"
170
179
  str.force_encoding("ASCII-8BIT")
171
- lambda { @pdf.text str }.should raise_error(Prawn::Errors::IncompatibleStringEncoding)
180
+ lambda { @pdf.text str }.should.raise(Prawn::Errors::IncompatibleStringEncoding)
172
181
  end
173
182
  it "should not raise an exception when a shift-jis string is rendered" do
174
183
  datafile = "#{Prawn::BASEDIR}/data/shift_jis_text.txt"
175
184
  sjis_str = File.open(datafile, "r:shift_jis") { |f| f.gets }
176
- lambda { @pdf.text sjis_str }.should_not raise_error(Prawn::Errors::IncompatibleStringEncoding)
185
+ @pdf.font("#{Prawn::BASEDIR}/data/fonts/gkai00mp.ttf")
186
+ lambda { @pdf.text sjis_str }.should.not.raise(Prawn::Errors::IncompatibleStringEncoding)
177
187
  end
178
188
  else
179
189
  # Handle non utf-8 string encodings in a sane way on non-M17N aware VMs
180
190
  it "should raise an exception when a corrupt utf-8 string is rendered" do
181
191
  str = "Blah \xDD"
182
- lambda { @pdf.text str }.should raise_error(Prawn::Errors::IncompatibleStringEncoding)
192
+ lambda { @pdf.text str }.should.raise(Prawn::Errors::IncompatibleStringEncoding)
183
193
  end
184
194
  it "should raise an exception when a shift-jis string is rendered" do
185
195
  sjis_str = File.read("#{Prawn::BASEDIR}/data/shift_jis_text.txt")
186
- lambda { @pdf.text sjis_str }.should raise_error(Prawn::Errors::IncompatibleStringEncoding)
196
+ lambda { @pdf.text sjis_str }.should.raise(Prawn::Errors::IncompatibleStringEncoding)
187
197
  end
188
198
  end
189
199
 
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: prawn
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.1
7
- date: 2008-08-08 00:00:00 -04:00
6
+ version: 0.1.2
7
+ date: 2008-08-11 00:00:00 -04:00
8
8
  summary: A fast and nimble PDF generator for Ruby
9
9
  require_paths:
10
10
  - lib
@@ -47,6 +47,7 @@ files:
47
47
  - examples/multi_page_layout.rb
48
48
  - examples/on_page_start.rb
49
49
  - examples/page_geometry.rb
50
+ - examples/png_types.rb
50
51
  - examples/polygons.rb
51
52
  - examples/ruport_formatter.rb
52
53
  - examples/ruport_helpers.rb
@@ -144,12 +145,21 @@ files:
144
145
  - data/fonts/Times-Roman.afm
145
146
  - data/fonts/ZapfDingbats.afm
146
147
  - data/images
148
+ - data/images/dice.alpha
149
+ - data/images/dice.dat
147
150
  - data/images/dice.png
151
+ - data/images/page_white_text.alpha
152
+ - data/images/page_white_text.dat
153
+ - data/images/page_white_text.png
148
154
  - data/images/pigs.jpg
155
+ - data/images/rails.dat
156
+ - data/images/rails.png
149
157
  - data/images/ruport.png
150
158
  - data/images/ruport_data.dat
151
159
  - data/images/ruport_transparent.png
152
160
  - data/images/stef.jpg
161
+ - data/images/web-links.dat
162
+ - data/images/web-links.png
153
163
  - data/shift_jis_text.txt
154
164
  - Rakefile
155
165
  - README