origami 1.2.7 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -0
  3. data/README.md +112 -0
  4. data/bin/config/pdfcop.conf.yml +232 -233
  5. data/bin/gui/about.rb +27 -37
  6. data/bin/gui/config.rb +108 -117
  7. data/bin/gui/file.rb +416 -365
  8. data/bin/gui/gtkhex.rb +1138 -1153
  9. data/bin/gui/hexview.rb +55 -57
  10. data/bin/gui/imgview.rb +48 -51
  11. data/bin/gui/menu.rb +388 -386
  12. data/bin/gui/properties.rb +114 -130
  13. data/bin/gui/signing.rb +571 -617
  14. data/bin/gui/textview.rb +77 -95
  15. data/bin/gui/treeview.rb +382 -387
  16. data/bin/gui/walker.rb +227 -232
  17. data/bin/gui/xrefs.rb +56 -60
  18. data/bin/pdf2pdfa +53 -57
  19. data/bin/pdf2ruby +212 -228
  20. data/bin/pdfcop +338 -348
  21. data/bin/pdfdecompress +58 -65
  22. data/bin/pdfdecrypt +56 -60
  23. data/bin/pdfencrypt +75 -80
  24. data/bin/pdfexplode +185 -182
  25. data/bin/pdfextract +201 -218
  26. data/bin/pdfmetadata +83 -82
  27. data/bin/pdfsh +4 -5
  28. data/bin/pdfwalker +1 -2
  29. data/bin/shell/.irbrc +45 -82
  30. data/bin/shell/console.rb +105 -130
  31. data/bin/shell/hexdump.rb +40 -64
  32. data/examples/README.md +34 -0
  33. data/examples/attachments/attachment.rb +38 -0
  34. data/examples/attachments/nested_document.rb +51 -0
  35. data/examples/encryption/encryption.rb +28 -0
  36. data/{samples/actions/triggerevents/trigger.rb → examples/events/events.rb} +13 -16
  37. data/examples/flash/flash.rb +37 -0
  38. data/{samples → examples}/flash/helloworld.swf +0 -0
  39. data/examples/forms/javascript.rb +54 -0
  40. data/examples/forms/xfa.rb +115 -0
  41. data/examples/javascript/hello_world.rb +22 -0
  42. data/examples/javascript/js_emulation.rb +54 -0
  43. data/examples/loop/goto.rb +32 -0
  44. data/examples/loop/named.rb +33 -0
  45. data/examples/signature/signature.rb +65 -0
  46. data/examples/uri/javascript.rb +56 -0
  47. data/examples/uri/open-uri.rb +21 -0
  48. data/examples/uri/submitform.rb +47 -0
  49. data/lib/origami.rb +29 -42
  50. data/lib/origami/3d.rb +350 -225
  51. data/lib/origami/acroform.rb +262 -288
  52. data/lib/origami/actions.rb +268 -288
  53. data/lib/origami/annotations.rb +697 -722
  54. data/lib/origami/array.rb +258 -184
  55. data/lib/origami/boolean.rb +74 -84
  56. data/lib/origami/catalog.rb +397 -434
  57. data/lib/origami/collections.rb +144 -0
  58. data/lib/origami/destinations.rb +233 -194
  59. data/lib/origami/dictionary.rb +253 -232
  60. data/lib/origami/encryption.rb +1274 -1243
  61. data/lib/origami/export.rb +232 -268
  62. data/lib/origami/extensions/fdf.rb +307 -220
  63. data/lib/origami/extensions/ppklite.rb +368 -435
  64. data/lib/origami/filespec.rb +197 -0
  65. data/lib/origami/filters.rb +301 -295
  66. data/lib/origami/filters/ascii.rb +177 -180
  67. data/lib/origami/filters/ccitt.rb +528 -535
  68. data/lib/origami/filters/crypt.rb +26 -35
  69. data/lib/origami/filters/dct.rb +46 -52
  70. data/lib/origami/filters/flate.rb +95 -94
  71. data/lib/origami/filters/jbig2.rb +49 -55
  72. data/lib/origami/filters/jpx.rb +38 -44
  73. data/lib/origami/filters/lzw.rb +189 -183
  74. data/lib/origami/filters/predictors.rb +221 -235
  75. data/lib/origami/filters/runlength.rb +103 -104
  76. data/lib/origami/font.rb +173 -186
  77. data/lib/origami/functions.rb +67 -81
  78. data/lib/origami/graphics.rb +25 -21
  79. data/lib/origami/graphics/colors.rb +178 -187
  80. data/lib/origami/graphics/instruction.rb +79 -85
  81. data/lib/origami/graphics/path.rb +142 -148
  82. data/lib/origami/graphics/patterns.rb +160 -167
  83. data/lib/origami/graphics/render.rb +43 -50
  84. data/lib/origami/graphics/state.rb +138 -153
  85. data/lib/origami/graphics/text.rb +188 -205
  86. data/lib/origami/graphics/xobject.rb +819 -815
  87. data/lib/origami/header.rb +63 -78
  88. data/lib/origami/javascript.rb +596 -597
  89. data/lib/origami/linearization.rb +285 -290
  90. data/lib/origami/metadata.rb +139 -148
  91. data/lib/origami/name.rb +112 -148
  92. data/lib/origami/null.rb +53 -62
  93. data/lib/origami/numeric.rb +162 -175
  94. data/lib/origami/obfuscation.rb +186 -174
  95. data/lib/origami/object.rb +593 -573
  96. data/lib/origami/outline.rb +42 -47
  97. data/lib/origami/outputintents.rb +73 -82
  98. data/lib/origami/page.rb +703 -592
  99. data/lib/origami/parser.rb +238 -290
  100. data/lib/origami/parsers/fdf.rb +41 -33
  101. data/lib/origami/parsers/pdf.rb +75 -95
  102. data/lib/origami/parsers/pdf/lazy.rb +137 -0
  103. data/lib/origami/parsers/pdf/linear.rb +64 -66
  104. data/lib/origami/parsers/ppklite.rb +34 -70
  105. data/lib/origami/pdf.rb +1030 -1005
  106. data/lib/origami/reference.rb +102 -102
  107. data/lib/origami/signature.rb +591 -609
  108. data/lib/origami/stream.rb +668 -551
  109. data/lib/origami/string.rb +397 -373
  110. data/lib/origami/template/patterns.rb +56 -0
  111. data/lib/origami/template/widgets.rb +151 -0
  112. data/lib/origami/trailer.rb +144 -158
  113. data/lib/origami/tree.rb +62 -0
  114. data/lib/origami/version.rb +23 -0
  115. data/lib/origami/webcapture.rb +88 -79
  116. data/lib/origami/xfa.rb +2863 -2882
  117. data/lib/origami/xreftable.rb +472 -384
  118. data/test/dataset/calc.pdf +85 -0
  119. data/test/dataset/crypto.pdf +82 -0
  120. data/test/dataset/empty.pdf +49 -0
  121. data/test/test_actions.rb +27 -0
  122. data/test/test_annotations.rb +90 -0
  123. data/test/test_pages.rb +31 -0
  124. data/test/test_pdf.rb +16 -0
  125. data/test/test_pdf_attachment.rb +34 -0
  126. data/test/test_pdf_create.rb +24 -0
  127. data/test/test_pdf_encrypt.rb +95 -0
  128. data/test/test_pdf_parse.rb +96 -0
  129. data/test/test_pdf_sign.rb +58 -0
  130. data/test/test_streams.rb +182 -0
  131. data/test/test_xrefs.rb +67 -0
  132. metadata +88 -58
  133. data/README +0 -67
  134. data/bin/pdf2graph +0 -121
  135. data/bin/pdfcocoon +0 -104
  136. data/lib/origami/file.rb +0 -233
  137. data/samples/README.txt +0 -45
  138. data/samples/actions/launch/calc.rb +0 -87
  139. data/samples/actions/launch/winparams.rb +0 -22
  140. data/samples/actions/loop/loopgoto.rb +0 -24
  141. data/samples/actions/loop/loopnamed.rb +0 -21
  142. data/samples/actions/named/named.rb +0 -31
  143. data/samples/actions/samba/smbrelay.rb +0 -26
  144. data/samples/actions/webbug/submitform.js +0 -26
  145. data/samples/actions/webbug/webbug-browser.rb +0 -68
  146. data/samples/actions/webbug/webbug-js.rb +0 -67
  147. data/samples/actions/webbug/webbug-reader.rb +0 -90
  148. data/samples/attachments/attach.rb +0 -40
  149. data/samples/attachments/attached.txt +0 -1
  150. data/samples/crypto/crypto.rb +0 -28
  151. data/samples/digsig/signed.rb +0 -46
  152. data/samples/exploits/cve-2008-2992-utilprintf.rb +0 -87
  153. data/samples/exploits/cve-2009-0927-geticon.rb +0 -65
  154. data/samples/exploits/exploit_customdictopen.rb +0 -55
  155. data/samples/exploits/getannots.rb +0 -69
  156. data/samples/flash/flash.rb +0 -31
  157. data/samples/javascript/attached.txt +0 -1
  158. data/samples/javascript/js.rb +0 -52
  159. data/templates/patterns.rb +0 -66
  160. data/templates/widgets.rb +0 -173
  161. data/templates/xdp.rb +0 -92
  162. data/test/ts_pdf.rb +0 -50
