pdf-writer 1.1.3 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/ChangeLog +1 -1
  2. data/LICENCE +9 -3
  3. data/README +1 -1
  4. data/bin/techbook +1 -1
  5. data/demo/chunkybacon.rb +1 -1
  6. data/demo/code.rb +1 -1
  7. data/demo/colornames.rb +1 -1
  8. data/demo/demo.rb +1 -1
  9. data/demo/gettysburg.rb +1 -1
  10. data/demo/hello.rb +1 -1
  11. data/demo/individual-i.rb +1 -1
  12. data/demo/pac.rb +1 -1
  13. data/demo/qr-language.rb +4 -4
  14. data/demo/qr-library.rb +1 -1
  15. data/lib/pdf/charts.rb +1 -1
  16. data/lib/pdf/charts/stddev.rb +2 -2
  17. data/lib/pdf/math.rb +1 -1
  18. data/lib/pdf/quickref.rb +17 -15
  19. data/lib/pdf/simpletable.rb +24 -23
  20. data/lib/pdf/techbook.rb +1 -1
  21. data/lib/pdf/writer.rb +21 -52
  22. data/lib/pdf/writer/arc4.rb +1 -1
  23. data/lib/pdf/writer/fontmetrics.rb +2 -1
  24. data/lib/pdf/writer/fonts/MustRead.html +19 -1
  25. data/lib/pdf/writer/graphics.rb +3 -3
  26. data/lib/pdf/writer/graphics/imageinfo.rb +365 -365
  27. data/lib/pdf/writer/lang.rb +1 -1
  28. data/lib/pdf/writer/lang/en.rb +1 -1
  29. data/lib/pdf/writer/object.rb +1 -1
  30. data/lib/pdf/writer/object/action.rb +1 -1
  31. data/lib/pdf/writer/object/annotation.rb +1 -1
  32. data/lib/pdf/writer/object/catalog.rb +1 -1
  33. data/lib/pdf/writer/object/contents.rb +1 -5
  34. data/lib/pdf/writer/object/destination.rb +1 -1
  35. data/lib/pdf/writer/object/encryption.rb +1 -1
  36. data/lib/pdf/writer/object/font.rb +1 -1
  37. data/lib/pdf/writer/object/fontdescriptor.rb +1 -1
  38. data/lib/pdf/writer/object/fontencoding.rb +1 -1
  39. data/lib/pdf/writer/object/image.rb +1 -5
  40. data/lib/pdf/writer/object/info.rb +3 -7
  41. data/lib/pdf/writer/object/outline.rb +1 -1
  42. data/lib/pdf/writer/object/outlines.rb +1 -1
  43. data/lib/pdf/writer/object/page.rb +1 -1
  44. data/lib/pdf/writer/object/pages.rb +1 -1
  45. data/lib/pdf/writer/object/procset.rb +1 -1
  46. data/lib/pdf/writer/object/viewerpreferences.rb +1 -1
  47. data/lib/pdf/writer/ohash.rb +1 -1
  48. data/lib/pdf/writer/oreader.rb +1 -1
  49. data/lib/pdf/writer/state.rb +1 -1
  50. data/lib/pdf/writer/strokestyle.rb +1 -1
  51. data/manual.pwd +1 -1
  52. metadata +115 -119
  53. data/demo/pagenumber.rb +0 -79
@@ -6,7 +6,7 @@
6
6
  # Licensed under a MIT-style licence. See LICENCE in the main distribution
7
7
  # for full licensing information.
8
8
  #
9
- # $Id: arc4.rb,v 1.2 2005/05/16 03:59:21 austin Exp $
9
+ # $Id: arc4.rb 184 2007-12-10 03:18:48Z sandal $
10
10
  #++
11
11
  require 'digest/md5'
12
12
 
@@ -6,7 +6,7 @@
6
6
  # Licensed under a MIT-style licence. See LICENCE in the main distribution
7
7
  # for full licensing information.
8
8
  #
9
- # $Id: fontmetrics.rb,v 1.4 2005/06/16 04:28:25 austin Exp $
9
+ # $Id: fontmetrics.rb 184 2007-12-10 03:18:48Z sandal $
10
10
  #++
11
11
 
12
12
  class PDF::Writer::FontMetrics
@@ -82,6 +82,7 @@ class PDF::Writer::FontMetrics
82
82
  line.chomp!
83
83
  line.strip!
84
84
  key, *values = line.split
85
+ next if key.nil?
85
86
  op = "#{key.downcase}=".to_sym
86
87
 
87
88
  # I probably need to deal with MetricsSet. The default value is
