origami 1.2.7 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
-