@@ -0,0 +1,197 @@
1
+ =begin
2
+
3
+ This file is part of Origami, PDF manipulation framework for Ruby
4
+ Copyright (C) 2016 Guillaume Delugré.
5
+
6
+ Origami is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ Origami is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ =end
20
+
21
+ module Origami
22
+
23
+ class PDF
24
+
25
+ #
26
+ # Attachs an embedded file to the PDF.
27
+ # _path_:: The path to the file to attach.
28
+ # _register_:: Whether the file shall be registered in the name directory.
29
+ # _name_:: The embedded file name of the attachment.
30
+ # _filter_:: The stream filter used to store the file contents.
31
+ #
32
+ def attach_file(path, register: true, name: nil, filter: :FlateDecode)
33
+
34
+ if path.is_a? FileSpec
35
+ filespec = path
36
+ name ||= ''
37
+ else
38
+ if path.respond_to?(:read)
39
+ data = path.read.force_encoding('binary')
40
+ name ||= ''
41
+ else
42
+ data = File.binread(File.expand_path(path))
43
+ name ||= File.basename(path)
44
+ end
45
+
46
+ fstream = EmbeddedFileStream.new
47
+ fstream.data = data
48
+
49
+ fstream.Filter = filter
50
+ filespec = FileSpec.new(:F => fstream)
51
+ end
52
+
53
+ fspec = FileSpec.new.setType(:Filespec).setF(name.dup).setEF(filespec)
54
+
55
+ self.register(
56
+ Names::EMBEDDED_FILES,
57
+ name.dup,
58
+ fspec
59
+ ) if register
60
+
61
+ fspec
62
+ end
63
+
64
+ #
65
+ # Lookup embedded file in the embedded files name directory.
66
+ #
67
+ def get_embedded_file_by_name(name)
68
+ resolve_name Names::EMBEDDED_FILES, name
69
+ end
70
+
71
+ #
72
+ # Calls block for each named embedded file.
73
+ #
74
+ def each_named_embedded_file(&b)
75
+ each_name(Names::EMBEDDED_FILES, &b)
76
+ end
77
+ alias each_attachment each_named_embedded_file
78
+ end
79
+
80
+ #
81
+ # Class used to convert system-dependent pathes into PDF pathes.
82
+ # PDF path specification offers a single form for representing file pathes over operating systems.
83
+ #
84
+ class Filename
85
+
86
+ class << self
87
+ #
88
+ # Converts UNIX file path into PDF file path.
89
+ #
90
+ def Unix(file)
91
+ LiteralString.new(file)
92
+ end
93
+
94
+ #
95
+ # Converts MacOS file path into PDF file path.
96
+ #
97
+ def Mac(file)
98
+ LiteralString.new("/" + file.gsub(":", "/"))
99
+ end
100
+
101
+ #
102
+ # Converts Windows file path into PDF file path.
103
+ #
104
+ def DOS(file)
105
+ path = ""
106
+ # Absolute vs relative path
107
+ if file.include? ":"
108
+ path << "/"
109
+ file.sub!(":","")
110
+ end
111
+
112
+ file.gsub!("\\", "/")
113
+ LiteralString.new(path + file)
114
+ end
115
+ end
116
+ end
117
+
118
+ #
119
+ # Class representing a file specification.
120
+ # File specifications can be used to reference external files, as well as embedded files and URIs.
121
+ #
122
+ class FileSpec < Dictionary
123
+ include StandardObject
124
+
125
+ field :Type, :Type => Name, :Default => :FileSpec
126
+ field :FS, :Type => Name, :Default => :URL
127
+ field :F, :Type => [ String, Stream ]
128
+ field :UF, :Type => String
129
+ field :DOS, :Type => String
130
+ field :Mac, :Type => String
131
+ field :Unix, :Type => String
132
+ field :ID, :Type => Array
133
+ field :V, :Type => Boolean, :Default => false, :Version => "1.2"
134
+ field :EF, :Type => Dictionary, :Version => "1.3"
135
+ field :RF, :Type => Dictionary, :Version => "1.3"
136
+ field :Desc, :Type => String, :Version => "1.6"
137
+ field :CI, :Type => Dictionary, :Version => "1.7"
138
+ field :Thumb, :Type => Stream, :Version => "1.7", :ExtensionLevel => 3
139
+ end
140
+
141
+ #
142
+ # Class representing a Uniform Resource Locator (URL)
143
+ #
144
+ class URL < FileSpec
145
+ field :Type, :Type => Name, :Default => :URL, :Required => true
146
+
147
+ def initialize(url)
148
+ super(:F => url)
149
+ end
150
+ end
151
+
152
+ #
153
+ # A class representing a file outside the current PDF file.
154
+ #
155
+ class ExternalFile < FileSpec
156
+ field :Type, :Type => Name, :Default => :FileSpec #, :Required => true
157
+
158
+ #
159
+ # Creates a new external file specification.
160
+ # _dos_:: The Windows path to this file.
161
+ # _mac_:: The MacOS path to this file.
162
+ # _unix_:: The UNIX path to this file.
163
+ #
164
+ def initialize(dos, mac: "", unix: "")
165
+ if not mac.empty? or not unix.empty?
166
+ super(:DOS => Filename.DOS(dos), :Mac => Filename.Mac(mac), :Unix => Filename.Unix(unix))
167
+ else
168
+ super(:F => dos)
169
+ end
170
+ end
171
+ end
172
+
173
+ #
174
+ # Class representing parameters for a EmbeddedFileStream.
175
+ #
176
+ class EmbeddedFileParameters < Dictionary
177
+ include StandardObject
178
+
179
+ field :Size, :Type => Integer
180
+ field :CreationDate, :Type => String
181
+ field :ModDate, :Type => String
182
+ field :Mac, :Type => Dictionary
183
+ field :Checksum, :Type => String
184
+ end
185
+
186
+ #
187
+ # Class representing the data of an embedded file.
188
+ #
189
+ class EmbeddedFileStream < Stream
190
+ include StandardObject
191
+
192
+ field :Type, :Type => Name, :Default => :EmbeddedFile
193
+ field :Subtype, :Type => Name
194
+ field :Params, :Type => EmbeddedFileParameters
195
+ end
196
+
197
+ end
@@ -1,333 +1,340 @@
1
1
  =begin