@@ -1 +1,19 @@
1
- <html>
1
+ <html>
2
+
3
+ <head>
4
+ <meta http-equiv="content-type" content="text/html;charset=iso-8859-1">
5
+ <meta name="generator" content="Adobe GoLive 4">
6
+ <title>Core 14 AFM Files - ReadMe</title>
7
+ </head>
8
+
9
+ <body bgcolor="white">
10
+ <font color="white">or</font>
11
+ <table border="0" cellpadding="0" cellspacing="2">
12
+ <tr>
13
+ <td width="40"></td>
14
+ <td width="300">This file and the 14 PostScript(R) AFM files it accompanies may be used, copied, and distributed for any purpose and without charge, with or without modification, provided that all copyright notices are retained; that the AFM files are not distributed without this file; that all modifications to this file or any of the AFM files are prominently noted in the modified file(s); and that this paragraph is not modified. Adobe Systems has no responsibility or obligation to support the use of the AFM files. <font color="white">Col</font></td>
15
+ </tr>
16
+ </table>
17
+ </body>
18
+
19
+ </html>
@@ -6,7 +6,7 @@
6
6
  # Licensed under a MIT-style licence. See LICENCE in the main distribution
7
7
  # for full licensing information.
8
8
  #
9
- # $Id: graphics.rb,v 1.12.2.1 2005/08/25 03:38:05 austin Exp $
9
+ # $Id: graphics.rb 184 2007-12-10 03:18:48Z sandal $
10
10
  #++
11
11
  # Points for use in the drawing of polygons.
12
12
  class PDF::Writer::PolygonPoint
@@ -518,7 +518,7 @@ module PDF::Writer::Graphics
518
518
  end
519
519
 
520
520
  # Add an image from a file to the current page at position <tt>(x,
521
- # y)</tt> (the upper left-hand corner of the image). The image will be
521
+ # y)</tt> (the lower left-hand corner of the image). The image will be
522
522
  # scaled to +width+ by +height+ units. The image may be a PNG or JPEG
523
523
  # image.
524
524
  #
@@ -550,7 +550,7 @@ module PDF::Writer::Graphics
550
550
  end
551
551
 
552
552
  # Add an image from a loaded image (JPEG or PNG) resource at position
553
- # <tt>(x, y)</tt> (the upper left-hand corner of the image) and scaled
553
+ # <tt>(x, y)</tt> (the lower left-hand corner of the image) and scaled
554
554
  # to +width+ by +height+ units. If provided, +image_info+ is a
555
555
  # PDF::Writer::Graphics::ImageInfo object.
556
556
  #