2
2
 
3
- = File
4
- filters.rb
5
-
6
- = Info
7
- This file is part of Origami, PDF manipulation framework for Ruby
8
- Copyright (C) 2010 Guillaume Delugré <guillaume AT security-labs DOT org>
9
- All right reserved.
10
-
11
- Origami is free software: you can redistribute it and/or modify
12
- it under the terms of the GNU Lesser General Public License as published by
13
- the Free Software Foundation, either version 3 of the License, or
14
- (at your option) any later version.
15
-
16
- Origami is distributed in the hope that it will be useful,
17
- but WITHOUT ANY WARRANTY; without even the implied warranty of
18
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
- GNU Lesser General Public License for more details.
20
-
21
- You should have received a copy of the GNU Lesser General Public License
22
- along with Origami. If not, see <http://www.gnu.org/licenses/>.
3
+ This file is part of Origami, PDF manipulation framework for Ruby
4
+ Copyright (C) 2016 Guillaume Delugré.
23
5
 
24
- =end
25
-
26
- module Origami
27
-
28
- #
29
- # Filters are algorithms used to encode data into a PDF Stream.
30
- #
31
- module Filter
32
-
33
- class InvalidFilterDataError < Exception # :nodoc:
34
- attr_reader :decoded_data
35
-
36
- def initialize(message, decoded_data = nil)
37
- super(message)
38
-
39
- @decoded_data = decoded_data
40
- end
41
- end
6
+ Origami is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
42
10
 
43
- module Utils
44
-
45
- class BitWriterError < Exception #:nodoc:
46
- end
47
-
48
- #
49
- # Class used to forge a String from a stream of bits.
50
- # Internally used by some filters.
51
- #
52
- class BitWriter
53
- def initialize
54
- @data = ''
55
- @last_byte = nil
56
- @ptr_bit = 0
57
- end
58
-
59
- #
60
- # Writes _data_ represented as Fixnum to a _length_ number of bits.
61
- #
62
- def write(data, length)
63
- return BitWriterError, "Invalid data length" unless length > 0 and (1 << length) > data
64
-
65
- # optimization for aligned byte writing
66
- if length == 8 and @last_byte.nil? and @ptr_bit == 0
67
- @data << data.chr
68
- return self
69
- end
70
-
71
- while length > 0
72
- if length >= 8 - @ptr_bit
73
- length -= 8 - @ptr_bit
74
- @last_byte ||= 0
75
- @last_byte |= (data >> length) & ((1 << (8 - @ptr_bit)) - 1)
76
-
77
- data &= (1 << length) - 1
78
- @data << @last_byte.chr
79
- @last_byte = nil
80
- @ptr_bit = 0
81
- else
82
- @last_byte ||= 0
83
- @last_byte |= (data & ((1 << length) - 1)) << (8 - @ptr_bit - length)
84
- @ptr_bit += length
85
-
86
- if @ptr_bit == 8
87
- @data << @last_byte.chr
88
- @last_byte = nil
89
- @ptr_bit = 0
90
- end
91
-
92
- length = 0
93
- end
94
- end
95
-
96
- self
97
- end
11
+ Origami is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
98
15
 