@@ -1,365 +1,365 @@
1
- #--
2
- # PDF::Writer for Ruby.
3
- # http://rubyforge.org/projects/ruby-pdf/
4
- # Copyright 2003 - 2005 Austin Ziegler.
5
- #
6
- # Licensed under a MIT-style licence. See LICENCE in the main distribution
7
- # for full licensing information.
8
- #
9
- # This file is also licensed under standard Ruby licensing provisions: the
10
- # Ruby licence and the GNU General Public Licence, version 2 or later.
11
- #
12
- # $Id: imageinfo.rb,v 1.2 2005/05/16 03:59:21 austin Exp $
13
- #++
14
- require 'pdf/writer/oreader'
15
-
16
- # This is based on ImageSize, by Keisuke Minami <keisuke@rccn.com>. It can
17
- # be found at http://www.rubycgi.org/tools/index.en.htm
18
- #
19
- # This has been integrated into PDF::Writer because as yet there has been
20
- # no response to emails asking for my extensions to be integrated and a
21
- # RubyGem package to be made available.
22
- class PDF::Writer::Graphics::ImageInfo
23
- # Image Format Constants
24
- module Formats
25
- OTHER = "OTHER"
26
- GIF = "GIF" # CompuServe GIF87a and GIF89a images.
27
- PNG = "PNG" # Portable Network Graphics, PNG
28
- JPEG = "JPEG" # JPEG
29
- BMP = "BMP" # Windows or OS/2 Bitmaps
30
- PPM = "PPM" # PPM is like PBM, PGM, & XV
31
- PBM = "PBM"
32
- PGM = "PGM"
33
- # XV = "XV" # Not supported
34
- TIFF = "TIFF" # TIFF formats
35
- XBM = "XBM" # X Bitmap
36
- XPM = "XPM" # X Pixmap
37
- PSD = "PSD" # PhotoShop
38
- PCX = "PCX" # PCX Bitmap
39
- SWF = "SWF" # Flash
40
- end
41
- Type = Formats
42
-
43
- class << self
44
- def formats
45
- Formats.constants
46
- end
47
- alias :type_list :formats
48
- end
49
-
50
- JPEG_SOF_BLOCKS = %W(\xc0 \xc1 \xc2 \xc3 \xc5 \xc6 \xc7 \xc9 \xca \xcb \xcd \xce \xcf)
51
- JPEG_APP_BLOCKS = %W(\xe0 \xe1 \xe2 \xe3 \xe4 \xe5 \xe6 \xe7 \xe8 \xe9 \xea \xeb \xec \xed \xee \xef)
52
-
53
- # Receive image & make size. argument is image String or IO
54
- def initialize(data, format = nil)
55
- @data = data.dup rescue data
56
- @info = {}
57
-
58
- if @data.kind_of?(IO)
59
- @top = @data.read(128)
60
- @data.seek(0, 0)
61
- # Define Singleton-method definition to IO (byte, offset)
62
- def @data.read_o(length = 1, offset = nil)
63
- self.seek(offset, 0) if offset
64
- ret = self.read(length)
65
- raise "cannot read!!" unless ret
66
- ret
67
- end
68
- elsif @data.is_a?(String)
69
- @top = @data[0, 128]
70
- # Define Singleton-method definition to String (byte, offset)
71
- @data.extend(PDF::Writer::OffsetReader)
72
- else
73
- raise "argument class error!! #{data.type}"
74
- end
75
-
76
- if format.nil?
77
- @format = discover_format
78
- else
79
- match = false
80
- Formats.constants.each { |t| match = true if format == t }
81
- raise("format is failed. #{format}\n") unless match
82
- @format = format
83
- end
84
-
85
- __send__("measure_#@format".intern) unless @format == Formats::OTHER
86
-
87
- @data = data.dup
88
- end
89
-
90
- attr_reader :format
91
- alias :get_type :format
92
- attr_reader :height
93
- alias :get_height :height
94
- attr_reader :width
95
- alias :get_width width
96
-
97
- attr_reader :bits
98
- attr_reader :channels
99
-
100
- attr_reader :info
101
-
102
- def discover_format
103
- if @top =~ %r{^GIF8[79]a}
104
- Formats::GIF
105
- elsif @top[0, 3] == "\xff\xd8\xff"
106
- Formats::JPEG
107
- elsif @top[0, 8] == "\x89PNG\x0d\x0a\x1a\x0a"
108
- Formats::PNG
109
- elsif @top[0, 3] == "FWS"
110
- Formats::SWF
111
- elsif @top[0, 4] == "8BPS"
112
- Formats::PSD
113
- elsif @top[0, 2] == 'BM'
114
- Formats::BMP
115
- elsif @top[0, 4] == "MM\x00\x2a"
116
- Formats::TIFF
117
- elsif @top[0, 4] == "II\x2a\x00"
118
- Formats::TIFF
119
- elsif @top[0, 12] == "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a"
120
- Formats::JP2
121
- elsif @top =~ %r{^P[1-7]}
122
- Formats::PPM
123
- elsif @top =~ %r{\#define\s+\S+\s+\d+}
124
- Formats::XBM
125
- elsif @top =~ %r{/\* XPM \*/}
126
- Formats::XPM
127
- elsif @top[0] == 10
128
- Formats::PCX
129
- else
130
- Formats::OTHER # might be WBMP
131
- end
132
- end
133
- private :discover_format
134
-
135
- def measure_GIF
136
- @data.read_o(6) # Skip GIF8.a
137
- @width, @height, @bits = @data.read_o(5).unpack('vvC')
138
- if @bits & 0x80 == 0x80
139
- @bits = (@bits & 0x07) + 1
140
- else
141
- @bits = 0
142
- end
143
- @channels = 3
144
- end
145
- private :measure_GIF
146
-
147
- def measure_PNG
148
- @data.read_o(12)
149
- raise "This file is not PNG." unless @data.read_o(4) == "IHDR"
150
- # The file information is in the IHDR section.
151
- # Offset Bytes Meaning
152
- # 0 4 Width
153
- # 5 4 Height
154
- # 9 1 Bit Depth
155
- # 10 1 Compression Method
156
- # 11 1 Filter Method
157
- # 12 1 Interlace Method
158
- ihdr = @data.read_o(13).unpack("NNCCCCC")
159
- @width = ihdr[0]
160
- @height = ihdr[1]
161
- @bits = ihdr[2]
162
- @info[:color_type] = ihdr[3]
163
- @info[:compression_method] = ihdr[4]
164
- @info[:filter_method] = ihdr[5]
165
- @info[:interlace_method] = ihdr[6]
166
-
167
-
168
- end
169
- private :measure_PNG
170
-
171
- def measure_JPEG
172
- c_marker = "\xff" # Section marker.
173
- @data.read_o(2) # Skip the first two bytes of JPEG identifier.
174
- loop do
175
- marker, code, length = @data.read_o(4).unpack('aan')
176
- raise "JPEG marker not found!" if marker != c_marker
177
-
178
- if JPEG_SOF_BLOCKS.include?(code)
179
- @bits, @height, @width, @channels = @data.read_o(6).unpack("CnnC")
180
- break
181
- end
182
-
183
- buffer = @data.read_o(length - 2)
184
-
185
- if JPEG_APP_BLOCKS.include?(code)
186
- @info["APP#{code.to_i - 0xe0}"] = buffer
187
- end
188
- end
189
- end
190
- private :measure_JPEG
191
-
192
- def measure_BMP
193
- # Skip the first 14 bytes of the image.
194
- @data.read_o(14)
195
- # Up to the next 16 bytes will be used.
196
- dim = @data.read_o(16)
197
-
198
- # Get the "size" of the image from the next four bytes.
199
- size = dim.unpack("V")
200
-
201
- if size == 12
202
- @width, @height, @bits = dim.unpack("x4vvx3C")
203
- elsif size > 12 and (size <= 64 or size == 108)
204
- @width, @height, @bits = dim.unpack("x4VVv")
205
- end
206
- end
207
- private :measure_BMP
208
-
209
- def measure_PPM
210
- header = @data.read_o(1024)
211
- header.gsub!(/^\#[^\n\r]*/m, "")
212
- md = %r{^(P[1-6])\s+?(\d+)\s+?(\d+)}mo.match(header)
213
-
214
- @width = md.captures[1]
215
- @height = md.captures[2]
216
-
217
- case md.captures[0]
218
- when "P1", "P4"
219
- @format = "PBM"
220
- when "P2", "P5"
221
- @format = "PGM"
222
- when "P3", "P6"
223
- @format = "PPM"
224
- # when "P7"
225
- # @format = "XV"
226
- # header =~ /IMGINFO:(\d+)x(\d+)/m
227
- # width = $1.to_i; height = $2.to_i
228
- end
229
- end
230
- private :measure_PPM
231
-
232
- alias :measure_PGM :measure_PPM
233
- private :measure_PGM
234
- alias :measure_PBM :measure_PPM
235
- private :measure_PBM
236
-
237
- XBM_DIMENSIONS_RE = %r{^\#define\s*\S*\s*(\d+)\s*\n\#define\s*\S*\s*(\d+)}mi
238
- def measure_XBM
239
- md = XBM_DIMENSIONS_RE.match(@data.read_o(1024))
240
-
241
- @width = md.captures[0].to_i
242
- @height = md.captures[1].to_i
243
- end
244
- private :measure_XBM
245
-
246
- XPM_DIMENSIONS_RE = %r<"\s*(\d+)\s+(\d+)(\s+\d+\s+\d+){1,2}\s*">m
247
- def measure_XPM
248
- while line = @data.read_o(1024)
249
- md = XPM_DIMENSIONS_RE.match(line)
250
- if md
251
- @width = md.captures[0].to_i
252
- @height = md.captures[1].to_i
253
- break
254
- end
255
- end
256
- end
257
- private :measure_XPM
258
-
259
- def measure_PSD
260
- @width, @height = @data.read_o(26).unpack("x14NN")
261
- end
262
- private :measure_PSD
263
-
264
- def measure_PCX
265
- header = @data.read_o(128)
266
- head_part = header.unpack('C4S4')
267
- @width = head_part[6] - head_part[4] + 1
268
- @height = head_part[7] - head_part[5] + 1
269
- end
270
- private :measure_PCX
271
-
272
- def measure_SWF
273
- header = @data.read_o(9)
274
- raise "This file is not SWF." unless header.unpack('a3')[0] == 'FWS'
275
-
276
- bits = Integer("0b#{header.unpack('@8B5')}")
277
- header << @data.read_o(bits * 4 / 8 + 1)
278
-
279
- str = *(header.unpack("@8B#{5 + bits * 4}"))
280
- last = 5
281
- x_min = Integer("0b#{str[last, bits]}")
282
- x_max = Integer("0b#{str[(last + bits), bits]}")
283
- y_min = Integer("0b#{str[(last + (2 * bits)), bits]}")
284
- y_max = Integer("0b#{str[(last + (3 * bits)), bits]}")
285
- @width = (x_max - x_min) / 20
286
- @height = (y_max - y_min) / 20
287
- end
288
- private :measure_SWF
289
-
290
- # The same as SWF, except that the original data is compressed with
291
- # Zlib. Disabled for now.
292
- def measure_SWC
293
- end
294
- private :measure_SWC
295
-
296
- def measure_TIFF
297
- # 'v' little-endian
298
- # 'n' default to big-endian
299
- endian = (@data.read_o(4) =~ /II\x2a\x00/o) ? 'v' : 'n'
300
-
301
- packspec = [
302
- nil, # nothing (shouldn't happen)
303
- 'C', # BYTE (8-bit unsigned integer)
304
- nil, # ASCII
305
- endian, # SHORT (16-bit unsigned integer)
306
- endian.upcase, # LONG (32-bit unsigned integer)
307
- nil, # RATIONAL
308
- 'c', # SBYTE (8-bit signed integer)
309
- nil, # UNDEFINED
310
- endian, # SSHORT (16-bit unsigned integer)
311
- endian.upcase, # SLONG (32-bit unsigned integer)
312
- ]
313
-
314
- # Find the IFD location.
315
- ifd_addr = *(@data.read_o(4).unpack(endian.upcase))
316
- # Get the number of entries in the IFD.
317
- ifd = @data.read_o(2, ifd_addr)
318
- num_dirent = *(ifd.unpack(endian)) # Make it useful
319
- ifd_addr += 2
320
- num_dirent = ifd_addr + (num_dirent * 12) # Calc. maximum offset of IFD
321
-
322
- loop do
323
- break if @width and @height
324
-
325
- ifd = @data.read_o(12, ifd_addr) # Get directory entry.
326
- break if ifd.nil? or ifd_addr > num_dirent
327
- ifd_addr += 12
328
-
329
- tag = *(ifd.unpack(endian)) # ...decode its tag
330
- type = *(ifd[2, 2].unpack(endian)) # ... and data type
331
-
332
- # Check the type for sanity.
333
- next if type > packspec.size or packspec[type].nil?
334
-
335
- case tag
336
- when 0x0100, 0xa002 # width
337
- @width = *(ifd[8, 4].unpack(packspec[type]))
338
- when 0x0101, 0xa003 # height
339
- @height = *(ifd[8, 4].unpack(packspec[type]))
340
- end
341
- end
342
- end
343
- private :measure_TIFF
344
- end
345
-
346
- if __FILE__ == $0
347
- require 'pdf/writer'
348
- require 'pdf/writer/graphics'
349
-
350
- print "Image Formats: #{PDF::Writer::Graphics::ImageInfo.formats.inspect}\n"
351
-
352
- Dir.glob("*").each do |file|
353
- print "#{file} (string)\n"
354
- open(file, "rb") do |fh|
355
- image = PDF::Writer::Graphics::ImageInfo.new(fh.read)
356
- print <<-EOF
357
- Format : #{image.format}
358
- Width : #{image.width.inspect}
359
- Height : #{image.height.inspect}
360
- Bits : #{image.bits.inspect}
361
- Channels: #{image.channels.inspect}
362
- EOF
363
- end
364
- end
365
- end
1
+ #--
2
+ # PDF::Writer for Ruby.
3
+ # http://rubyforge.org/projects/ruby-pdf/
4
+ # Copyright 2003 - 2005 Austin Ziegler.
5
+ #
6
+ # Licensed under a MIT-style licence. See LICENCE in the main distribution
7
+ # for full licensing information.
8
+ #
9
+ # This file is also licensed under standard Ruby licensing provisions: the
10
+ # Ruby licence and the GNU General Public Licence, version 2 or later.
11
+ #
12
+ # $Id: imageinfo.rb 184 2007-12-10 03:18:48Z sandal $
13
+ #++
14
+ require 'pdf/writer/oreader'
15
+
16
+ # This is based on ImageSize, by Keisuke Minami <keisuke@rccn.com>. It can
17
+ # be found at http://www.rubycgi.org/tools/index.en.htm
18
+ #
19
+ # This has been integrated into PDF::Writer because as yet there has been
20
+ # no response to emails asking for my extensions to be integrated and a
21
+ # RubyGem package to be made available.
22
+ class PDF::Writer::Graphics::ImageInfo
23
+ # Image Format Constants
24
+ module Formats
25
+ OTHER = "OTHER"
26
+ GIF = "GIF" # CompuServe GIF87a and GIF89a images.
27
+ PNG = "PNG" # Portable Network Graphics, PNG
28
+ JPEG = "JPEG" # JPEG
29
+ BMP = "BMP" # Windows or OS/2 Bitmaps
30
+ PPM = "PPM" # PPM is like PBM, PGM, & XV
31
+ PBM = "PBM"
32
+ PGM = "PGM"
33
+ # XV = "XV" # Not supported
34
+ TIFF = "TIFF" # TIFF formats
35
+ XBM = "XBM" # X Bitmap
36
+ XPM = "XPM" # X Pixmap
37
+ PSD = "PSD" # PhotoShop
38
+ PCX = "PCX" # PCX Bitmap
39
+ SWF = "SWF" # Flash
40
+ end
41
+ Type = Formats
42
+
43
+ class << self
44
+ def formats
45
+ Formats.constants
46
+ end
47
+ alias :type_list :formats
48
+ end
49
+
50
+ JPEG_SOF_BLOCKS = %W(\xc0 \xc1 \xc2 \xc3 \xc5 \xc6 \xc7 \xc9 \xca \xcb \xcd \xce \xcf)
51
+ JPEG_APP_BLOCKS = %W(\xe0 \xe1 \xe2 \xe3 \xe4 \xe5 \xe6 \xe7 \xe8 \xe9 \xea \xeb \xec \xed \xee \xef)
52
+
53
+ # Receive image & make size. argument is image String or IO
54
+ def initialize(data, format = nil)
55
+ @data = data.dup rescue data
56
+ @info = {}
57
+
58
+ if @data.kind_of?(IO)
59
+ @top = @data.read(128)
60
+ @data.seek(0, 0)
61
+ # Define Singleton-method definition to IO (byte, offset)
62
+ def @data.read_o(length = 1, offset = nil)
63
+ self.seek(offset, 0) if offset
64
+ ret = self.read(length)
65
+ raise "cannot read!!" unless ret
66
+ ret
67
+ end
68
+ elsif @data.is_a?(String)
69
+ @top = @data[0, 128]
70
+ # Define Singleton-method definition to String (byte, offset)
71
+ @data.extend(PDF::Writer::OffsetReader)
72
+ else
73
+ raise "argument class error!! #{data.type}"
74
+ end
75
+
76
+ if format.nil?
77
+ @format = discover_format
78
+ else
79
+ match = false
80
+ Formats.constants.each { |t| match = true if format == t }
81
+ raise("format is failed. #{format}\n") unless match
82
+ @format = format
83
+ end
84
+
85
+ __send__("measure_#@format".intern) unless @format == Formats::OTHER
86
+
87
+ @data = data.dup
88
+ end
89
+
90
+ attr_reader :format
91
+ alias :get_type :format
92
+ attr_reader :height
93
+ alias :get_height :height
94
+ attr_reader :width
95
+ alias :get_width width
96
+
97
+ attr_reader :bits
98
+ attr_reader :channels
99
+
100
+ attr_reader :info
101
+
102
+ def discover_format
103
+ if @top =~ %r{^GIF8[79]a}
104
+ Formats::GIF
105
+ elsif @top[0, 3] == "\xff\xd8\xff"
106
+ Formats::JPEG
107
+ elsif @top[0, 8] == "\x89PNG\x0d\x0a\x1a\x0a"
108
+ Formats::PNG
109
+ elsif @top[0, 3] == "FWS"
110
+ Formats::SWF
111
+ elsif @top[0, 4] == "8BPS"
112
+ Formats::PSD
113
+ elsif @top[0, 2] == 'BM'
114
+ Formats::BMP
115
+ elsif @top[0, 4] == "MM\x00\x2a"
116
+ Formats::TIFF
117
+ elsif @top[0, 4] == "II\x2a\x00"
118
+ Formats::TIFF
119
+ elsif @top[0, 12] == "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a"
120
+ Formats::JP2
121
+ elsif @top =~ %r{^P[1-7]}
122
+ Formats::PPM
123
+ elsif @top =~ %r{\#define\s+\S+\s+\d+}
124
+ Formats::XBM
125
+ elsif @top =~ %r{/\* XPM \*/}
126
+ Formats::XPM
127
+ elsif @top[0] == 10
128
+ Formats::PCX
129
+ else
130
+ Formats::OTHER # might be WBMP
131
+ end
132
+ end
133
+ private :discover_format
134
+
135
+ def measure_GIF
136
+ @data.read_o(6) # Skip GIF8.a
137
+ @width, @height, @bits = @data.read_o(5).unpack('vvC')
138
+ if @bits & 0x80 == 0x80
139
+ @bits = (@bits & 0x07) + 1
140
+ else
141
+ @bits = 0
142
+ end
143
+ @channels = 3
144
+ end
145
+ private :measure_GIF
146
+
147
+ def measure_PNG
148
+ @data.read_o(12)
149
+ raise "This file is not PNG." unless @data.read_o(4) == "IHDR"
150
+ # The file information is in the IHDR section.
151
+ # Offset Bytes Meaning
152
+ # 0 4 Width
153
+ # 5 4 Height
154
+ # 9 1 Bit Depth
155
+ # 10 1 Compression Method
156
+ # 11 1 Filter Method
157
+ # 12 1 Interlace Method
158
+ ihdr = @data.read_o(13).unpack("NNCCCCC")
159
+ @width = ihdr[0]
160
+ @height = ihdr[1]
161
+ @bits = ihdr[2]
162
+ @info[:color_type] = ihdr[3]
163
+ @info[:compression_method] = ihdr[4]
164
+ @info[:filter_method] = ihdr[5]
165
+ @info[:interlace_method] = ihdr[6]
166
+
167
+
168
+ end
169
+ private :measure_PNG
170
+
171
+ def measure_JPEG
172
+ c_marker = "\xff" # Section marker.
173
+ @data.read_o(2) # Skip the first two bytes of JPEG identifier.
174
+ loop do
175
+ marker, code, length = @data.read_o(4).unpack('aan')
176
+ raise "JPEG marker not found!" if marker != c_marker
177
+
178
+ if JPEG_SOF_BLOCKS.include?(code)
179
+ @bits, @height, @width, @channels = @data.read_o(6).unpack("CnnC")
180
+ break
181
+ end
182
+
183
+ buffer = @data.read_o(length - 2)
184
+
185
+ if JPEG_APP_BLOCKS.include?(code)
186
+ @info["APP#{code.to_i - 0xe0}"] = buffer
187
+ end
188
+ end
189
+ end
190
+ private :measure_JPEG
191
+
192
+ def measure_BMP
193
+ # Skip the first 14 bytes of the image.
194
+ @data.read_o(14)
195
+ # Up to the next 16 bytes will be used.
196
+ dim = @data.read_o(16)
197
+
198
+ # Get the "size" of the image from the next four bytes.
199
+ size = dim.unpack("V").first # <- UNPACK RETURNS ARRAY, SO GET FIRST ELEMENT
200
+
201
+ if size == 12
202
+ @width, @height, @bits = dim.unpack("x4vvx3C")
203
+ elsif size > 12 and (size <= 64 or size == 108)
204
+ @width, @height, @bits = dim.unpack("x4VVv")
205
+ end
206
+ end
207
+ private :measure_BMP
208
+
209
+ def measure_PPM
210
+ header = @data.read_o(1024)
211
+ header.gsub!(/^\#[^\n\r]*/m, "")
212
+ md = %r{^(P[1-6])\s+?(\d+)\s+?(\d+)}mo.match(header)
213
+
214
+ @width = md.captures[1]
215
+ @height = md.captures[2]
216
+
217
+ case md.captures[0]
218
+ when "P1", "P4"
219
+ @format = "PBM"
220
+ when "P2", "P5"
221
+ @format = "PGM"
222
+ when "P3", "P6"
223
+ @format = "PPM"
224
+ # when "P7"
225
+ # @format = "XV"
226
+ # header =~ /IMGINFO:(\d+)x(\d+)/m
227
+ # width = $1.to_i; height = $2.to_i
228
+ end
229
+ end
230
+ private :measure_PPM
231
+
232
+ alias :measure_PGM :measure_PPM
233
+ private :measure_PGM
234
+ alias :measure_PBM :measure_PPM
235
+ private :measure_PBM
236
+
237
+ XBM_DIMENSIONS_RE = %r{^\#define\s*\S*\s*(\d+)\s*\n\#define\s*\S*\s*(\d+)}mi
238
+ def measure_XBM
239
+ md = XBM_DIMENSIONS_RE.match(@data.read_o(1024))
240
+
241
+ @width = md.captures[0].to_i
242
+ @height = md.captures[1].to_i
243
+ end
244
+ private :measure_XBM
245
+
246
+ XPM_DIMENSIONS_RE = %r<"\s*(\d+)\s+(\d+)(\s+\d+\s+\d+){1,2}\s*">m
247
+ def measure_XPM
248
+ while line = @data.read_o(1024)
249
+ md = XPM_DIMENSIONS_RE.match(line)
250
+ if md
251
+ @width = md.captures[0].to_i
252
+ @height = md.captures[1].to_i
253
+ break
254
+ end
255
+ end
256
+ end
257
+ private :measure_XPM
258
+
259
+ def measure_PSD
260
+ @width, @height = @data.read_o(26).unpack("x14NN")
261
+ end
262
+ private :measure_PSD
263
+
264
+ def measure_PCX
265
+ header = @data.read_o(128)
266
+ head_part = header.unpack('C4S4')
267
+ @width = head_part[6] - head_part[4] + 1
268
+ @height = head_part[7] - head_part[5] + 1
269
+ end
270
+ private :measure_PCX
271
+
272
+ def measure_SWF
273
+ header = @data.read_o(9)
274
+ raise "This file is not SWF." unless header.unpack('a3')[0] == 'FWS'
275
+
276
+ bits = Integer("0b#{header.unpack('@8B5')}")
277
+ header << @data.read_o(bits * 4 / 8 + 1)
278
+
279
+ str = *(header.unpack("@8B#{5 + bits * 4}"))
280
+ last = 5
281
+ x_min = Integer("0b#{str[last, bits]}")
282
+ x_max = Integer("0b#{str[(last + bits), bits]}")
283
+ y_min = Integer("0b#{str[(last + (2 * bits)), bits]}")
284
+ y_max = Integer("0b#{str[(last + (3 * bits)), bits]}")
285
+ @width = (x_max - x_min) / 20
286
+ @height = (y_max - y_min) / 20
287
+ end
288
+ private :measure_SWF
289
+
290
+ # The same as SWF, except that the original data is compressed with
291
+ # Zlib. Disabled for now.
292
+ def measure_SWC
293
+ end
294
+ private :measure_SWC
295
+
296
+ def measure_TIFF
297
+ # 'v' little-endian
298
+ # 'n' default to big-endian
299
+ endian = (@data.read_o(4) =~ /II\x2a\x00/o) ? 'v' : 'n'
300
+
301
+ packspec = [
302
+ nil, # nothing (shouldn't happen)
303
+ 'C', # BYTE (8-bit unsigned integer)
304
+ nil, # ASCII
305
+ endian, # SHORT (16-bit unsigned integer)
306
+ endian.upcase, # LONG (32-bit unsigned integer)
307
+ nil, # RATIONAL
308
+ 'c', # SBYTE (8-bit signed integer)
309
+ nil, # UNDEFINED
310
+ endian, # SSHORT (16-bit unsigned integer)
311
+ endian.upcase, # SLONG (32-bit unsigned integer)
312
+ ]
313
+
314
+ # Find the IFD location.
315
+ ifd_addr = *(@data.read_o(4).unpack(endian.upcase))
316
+ # Get the number of entries in the IFD.
317
+ ifd = @data.read_o(2, ifd_addr)
318
+ num_dirent = *(ifd.unpack(endian)) # Make it useful
319
+ ifd_addr += 2
320
+ num_dirent = ifd_addr + (num_dirent * 12) # Calc. maximum offset of IFD
321
+
322
+ loop do
323
+ break if @width and @height
324
+
325
+ ifd = @data.read_o(12, ifd_addr) # Get directory entry.
326
+ break if ifd.nil? or ifd_addr > num_dirent
327
+ ifd_addr += 12
328
+
329
+ tag = *(ifd.unpack(endian)) # ...decode its tag
330
+ type = *(ifd[2, 2].unpack(endian)) # ... and data type
331
+
332
+ # Check the type for sanity.
333
+ next if type > packspec.size or packspec[type].nil?
334
+
335
+ case tag
336
+ when 0x0100, 0xa002 # width
337
+ @width = *(ifd[8, 4].unpack(packspec[type]))
338
+ when 0x0101, 0xa003 # height
339
+ @height = *(ifd[8, 4].unpack(packspec[type]))
340
+ end
341
+ end
342
+ end
343
+ private :measure_TIFF
344
+ end
345
+
346
+ if __FILE__ == $0
347
+ require 'pdf/writer'
348
+ require 'pdf/writer/graphics'
349
+
350
+ print "Image Formats: #{PDF::Writer::Graphics::ImageInfo.formats.inspect}\n"
351
+
352
+ Dir.glob("*").each do |file|
353
+ print "#{file} (string)\n"
354
+ open(file, "rb") do |fh|
355
+ image = PDF::Writer::Graphics::ImageInfo.new(fh.read)
356
+ print <<-EOF
357
+ Format : #{image.format}
358
+ Width : #{image.width.inspect}
359
+ Height : #{image.height.inspect}
360
+ Bits : #{image.bits.inspect}
361
+ Channels: #{image.channels.inspect}
362
+ EOF
363
+ end
364
+ end
365
+ end