99
- #
100
- # Returns the data size in bits.
101
- #
102
- def size
103
- (@data.size << 3) + @ptr_bit
104
- end
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
105
18
 
106
- #
107
- # Finalizes the stream.
108
- #
109
- def final
110
- @data << @last_byte.chr if @last_byte
111
- @last_byte = nil
112
- @p = 0
19
+ =end
113
20
 
114
- self
115
- end
21
+ module Origami
116
22
 
117
- #
118
- # Outputs the stream as a String.
119
- #
120
- def to_s
121
- @data.dup
122
- end
123
- end
124
-
125
- class BitReaderError < Exception #:nodoc:
126
- end
127
-
128
- #
129
- # Class used to read a String as a stream of bits.
130
- # Internally used by some filters.
131
- #
132
- class BitReader
133
- BRUIJIN_TABLE = ::Array.new(32)
134
- BRUIJIN_TABLE.size.times { |i|
135
- BRUIJIN_TABLE[((0x77cb531 * (1 << i)) >> 27) & 31] = i
136
- }
137
-
138
- def initialize(data)
139
- @data = data
140
- reset
141
- end
23
+ #
24
+ # Filters are algorithms used to encode data into a PDF Stream.
25
+ #
26
+ module Filter
142
27
 
143
28
  #
144
- # Resets the read pointer.
29
+ # Base class for filter Exceptions.
145
30
  #
146
- def reset
147
- @ptr_byte, @ptr_bit = 0, 0
148
- self
149
- end
31
+ class Error < Origami::Error
32
+ attr_accessor :input_data, :decoded_data
150
33
 
151
- #
152
- # Returns true if end of data has been reached.
153
- #
154
- def eod?
155
- @ptr_byte >= @data.size
156
- end
34
+ def initialize(message, input_data: nil, decoded_data: nil)
35
+ super(message)
157
36
 
158
- #
159
- # Returns the read pointer position in bits.
160
- #
161
- def pos
162
- (@ptr_byte << 3) + @ptr_bit
37
+ @input_data, @decoded_data = input_data, decoded_data
38
+ end
163
39
  end
164
40
 
165
41
  #
166
- # Returns the data size in bits.
42
+ # Exception class for unsupported filters or unsupported filter parameters.
167
43
  #
168
- def size
169
- @data.size << 3
170
- end
44
+ class NotImplementedError < Error; end
171
45
 
172
46
  #
173
- # Sets the read pointer position in bits.
47
+ # Exception class for errors occuring during decode operations.
174
48
  #
175
- def pos=(bits)
176
- raise BitReaderError, "Pointer position out of data" if bits > self.size
177
-
178
- pbyte = bits >> 3
179
- pbit = bits - (pbyte << 3)
180
- @ptr_byte, @ptr_bit = pbyte, pbit
181
-
182
- bits
183
- end
49
+ class DecodeError < Error; end
184
50
 
185
- #
186
- # Reads _length_ bits as a Fixnum and advances read pointer.
187
- #
188
- def read(length)
189
- n = self.peek(length)
190
- self.pos += length
51
+ module Utils
191
52
 
192
- n
193
- end
194
-
195
- #
196
- # Reads _length_ bits as a Fixnum. Does not advance read pointer.
197
- #
198
- def peek(length)
199
- return BitReaderError, "Invalid read length" unless length > 0
200
- return BitReaderError, "Insufficient data" if self.pos + length > self.size
201
-
202
- n = 0
203
- ptr_byte, ptr_bit = @ptr_byte, @ptr_bit
204
-
205
- while length > 0
206
- byte = @data[ptr_byte].ord
207
-
208
- if length > 8 - ptr_bit
209
- length -= 8 - ptr_bit
210
- n |= ( byte & ((1 << (8 - ptr_bit)) - 1) ) << length
211
-
212
- ptr_byte += 1
213
- ptr_bit = 0
214
- else
215
- n |= (byte >> (8 - ptr_bit - length)) & ((1 << length) - 1)
216
- length = 0
53
+ class BitWriterError < Error #:nodoc:
217
54
  end
218
- end
219
55
 
220
- n
221
- end
56
+ #
57
+ # Class used to forge a String from a stream of bits.
58
+ # Internally used by some filters.
59
+ #
60
+ class BitWriter
61
+ def initialize
62
+ @data = ''
63
+ @last_byte = nil
64
+ @ptr_bit = 0
65
+ end
66
+
67
+ #
68
+ # Writes _data_ represented as Fixnum to a _length_ number of bits.
69
+ #
70
+ def write(data, length)
71
+ return BitWriterError, "Invalid data length" unless length > 0 and (1 << length) > data
72
+
73
+ # optimization for aligned byte writing
74
+ if length == 8 and @last_byte.nil? and @ptr_bit == 0
75
+ @data << data.chr
76
+ return self
77
+ end
78
+
79
+ while length > 0
80
+ if length >= 8 - @ptr_bit
81
+ length -= 8 - @ptr_bit
82
+ @last_byte ||= 0
83
+ @last_byte |= (data >> length) & ((1 << (8 - @ptr_bit)) - 1)
84
+
85
+ data &= (1 << length) - 1
86
+ @data << @last_byte.chr
87
+ @last_byte = nil
88
+ @ptr_bit = 0
89
+ else
90
+ @last_byte ||= 0
91
+ @last_byte |= (data & ((1 << length) - 1)) << (8 - @ptr_bit - length)
92
+ @ptr_bit += length
93
+
94
+ if @ptr_bit == 8
95
+ @data << @last_byte.chr
96
+ @last_byte = nil
97
+ @ptr_bit = 0
98
+ end
99
+
100
+ length = 0
101
+ end
102
+ end
103
+
104
+ self
105
+ end
106
+
107
+ #
108
+ # Returns the data size in bits.
109
+ #
110
+ def size
111
+ (@data.size << 3) + @ptr_bit
112
+ end
113
+
114
+ #
115
+ # Finalizes the stream.
116
+ #
117
+ def final
118
+ @data << @last_byte.chr if @last_byte
119
+ @last_byte = nil
120
+ @p = 0
121
+
122
+ self
123
+ end
124
+
125
+ #
126
+ # Outputs the stream as a String.
127
+ #
128
+ def to_s
129
+ @data.dup
130
+ end
131
+ end
222
132
 
223
- #
224
- # Used for bit scanning.
225
- # Counts leading zeros. Does not advance read pointer.
226
- #
227
- def clz
228
- count = 0
229
- if @ptr_bit != 0
230
- bits = peek(8 - @ptr_bit)
231
- count = clz32(bits << (32 - (8 - @ptr_bit)))
232
-
233
- return count if count < (8 - @ptr_bit)
234
- end
235
-
236
- delta = 0
237
- while @data.size > @ptr_byte + delta * 4
238
- word = @data[@ptr_byte + delta * 4, 4] # next 32 bits
239
- z = clz32((word << (4 - word.size)).unpack("N")[0])
240
-
241
- count += z
242
- delta += 1
243
-
244
- return count if z < 32 - ((4 - word.size) << 3)
245
- end
246
-
247
- count
248
- end
133
+ class BitReaderError < Error #:nodoc:
134
+ end
249
135
 
250
- #
251
- # Used for bit scanning.
252
- # Count leading ones. Does not advance read pointer.
253
- #
254
- def clo
255
- count = 0
256
- if @ptr_bit != 0
257
- bits = peek(8 - @ptr_bit)
258
- count = clz32(~(bits << (32 - (8 - @ptr_bit))) & 0xff)
259
-
260
- return count if count < (8 - @ptr_bit)
261
- end
262
-
263
- delta = 0
264
- while @data.size > @ptr_byte + delta * 4
265
- word = @data[@ptr_byte + delta * 4, 4] # next 32 bits
266
- z = clz32(~((word << (4 - word.size)).unpack("N")[0]) & 0xffff_ffff)
267
-
268
- count += z
269
- delta += 1
270
-
271
- return count if z < 32 - ((4 - word.size) << 3)
272
- end
273
-
274
- count
136
+ #
137
+ # Class used to read a String as a stream of bits.
138
+ # Internally used by some filters.
139
+ #
140
+ class BitReader
141
+ BRUIJIN_TABLE = ::Array.new(32)
142
+ BRUIJIN_TABLE.size.times do |i|
143
+ BRUIJIN_TABLE[((0x77cb531 * (1 << i)) >> 27) & 31] = i
144
+ end
145
+
146
+ def initialize(data)
147
+ @data = data
148
+ reset
149
+ end
150
+
151
+ #
152
+ # Resets the read pointer.
153
+ #
154
+ def reset
155
+ @ptr_byte, @ptr_bit = 0, 0
156
+ self
157
+ end
158
+
159
+ #
160
+ # Returns true if end of data has been reached.
161
+ #
162
+ def eod?
163
+ @ptr_byte >= @data.size
164
+ end
165
+
166
+ #
167
+ # Returns the read pointer position in bits.
168
+ #
169
+ def pos
170
+ (@ptr_byte << 3) + @ptr_bit
171
+ end
172
+
173
+ #
174
+ # Returns the data size in bits.
175
+ #
176
+ def size
177
+ @data.size << 3
178
+ end
179
+
180
+ #
181
+ # Sets the read pointer position in bits.
182
+ #
183
+ def pos=(bits)
184
+ raise BitReaderError, "Pointer position out of data" if bits > self.size
185
+
186
+ pbyte = bits >> 3
187
+ pbit = bits - (pbyte << 3)
188
+ @ptr_byte, @ptr_bit = pbyte, pbit
189
+
190
+ bits
191
+ end
192
+
193
+ #
194
+ # Reads _length_ bits as a Fixnum and advances read pointer.
195
+ #
196
+ def read(length)
197
+ n = self.peek(length)
198
+ self.pos += length
199
+
200
+ n
201
+ end
202
+
203
+ #
204
+ # Reads _length_ bits as a Fixnum. Does not advance read pointer.
205
+ #
206
+ def peek(length)
207
+ return BitReaderError, "Invalid read length" unless length > 0
208
+ return BitReaderError, "Insufficient data" if self.pos + length > self.size
209
+
210
+ n = 0
211
+ ptr_byte, ptr_bit = @ptr_byte, @ptr_bit
212
+
213
+ while length > 0
214
+ byte = @data[ptr_byte].ord
215
+
216
+ if length > 8 - ptr_bit
217
+ length -= 8 - ptr_bit
218
+ n |= ( byte & ((1 << (8 - ptr_bit)) - 1) ) << length
219
+
220
+ ptr_byte += 1
221
+ ptr_bit = 0
222
+ else
223
+ n |= (byte >> (8 - ptr_bit - length)) & ((1 << length) - 1)
224
+ length = 0
225
+ end
226
+ end
227
+
228
+ n
229
+ end
230
+
231
+ #
232
+ # Used for bit scanning.
233
+ # Counts leading zeros. Does not advance read pointer.
234
+ #
235
+ def clz
236
+ count = 0
237
+ if @ptr_bit != 0
238
+ bits = peek(8 - @ptr_bit)
239
+ count = clz32(bits << (32 - (8 - @ptr_bit)))
240
+
241
+ return count if count < (8 - @ptr_bit)
242
+ end
243
+
244
+ delta = 0
245
+ while @data.size > @ptr_byte + delta * 4
246
+ word = @data[@ptr_byte + delta * 4, 4] # next 32 bits
247
+ z = clz32((word << (4 - word.size)).unpack("N")[0])
248
+
249
+ count += z
250
+ delta += 1
251
+
252
+ return count if z < 32 - ((4 - word.size) << 3)
253
+ end
254
+
255
+ count
256
+ end
257
+
258
+ #
259
+ # Used for bit scanning.
260
+ # Count leading ones. Does not advance read pointer.
261
+ #
262
+ def clo
263
+ count = 0
264
+ if @ptr_bit != 0
265
+ bits = peek(8 - @ptr_bit)
266
+ count = clz32(~(bits << (32 - (8 - @ptr_bit))) & 0xff)
267
+
268
+ return count if count < (8 - @ptr_bit)
269
+ end
270
+
271
+ delta = 0
272
+ while @data.size > @ptr_byte + delta * 4
273
+ word = @data[@ptr_byte + delta * 4, 4] # next 32 bits
274
+ z = clz32(~((word << (4 - word.size)).unpack("N")[0]) & 0xffff_ffff)
275
+
276
+ count += z
277
+ delta += 1
278
+
279
+ return count if z < 32 - ((4 - word.size) << 3)
280
+ end
281
+
282
+ count
283
+ end
284
+
285
+ private
286
+
287
+ def bitswap8(i) #:nodoc
288
+ ((i * 0x0202020202) & 0x010884422010) % 1023
289
+ end
290
+
291
+ def bitswap32(i) #:nodoc:
292
+ (bitswap8((i >> 0) & 0xff) << 24) |
293
+ (bitswap8((i >> 8) & 0xff) << 16) |
294
+ (bitswap8((i >> 16) & 0xff) << 8) |
295
+ (bitswap8((i >> 24) & 0xff) << 0)
296
+ end
297
+
298
+ def ctz32(i) #:nodoc:
299
+ if i == 0 then 32
300
+ else
301
+ BRUIJIN_TABLE[(((i & -i) * 0x77cb531) >> 27) & 31]
302
+ end
303
+ end
304
+
305
+ def clz32(i) #:nodoc:
306
+ ctz32 bitswap32 i
307
+ end
308
+ end
275
309
  end
276
310
 
277
- private
278
-
279
- def bitswap8(i) #:nodoc
280
- ((i * 0x0202020202) & 0x010884422010) % 1023
281
- end
311
+ module ClassMethods
312
+ #
313
+ # Decodes the given data.
314
+ # _stream_:: The data to decode.
315
+ #
316
+ def decode(stream, params = {})
317
+ self.new(params).decode(stream)
318
+ end
282
319
 
283
- def bitswap32(i) #:nodoc:
284
- (bitswap8((i >> 0) & 0xff) << 24) |
285
- (bitswap8((i >> 8) & 0xff) << 16) |
286
- (bitswap8((i >> 16) & 0xff) << 8) |
287
- (bitswap8((i >> 24) & 0xff) << 0)
320
+ #
321
+ # Encodes the given data.
322
+ # _stream_:: The data to encode.
323
+ #
324
+ def encode(stream, params = {})
325
+ self.new(params).encode(stream)
326
+ end
288
327
  end
289
328
 
290
- def ctz32(i) #:nodoc:
291
- if i == 0 then 32
292
- else
293
- BRUIJIN_TABLE[(((i & -i) * 0x77cb531) >> 27) & 31]
294
- end
329
+ def initialize(parameters = {})
330
+ @params = parameters
295
331
  end
296
332
 
297
- def clz32(i) #:nodoc:
298
- ctz32 bitswap32 i
333
+ def self.included(receiver)
334
+ receiver.extend(ClassMethods)
299
335
  end
300
- end
301
- end
302
-
303
- module ClassMethods
304
- #
305
- # Decodes the given data.
306
- # _stream_:: The data to decode.
307
- #
308
- def decode(stream, params = {})
309
- self.new(params).decode(stream)
310
- end
311
-
312
- #
313
- # Encodes the given data.
314
- # _stream_:: The data to encode.
315
- #
316
- def encode(stream, params = {})
317
- self.new(params).encode(stream)
318
- end
319
336
  end
320
337
 
321
- def initialize(parameters = {})
322
- @params = parameters
323
- end
324
-
325
- def self.included(receiver)
326
- receiver.extend(ClassMethods)
327
- end
328
-
329
- end
330
-
331
338
  end
332
339
 
333
340
  require 'origami/filters/ascii'
@@ -339,4 +346,3 @@ require 'origami/filters/dct'
339
346
  require 'origami/filters/jbig2'
340
347
  require 'origami/filters/jpx'
341
348
  require 'origami/filters/crypt'
342
-