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
@@ -1,196 +1,193 @@
1
1
  =begin
2
2
 
3
- = File
4
- filters/ascii.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é.
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/>.
23
18
 
24
19
  =end
25
20
 
26
21
  module Origami
27
22
 
28
- module Filter
29
-
30
- class InvalidASCIIHexStringError < InvalidFilterDataError #:nodoc:
31
- end
32
-
33
- #
34
- # Class representing a filter used to encode and decode data written into hexadecimal.
35
- #
36
- class ASCIIHex
37
- include Filter
38
-
39
- EOD = ">" #:nodoc:
40
-
41
- #
42
- # Encodes given data into upcase hexadecimal representation.
43
- # _stream_:: The data to encode.
44
- #
45
- def encode(stream)
46
- stream.unpack("H2" * stream.size).join.upcase
47
- end
48
-
49
- #
50
- # Decodes given data writen into upcase hexadecimal representation.
51
- # _string_:: The data to decode.
52
- #
53
- def decode(string)
54
-
55
- input = string.include?(?>) ? string[0..string.index(?>) - 1] : string
56
- digits = input.delete(" \f\t\r\n\0").split(/(..)/).delete_if{|digit| digit.empty?}
57
-
58
- if not digits.all? { |d| d =~ /[a-fA-F0-9]{1,2}/ }
59
- raise InvalidASCIIHexStringError, input
23
+ module Filter
24
+
25
+ class InvalidASCIIHexStringError < DecodeError #:nodoc:
60
26
  end
61
-
62
- digits.pack("H2" * digits.size)
63
- end
64
-
65
- end
66
- AHx = ASCIIHex
67
-
68
- class InvalidASCII85StringError < InvalidFilterDataError #:nodoc:
69
- end
70
-
71
- #
72
- # Class representing a filter used to encode and decode data written in base85 encoding.
73
- #
74
- class ASCII85
75
- include Filter
76
-
77
- EOD = "~>" #:nodoc:
78
-
79
- #
80
- # Encodes given data into base85.
81
- # _stream_:: The data to encode.
82
- #
83
- def encode(stream)
84
-
85
- i = 0
86
- code = ""
87
- input = stream.dup
88
-
89
- while i < input.size do
90
-
91
- if input.length - i < 4
92
- addend = 4 - (input.length - i)
93
- input << "\0" * addend
94
- else
95
- addend = 0
96
- end
97
-
98
- inblock = (input[i].ord * 256**3 + input[i+1].ord * 256**2 + input[i+2].ord * 256 + input[i+3].ord)
99
- outblock = ""
100
-
101
- 5.times do |p|
102
- c = inblock / 85 ** (4 - p)
103
- outblock << ("!"[0].ord + c).chr
104
-
105
- inblock -= c * 85 ** (4 - p)
106
- end
107
-
108
- outblock = "z" if outblock == "!!!!!" and addend == 0
109
-
110
- if addend != 0
111
- outblock = outblock[0,(4 - addend) + 1]
112
- end
113
-
114
- code << outblock
115
-
116
- i = i + 4
27
+
28
+ #
29
+ # Class representing a filter used to encode and decode data written into hexadecimal.
30
+ #
31
+ class ASCIIHex
32
+ include Filter
33
+
34
+ EOD = ">" #:nodoc:
35
+
36
+ #
37
+ # Encodes given data into upcase hexadecimal representation.
38
+ # _stream_:: The data to encode.
39
+ #
40
+ def encode(stream)
41
+ stream.unpack("H*").join.upcase
42
+ end
43
+
44
+ #
45
+ # Decodes given data writen into upcase hexadecimal representation.
46
+ # _string_:: The data to decode.
47
+ #
48
+ def decode(string)
49
+ input = string.include?(EOD) ? string[0...string.index(EOD)] : string
50
+ digits = input.delete(" \f\t\r\n\0")
51
+
52
+ # Ensure every digit is in the hexadecimal charset.
53
+ unless digits =~ /^\h*$/
54
+ digits = digits.match(/^\h*/).to_s
55
+
56
+ raise InvalidASCIIHexStringError.new("Invalid characters", input_data: string, decoded_data: [ digits ].pack('H*'))
57
+ end
58
+
59
+ [ digits ].pack "H*"
60
+ end
61
+
62
+ end
63
+ AHx = ASCIIHex
64
+
65
+ class InvalidASCII85StringError < DecodeError #:nodoc:
117
66
  end
118
67
 
119
- code
120
- end
121
-
122
- #
123
- # Decodes the given data encoded in base85.
124
- # _string_:: The data to decode.
125
- #
126
- def decode(string)
127
-
128
- input = (string.include?(EOD) ? string[0..string.index(EOD) - 1] : string).delete(" \f\t\r\n\0")
129
-
130
- i = 0
131
- result = ""
132
- while i < input.size do
133
-
134
- outblock = ""
135
-
136
- if input[i].ord == "z"[0].ord
137
- inblock = 0
138
- codelen = 1
139
- else
140
-
141
- inblock = 0
142
- codelen = 5
143
-
144
- if input.length - i < 5
145
- raise InvalidASCII85StringError.new("Invalid length", result) if input.length - i == 1
146
-
147
- addend = 5 - (input.length - i)
148
- input << "u" * addend
149
- else
150
- addend = 0
68
+ #
69
+ # Class representing a filter used to encode and decode data written in base85 encoding.
70
+ #
71
+ class ASCII85
72
+ include Filter
73
+
74
+ EOD = "~>" #:nodoc:
75
+
76
+ #
77
+ # Encodes given data into base85.
78
+ # _stream_:: The data to encode.
79
+ #
80
+ def encode(stream)
81
+ i = 0
82
+ code = "".b
83
+ input = stream.dup
84
+
85
+ while i < input.size do
86
+
87
+ if input.length - i < 4
88
+ addend = 4 - (input.length - i)
89
+ input << "\0" * addend
90
+ else
91
+ addend = 0
92
+ end
93
+
94
+ inblock = (input[i].ord * 256**3 +
95
+ input[i+1].ord * 256**2 +
96
+ input[i+2].ord * 256 +
97
+ input[i+3].ord)
98
+ outblock = ""
99
+
100
+ 5.times do |p|
101
+ c = inblock / 85 ** (4 - p)
102
+ outblock << ("!".ord + c).chr
103
+
104
+ inblock -= c * 85 ** (4 - p)
105
+ end
106
+
107
+ outblock = "z" if outblock == "!!!!!" and addend == 0
108
+
109
+ if addend != 0
110
+ outblock = outblock[0,(4 - addend) + 1]
111
+ end
112
+
113
+ code << outblock
114
+
115
+ i = i + 4
116
+ end
117
+
118
+ code
151
119
  end
152
-
153
- # Checking if this string is in base85
154
- 5.times do |j|
155
- if input[i+j].ord > "u"[0].ord or input[i+j].ord < "!"[0].ord
156
- raise InvalidASCII85StringError.new(
157
- "Invalid character sequence: #{input[i,5].inspect}",
158
- result
159
- )
160
- else
161
- inblock += (input[i+j].ord - "!"[0].ord) * 85 ** (4 - j)
162
- end
120
+
121
+ #
122
+ # Decodes the given data encoded in base85.
123
+ # _string_:: The data to decode.
124
+ #
125
+ def decode(string)
126
+ input = (string.include?(EOD) ? string[0..string.index(EOD) - 1] : string).delete(" \f\t\r\n\0")
127
+
128
+ i = 0
129
+ result = "".b
130
+ while i < input.size do
131
+
132
+ outblock = ""
133
+
134
+ if input[i] == "z"
135
+ inblock = 0
136
+ codelen = 1
137
+ else
138
+ inblock = 0
139
+ codelen = 5
140
+
141
+ if input.length - i < 5
142
+ raise InvalidASCII85StringError.new("Invalid length", input_data: string, decoded_data: result) if input.length - i == 1
143
+
144
+ addend = 5 - (input.length - i)
145
+ input << "u" * addend
146
+ else
147
+ addend = 0
148
+ end
149
+
150
+ # Checking if this string is in base85
151
+ 5.times do |j|
152
+ if input[i+j].ord > "u".ord or input[i+j].ord < "!".ord
153
+ raise InvalidASCII85StringError.new(
154
+ "Invalid character sequence: #{input[i,5].inspect}",
155
+ input_data: string,
156
+ decoded_data: result
157
+ )
158
+ else
159
+ inblock += (input[i+j].ord - "!".ord) * 85 ** (4 - j)
160
+ end
161
+ end
162
+
163
+ raise InvalidASCII85StringError.new(
164
+ "Invalid value (#{inblock}) for block #{input[i,5].inspect}",
165
+ input_data: string,
166
+ decoded_data: result
167
+ ) if inblock >= 2**32
168
+ end
169
+
170
+ 4.times do |p|
171
+ c = inblock / 256 ** (3 - p)
172
+ outblock << c.chr
173
+
174
+ inblock -= c * 256 ** (3 - p)
175
+ end
176
+
177
+ if addend != 0
178
+ outblock = outblock[0, 4 - addend]
179
+ end
180
+
181
+ result << outblock
182
+
183
+ i = i + codelen
184
+ end
185
+
186
+ result
163
187
  end
164
-
165
-
166
- raise InvalidASCII85StringError.new(
167
- "Invalid value (#{inblock}) for block #{input[i,5].inspect}",
168
- result
169
- ) if inblock >= 2**32
170
-
171
- end
172
-
173
- 4.times do |p|
174
- c = inblock / 256 ** (3 - p)
175
- outblock << c.chr
176
-
177
- inblock -= c * 256 ** (3 - p)
178
- end
179
-
180
- if addend != 0
181
- outblock = outblock[0, 4 - addend]
182
- end
183
-
184
- result << outblock
185
-
186
- i = i + codelen
188
+
187
189
  end
188
-
189
- result
190
- end
190
+ A85 = ASCII85
191
191
 
192
192
  end
193
- A85 = ASCII85
194
- end
195
193
  end
196
-
@@ -1,576 +1,569 @@
1
1
  =begin
2
2
 
3
- = File
4
- filters/ccitt.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
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.
25
10
 
26
- module Origami
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.
27
15
 
28
- module Filter
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/>.
29
18
 
30
- class InvalidCCITTFaxDataError < InvalidFilterDataError #:nodoc:
31
- end
19
+ =end
32
20
 
33
- class CCITTFaxFilterError < Exception #:nodoc:
34
- end
21
+ module Origami
35
22
 
36
- #
37
- # Class representing a Filter used to encode and decode data with CCITT-facsimile compression algorithm.
38
- #
39
- class CCITTFax
40
- include Filter
41
-
42
- class DecodeParms < Dictionary
43
- include StandardObject
44
-
45
- field :K, :Type => Integer, :Default => 0
46
- field :EndOfLine, :Type => Boolean, :Default => false
47
- field :EncodedByteAlign, :Type => Boolean, :Default => false
48
- field :Columns, :Type => Integer, :Default => 1728
49
- field :Rows, :Type => Integer, :Default => 0
50
- field :EndOfBlock, :Type => Boolean, :Default => true
51
- field :BlackIs1, :Type => Boolean, :Default => false
52
- field :DamagedRowsBeforeError, :Type => :Integer, :Default => 0
53
- end
54
-
55
- def self.codeword(str) #:nodoc:
56
- [ str.to_i(2), str.length ]
57
- end
58
-
59
- EOL = codeword('000000000001')
60
- RTC = codeword('000000000001' * 6)
61
-
62
- WHITE_TERMINAL_ENCODE_TABLE =
63
- {
64
- 0 => codeword('00110101'),
65
- 1 => codeword('000111'),
66
- 2 => codeword('0111'),
67
- 3 => codeword('1000'),
68
- 4 => codeword('1011'),
69
- 5 => codeword('1100'),
70
- 6 => codeword('1110'),
71
- 7 => codeword('1111'),
72
- 8 => codeword('10011'),
73
- 9 => codeword('10100'),
74
- 10 => codeword('00111'),
75
- 11 => codeword('01000'),
76
- 12 => codeword('001000'),
77
- 13 => codeword('000011'),
78
- 14 => codeword('110100'),
79
- 15 => codeword('110101'),
80
- 16 => codeword('101010'),
81
- 17 => codeword('101011'),
82
- 18 => codeword('0100111'),
83
- 19 => codeword('0001100'),
84
- 20 => codeword('0001000'),
85
- 21 => codeword('0010111'),
86
- 22 => codeword('0000011'),
87
- 23 => codeword('0000100'),
88
- 24 => codeword('0101000'),
89
- 25 => codeword('0101011'),
90
- 26 => codeword('0010011'),
91
- 27 => codeword('0100100'),
92
- 28 => codeword('0011000'),
93
- 29 => codeword('00000010'),
94
- 30 => codeword('00000011'),
95
- 31 => codeword('00011010'),
96
- 32 => codeword('00011011'),
97
- 33 => codeword('00010010'),
98
- 34 => codeword('00010011'),
99
- 35 => codeword('00010100'),
100
- 36 => codeword('00010101'),
101
- 37 => codeword('00010110'),
102
- 38 => codeword('00010111'),
103
- 39 => codeword('00101000'),
104
- 40 => codeword('00101001'),
105
- 41 => codeword('00101010'),
106
- 42 => codeword('00101011'),
107
- 43 => codeword('00101100'),
108
- 44 => codeword('00101101'),
109
- 45 => codeword('00000100'),
110
- 46 => codeword('00000101'),
111
- 47 => codeword('00001010'),
112
- 48 => codeword('00001011'),
113
- 49 => codeword('01010010'),
114
- 50 => codeword('01010011'),
115
- 51 => codeword('01010100'),
116
- 52 => codeword('01010101'),
117
- 53 => codeword('00100100'),
118
- 54 => codeword('00100101'),
119
- 55 => codeword('01011000'),
120
- 56 => codeword('01011001'),
121
- 57 => codeword('01011010'),
122
- 58 => codeword('01011011'),
123
- 59 => codeword('01001010'),
124
- 60 => codeword('01001011'),
125
- 61 => codeword('00110010'),
126
- 62 => codeword('00110011'),
127
- 63 => codeword('00110100')
128
- }
129
- WHITE_TERMINAL_DECODE_TABLE = WHITE_TERMINAL_ENCODE_TABLE.invert
130
-
131
- BLACK_TERMINAL_ENCODE_TABLE =
132
- {
133
- 0 => codeword('0000110111'),
134
- 1 => codeword('010'),
135
- 2 => codeword('11'),
136
- 3 => codeword('10'),
137
- 4 => codeword('011'),
138
- 5 => codeword('0011'),
139
- 6 => codeword('0010'),
140
- 7 => codeword('00011'),
141
- 8 => codeword('000101'),
142
- 9 => codeword('000100'),
143
- 10 => codeword('0000100'),
144
- 11 => codeword('0000101'),
145
- 12 => codeword('0000111'),
146
- 13 => codeword('00000100'),
147
- 14 => codeword('00000111'),
148
- 15 => codeword('000011000'),
149
- 16 => codeword('0000010111'),
150
- 17 => codeword('0000011000'),
151
- 18 => codeword('0000001000'),
152
- 19 => codeword('00001100111'),
153
- 20 => codeword('00001101000'),
154
- 21 => codeword('00001101100'),
155
- 22 => codeword('00000110111'),
156
- 23 => codeword('00000101000'),
157
- 24 => codeword('00000010111'),
158
- 25 => codeword('00000011000'),
159
- 26 => codeword('000011001010'),
160
- 27 => codeword('000011001011'),
161
- 28 => codeword('000011001100'),
162
- 29 => codeword('000011001101'),
163
- 30 => codeword('000001101000'),
164
- 31 => codeword('000001101001'),
165
- 32 => codeword('000001101010'),
166
- 33 => codeword('000001101011'),
167
- 34 => codeword('000011010010'),
168
- 35 => codeword('000011010011'),
169
- 36 => codeword('000011010100'),
170
- 37 => codeword('000011010101'),
171
- 38 => codeword('000011010110'),
172
- 39 => codeword('000011010111'),
173
- 40 => codeword('000001101100'),
174
- 41 => codeword('000001101101'),
175
- 42 => codeword('000011011010'),
176
- 43 => codeword('000011011011'),
177
- 44 => codeword('000001010100'),
178
- 45 => codeword('000001010101'),
179
- 46 => codeword('000001010110'),
180
- 47 => codeword('000001010111'),
181
- 48 => codeword('000001100100'),
182
- 49 => codeword('000001100101'),
183
- 50 => codeword('000001010010'),
184
- 51 => codeword('000001010011'),
185
- 52 => codeword('000000100100'),
186
- 53 => codeword('000000110111'),
187
- 54 => codeword('000000111000'),
188
- 55 => codeword('000000100111'),
189
- 56 => codeword('000000101000'),
190
- 57 => codeword('000001011000'),
191
- 58 => codeword('000001011001'),
192
- 59 => codeword('000000101011'),
193
- 60 => codeword('000000101100'),
194
- 61 => codeword('000001011010'),
195
- 62 => codeword('000001100110'),
196
- 63 => codeword('000001100111')
197
- }
198
- BLACK_TERMINAL_DECODE_TABLE = BLACK_TERMINAL_ENCODE_TABLE.invert
199
-
200
- WHITE_CONFIGURATION_ENCODE_TABLE =
201
- {
202
- 64 => codeword('11011'),
203
- 128 => codeword('10010'),
204
- 192 => codeword('010111'),
205
- 256 => codeword('0110111'),
206
- 320 => codeword('00110110'),
207
- 384 => codeword('00110111'),
208
- 448 => codeword('01100100'),
209
- 512 => codeword('01100101'),
210
- 576 => codeword('01101000'),
211
- 640 => codeword('01100111'),
212
- 704 => codeword('011001100'),
213
- 768 => codeword('011001101'),
214
- 832 => codeword('011010010'),
215
- 896 => codeword('011010011'),
216
- 960 => codeword('011010100'),
217
- 1024 => codeword('011010101'),
218
- 1088 => codeword('011010110'),
219
- 1152 => codeword('011010111'),
220
- 1216 => codeword('011011000'),
221
- 1280 => codeword('011011001'),
222
- 1344 => codeword('011011010'),
223
- 1408 => codeword('011011011'),
224
- 1472 => codeword('010011000'),
225
- 1536 => codeword('010011001'),
226
- 1600 => codeword('010011010'),
227
- 1664 => codeword('011000'),
228
- 1728 => codeword('010011011'),
229
-
230
- 1792 => codeword('00000001000'),
231
- 1856 => codeword('00000001100'),
232
- 1920 => codeword('00000001001'),
233
- 1984 => codeword('000000010010'),
234
- 2048 => codeword('000000010011'),
235
- 2112 => codeword('000000010100'),
236
- 2176 => codeword('000000010101'),
237
- 2240 => codeword('000000010110'),
238
- 2340 => codeword('000000010111'),
239
- 2368 => codeword('000000011100'),
240
- 2432 => codeword('000000011101'),
241
- 2496 => codeword('000000011110'),
242
- 2560 => codeword('000000011111')
243
- }
244
- WHITE_CONFIGURATION_DECODE_TABLE = WHITE_CONFIGURATION_ENCODE_TABLE.invert
245
-
246
- BLACK_CONFIGURATION_ENCODE_TABLE =
247
- {
248
- 64 => codeword('0000001111'),
249
- 128 => codeword('000011001000'),
250
- 192 => codeword('000011001001'),
251
- 256 => codeword('000001011011'),
252
- 320 => codeword('000000110011'),
253
- 384 => codeword('000000110100'),
254
- 448 => codeword('000000110101'),
255
- 512 => codeword('0000001101100'),
256
- 576 => codeword('0000001101101'),
257
- 640 => codeword('0000001001010'),
258
- 704 => codeword('0000001001011'),
259
- 768 => codeword('0000001001100'),
260
- 832 => codeword('0000001001101'),
261
- 896 => codeword('0000001110010'),
262
- 960 => codeword('0000001110011'),
263
- 1024 => codeword('0000001110100'),
264
- 1088 => codeword('0000001110101'),
265
- 1152 => codeword('0000001110110'),
266
- 1216 => codeword('0000001110111'),
267
- 1280 => codeword('0000001010010'),
268
- 1344 => codeword('0000001010011'),
269
- 1408 => codeword('0000001010100'),
270
- 1472 => codeword('0000001010101'),
271
- 1536 => codeword('0000001011010'),
272
- 1600 => codeword('0000001011011'),
273
- 1664 => codeword('0000001100100'),
274
- 1728 => codeword('0000001100101'),
275
-
276
- 1792 => codeword('00000001000'),
277
- 1856 => codeword('00000001100'),
278
- 1920 => codeword('00000001001'),
279
- 1984 => codeword('000000010010'),
280
- 2048 => codeword('000000010011'),
281
- 2112 => codeword('000000010100'),
282
- 2176 => codeword('000000010101'),
283
- 2240 => codeword('000000010110'),
284
- 2340 => codeword('000000010111'),
285
- 2368 => codeword('000000011100'),
286
- 2432 => codeword('000000011101'),
287
- 2496 => codeword('000000011110'),
288
- 2560 => codeword('000000011111')
289
- }
290
- BLACK_CONFIGURATION_DECODE_TABLE = BLACK_CONFIGURATION_ENCODE_TABLE.invert
291
-
292
- #
293
- # Creates a new CCITT Fax Filter.
294
- #
295
- def initialize(parameters = {})
296
- super(DecodeParms.new(parameters))
297
- end
298
-
299
- #
300
- # Encodes data using CCITT-facsimile compression method.
301
- #
302
- def encode(stream)
303
- mode = @params.has_key?(:K) ? @params.K.value : 0
304
-
305
- unless mode.is_a?(::Integer) and mode <= 0
306
- raise NotImplementedError, "CCITT encoding scheme not supported"
307
- end
23
+ module Filter
308
24
 
309
- columns = @params.has_key?(:Columns) ? @params.Columns.value : (stream.size << 3)
310
- unless columns.is_a?(::Integer) and columns > 0 #and columns % 8 == 0
311
- raise CCITTFaxFilterError, "Invalid value for parameter `Columns'"
25
+ class InvalidCCITTFaxDataError < DecodeError #:nodoc:
312
26
  end
313
27
 
314
- if stream.size % (columns >> 3) != 0
315
- raise CCITTFaxFilterError, "Data size is not a multiple of image width"
28
+ class CCITTFaxFilterError < Error #:nodoc:
316
29
  end
317
30
 
318
- colors = (@params.BlackIs1 == true) ? [0,1] : [1,0]
319
- white, _black = colors
320
- bitr = Utils::BitReader.new(stream)
321
- bitw = Utils::BitWriter.new
31
+ #
32
+ # Class representing a Filter used to encode and decode data with CCITT-facsimile compression algorithm.
33
+ #
34
+ class CCITTFax
35
+ include Filter
36
+
37
+ class DecodeParms < Dictionary
38
+ include StandardObject
39
+
40
+ field :K, :Type => Integer, :Default => 0
41
+ field :EndOfLine, :Type => Boolean, :Default => false
42
+ field :EncodedByteAlign, :Type => Boolean, :Default => false
43
+ field :Columns, :Type => Integer, :Default => 1728
44
+ field :Rows, :Type => Integer, :Default => 0
45
+ field :EndOfBlock, :Type => Boolean, :Default => true
46
+ field :BlackIs1, :Type => Boolean, :Default => false
47
+ field :DamagedRowsBeforeError, :Type => :Integer, :Default => 0
48
+ end
322
49
 
323
- # Group 4 requires an imaginary white line
324
- if mode < 0
325
- prev_line = Utils::BitWriter.new
326
- write_bit_range(prev_line, white, columns)
327
- prev_line = Utils::BitReader.new(prev_line.final.to_s)
328
- end
50
+ def self.codeword(str) #:nodoc:
51
+ [ str.to_i(2), str.length ]
52
+ end
329
53
 
330
- until bitr.eod?
331
- case
332
- when mode == 0
333
- encode_one_dimensional_line(bitr, bitw, columns, colors)
334
- when mode < 0
335
- encode_two_dimensional_line(bitr, bitw, columns, colors, prev_line)
336
- end
337
- end
54
+ EOL = codeword('000000000001')
55
+ RTC = codeword('000000000001' * 6)
56
+
57
+ WHITE_TERMINAL_ENCODE_TABLE =
58
+ {
59
+ 0 => codeword('00110101'),
60
+ 1 => codeword('000111'),
61
+ 2 => codeword('0111'),
62
+ 3 => codeword('1000'),
63
+ 4 => codeword('1011'),
64
+ 5 => codeword('1100'),
65
+ 6 => codeword('1110'),
66
+ 7 => codeword('1111'),
67
+ 8 => codeword('10011'),
68
+ 9 => codeword('10100'),
69
+ 10 => codeword('00111'),
70
+ 11 => codeword('01000'),
71
+ 12 => codeword('001000'),
72
+ 13 => codeword('000011'),
73
+ 14 => codeword('110100'),
74
+ 15 => codeword('110101'),
75
+ 16 => codeword('101010'),
76
+ 17 => codeword('101011'),
77
+ 18 => codeword('0100111'),
78
+ 19 => codeword('0001100'),
79
+ 20 => codeword('0001000'),
80
+ 21 => codeword('0010111'),
81
+ 22 => codeword('0000011'),
82
+ 23 => codeword('0000100'),
83
+ 24 => codeword('0101000'),
84
+ 25 => codeword('0101011'),
85
+ 26 => codeword('0010011'),
86
+ 27 => codeword('0100100'),
87
+ 28 => codeword('0011000'),
88
+ 29 => codeword('00000010'),
89
+ 30 => codeword('00000011'),
90
+ 31 => codeword('00011010'),
91
+ 32 => codeword('00011011'),
92
+ 33 => codeword('00010010'),
93
+ 34 => codeword('00010011'),
94
+ 35 => codeword('00010100'),
95
+ 36 => codeword('00010101'),
96
+ 37 => codeword('00010110'),
97
+ 38 => codeword('00010111'),
98
+ 39 => codeword('00101000'),
99
+ 40 => codeword('00101001'),
100
+ 41 => codeword('00101010'),
101
+ 42 => codeword('00101011'),
102
+ 43 => codeword('00101100'),
103
+ 44 => codeword('00101101'),
104
+ 45 => codeword('00000100'),
105
+ 46 => codeword('00000101'),
106
+ 47 => codeword('00001010'),
107
+ 48 => codeword('00001011'),
108
+ 49 => codeword('01010010'),
109
+ 50 => codeword('01010011'),
110
+ 51 => codeword('01010100'),
111
+ 52 => codeword('01010101'),
112
+ 53 => codeword('00100100'),
113
+ 54 => codeword('00100101'),
114
+ 55 => codeword('01011000'),
115
+ 56 => codeword('01011001'),
116
+ 57 => codeword('01011010'),
117
+ 58 => codeword('01011011'),
118
+ 59 => codeword('01001010'),
119
+ 60 => codeword('01001011'),
120
+ 61 => codeword('00110010'),
121
+ 62 => codeword('00110011'),
122
+ 63 => codeword('00110100')
123
+ }
124
+ WHITE_TERMINAL_DECODE_TABLE = WHITE_TERMINAL_ENCODE_TABLE.invert
125
+
126
+ BLACK_TERMINAL_ENCODE_TABLE =
127
+ {
128
+ 0 => codeword('0000110111'),
129
+ 1 => codeword('010'),
130
+ 2 => codeword('11'),
131
+ 3 => codeword('10'),
132
+ 4 => codeword('011'),
133
+ 5 => codeword('0011'),
134
+ 6 => codeword('0010'),
135
+ 7 => codeword('00011'),
136
+ 8 => codeword('000101'),
137
+ 9 => codeword('000100'),
138
+ 10 => codeword('0000100'),
139
+ 11 => codeword('0000101'),
140
+ 12 => codeword('0000111'),
141
+ 13 => codeword('00000100'),
142
+ 14 => codeword('00000111'),
143
+ 15 => codeword('000011000'),
144
+ 16 => codeword('0000010111'),
145
+ 17 => codeword('0000011000'),
146
+ 18 => codeword('0000001000'),
147
+ 19 => codeword('00001100111'),
148
+ 20 => codeword('00001101000'),
149
+ 21 => codeword('00001101100'),
150
+ 22 => codeword('00000110111'),
151
+ 23 => codeword('00000101000'),
152
+ 24 => codeword('00000010111'),
153
+ 25 => codeword('00000011000'),
154
+ 26 => codeword('000011001010'),
155
+ 27 => codeword('000011001011'),
156
+ 28 => codeword('000011001100'),
157
+ 29 => codeword('000011001101'),
158
+ 30 => codeword('000001101000'),
159
+ 31 => codeword('000001101001'),
160
+ 32 => codeword('000001101010'),
161
+ 33 => codeword('000001101011'),
162
+ 34 => codeword('000011010010'),
163
+ 35 => codeword('000011010011'),
164
+ 36 => codeword('000011010100'),
165
+ 37 => codeword('000011010101'),
166
+ 38 => codeword('000011010110'),
167
+ 39 => codeword('000011010111'),
168
+ 40 => codeword('000001101100'),
169
+ 41 => codeword('000001101101'),
170
+ 42 => codeword('000011011010'),
171
+ 43 => codeword('000011011011'),
172
+ 44 => codeword('000001010100'),
173
+ 45 => codeword('000001010101'),
174
+ 46 => codeword('000001010110'),
175
+ 47 => codeword('000001010111'),
176
+ 48 => codeword('000001100100'),
177
+ 49 => codeword('000001100101'),
178
+ 50 => codeword('000001010010'),
179
+ 51 => codeword('000001010011'),
180
+ 52 => codeword('000000100100'),
181
+ 53 => codeword('000000110111'),
182
+ 54 => codeword('000000111000'),
183
+ 55 => codeword('000000100111'),
184
+ 56 => codeword('000000101000'),
185
+ 57 => codeword('000001011000'),
186
+ 58 => codeword('000001011001'),
187
+ 59 => codeword('000000101011'),
188
+ 60 => codeword('000000101100'),
189
+ 61 => codeword('000001011010'),
190
+ 62 => codeword('000001100110'),
191
+ 63 => codeword('000001100111')
192
+ }
193
+ BLACK_TERMINAL_DECODE_TABLE = BLACK_TERMINAL_ENCODE_TABLE.invert
194
+
195
+ WHITE_CONFIGURATION_ENCODE_TABLE =
196
+ {
197
+ 64 => codeword('11011'),
198
+ 128 => codeword('10010'),
199
+ 192 => codeword('010111'),
200
+ 256 => codeword('0110111'),
201
+ 320 => codeword('00110110'),
202
+ 384 => codeword('00110111'),
203
+ 448 => codeword('01100100'),
204
+ 512 => codeword('01100101'),
205
+ 576 => codeword('01101000'),
206
+ 640 => codeword('01100111'),
207
+ 704 => codeword('011001100'),
208
+ 768 => codeword('011001101'),
209
+ 832 => codeword('011010010'),
210
+ 896 => codeword('011010011'),
211
+ 960 => codeword('011010100'),
212
+ 1024 => codeword('011010101'),
213
+ 1088 => codeword('011010110'),
214
+ 1152 => codeword('011010111'),
215
+ 1216 => codeword('011011000'),
216
+ 1280 => codeword('011011001'),
217
+ 1344 => codeword('011011010'),
218
+ 1408 => codeword('011011011'),
219
+ 1472 => codeword('010011000'),
220
+ 1536 => codeword('010011001'),
221
+ 1600 => codeword('010011010'),
222
+ 1664 => codeword('011000'),
223
+ 1728 => codeword('010011011'),
224
+
225
+ 1792 => codeword('00000001000'),
226
+ 1856 => codeword('00000001100'),
227
+ 1920 => codeword('00000001001'),
228
+ 1984 => codeword('000000010010'),
229
+ 2048 => codeword('000000010011'),
230
+ 2112 => codeword('000000010100'),
231
+ 2176 => codeword('000000010101'),
232
+ 2240 => codeword('000000010110'),
233
+ 2340 => codeword('000000010111'),
234
+ 2368 => codeword('000000011100'),
235
+ 2432 => codeword('000000011101'),
236
+ 2496 => codeword('000000011110'),
237
+ 2560 => codeword('000000011111')
238
+ }
239
+ WHITE_CONFIGURATION_DECODE_TABLE = WHITE_CONFIGURATION_ENCODE_TABLE.invert
240
+
241
+ BLACK_CONFIGURATION_ENCODE_TABLE =
242
+ {
243
+ 64 => codeword('0000001111'),
244
+ 128 => codeword('000011001000'),
245
+ 192 => codeword('000011001001'),
246
+ 256 => codeword('000001011011'),
247
+ 320 => codeword('000000110011'),
248
+ 384 => codeword('000000110100'),
249
+ 448 => codeword('000000110101'),
250
+ 512 => codeword('0000001101100'),
251
+ 576 => codeword('0000001101101'),
252
+ 640 => codeword('0000001001010'),
253
+ 704 => codeword('0000001001011'),
254
+ 768 => codeword('0000001001100'),
255
+ 832 => codeword('0000001001101'),
256
+ 896 => codeword('0000001110010'),
257
+ 960 => codeword('0000001110011'),
258
+ 1024 => codeword('0000001110100'),
259
+ 1088 => codeword('0000001110101'),
260
+ 1152 => codeword('0000001110110'),
261
+ 1216 => codeword('0000001110111'),
262
+ 1280 => codeword('0000001010010'),
263
+ 1344 => codeword('0000001010011'),
264
+ 1408 => codeword('0000001010100'),
265
+ 1472 => codeword('0000001010101'),
266
+ 1536 => codeword('0000001011010'),
267
+ 1600 => codeword('0000001011011'),
268
+ 1664 => codeword('0000001100100'),
269
+ 1728 => codeword('0000001100101'),
270
+
271
+ 1792 => codeword('00000001000'),
272
+ 1856 => codeword('00000001100'),
273
+ 1920 => codeword('00000001001'),
274
+ 1984 => codeword('000000010010'),
275
+ 2048 => codeword('000000010011'),
276
+ 2112 => codeword('000000010100'),
277
+ 2176 => codeword('000000010101'),
278
+ 2240 => codeword('000000010110'),
279
+ 2340 => codeword('000000010111'),
280
+ 2368 => codeword('000000011100'),
281
+ 2432 => codeword('000000011101'),
282
+ 2496 => codeword('000000011110'),
283
+ 2560 => codeword('000000011111')
284
+ }
285
+ BLACK_CONFIGURATION_DECODE_TABLE = BLACK_CONFIGURATION_ENCODE_TABLE.invert
286
+
287
+ #
288
+ # Creates a new CCITT Fax Filter.
289
+ #
290
+ def initialize(parameters = {})
291
+ super(DecodeParms.new(parameters))
292
+ end
338
293
 
339
- # Emit return-to-control code
340
- bitw.write(*RTC)
294
+ #
295
+ # Encodes data using CCITT-facsimile compression method.
296
+ #
297
+ def encode(stream)
298
+ mode = @params.has_key?(:K) ? @params.K.value : 0
299
+
300
+ unless mode.is_a?(::Integer) and mode <= 0
301
+ raise NotImplementedError.new("CCITT encoding scheme not supported", input_data: stream)
302
+ end
303
+
304
+ columns = @params.has_key?(:Columns) ? @params.Columns.value : (stream.size << 3)
305
+ unless columns.is_a?(::Integer) and columns > 0 #and columns % 8 == 0
306
+ raise CCITTFaxFilterError.new("Invalid value for parameter `Columns'", input_data: stream)
307
+ end
308
+
309
+ if stream.size % (columns >> 3) != 0
310
+ raise CCITTFaxFilterError.new("Data size is not a multiple of image width", input_data: stream)
311
+ end
312
+
313
+ colors = (@params.BlackIs1 == true) ? [0,1] : [1,0]
314
+ white, _black = colors
315
+ bitr = Utils::BitReader.new(stream)
316
+ bitw = Utils::BitWriter.new
317
+
318
+ # Group 4 requires an imaginary white line
319
+ if mode < 0
320
+ prev_line = Utils::BitWriter.new
321
+ write_bit_range(prev_line, white, columns)
322
+ prev_line = Utils::BitReader.new(prev_line.final.to_s)
323
+ end
324
+
325
+ until bitr.eod?
326
+ case
327
+ when mode == 0
328
+ encode_one_dimensional_line(bitr, bitw, columns, colors)
329
+ when mode < 0
330
+ encode_two_dimensional_line(bitr, bitw, columns, colors, prev_line)
331
+ end
332
+ end
333
+
334
+ # Emit return-to-control code
335
+ bitw.write(*RTC)
336
+
337
+ bitw.final.to_s
338
+ end
341
339
 
342
- bitw.final.to_s
343
- end
344
-
345
- #
346
- # Decodes data using CCITT-facsimile compression method.
347
- #
348
- def decode(stream)
349
- mode = @params.has_key?(:K) ? @params.K.value : 0
340
+ #
341
+ # Decodes data using CCITT-facsimile compression method.
342
+ #
343
+ def decode(stream)
344
+ mode = @params.has_key?(:K) ? @params.K.value : 0
345
+
346
+ unless mode.is_a?(::Integer) and mode <= 0
347
+ raise NotImplementedError.new("CCITT encoding scheme not supported", input_data: stream)
348
+ end
349
+
350
+ columns = @params.has_key?(:Columns) ? @params.Columns.value : 1728
351
+ unless columns.is_a?(::Integer) and columns > 0 #and columns % 8 == 0
352
+ raise CCITTFaxFilterError.new("Invalid value for parameter `Columns'", input_data: stream)
353
+ end
354
+
355
+ colors = (@params.BlackIs1 == true) ? [0,1] : [1,0]
356
+ white, _black = colors
357
+ params =
358
+ {
359
+ is_aligned?: (@params.EncodedByteAlign == true),
360
+ has_eob?: (@params.EndOfBlock.nil? or @params.EndOfBlock == true),
361
+ has_eol?: (@params.EndOfLine == true)
362
+ }
363
+
364
+ unless params[:has_eob?]
365
+ unless @params.has_key?(:Rows) and @params.Rows.is_a?(::Integer) and @params.Rows.value > 0
366
+ raise CCITTFaxFilterError.new("Invalid value for parameter `Rows'", input_data: stream)
367
+ end
368
+
369
+ rows = @params.Rows.to_i
370
+ end
371
+
372
+ bitr = Utils::BitReader.new(stream)
373
+ bitw = Utils::BitWriter.new
374
+
375
+ # Group 4 requires an imaginary white line
376
+ if mode < 0
377
+ prev_line = Utils::BitWriter.new
378
+ write_bit_range(prev_line, white, columns)
379
+ prev_line = Utils::BitReader.new(prev_line.final.to_s)
380
+ end
381
+
382
+ until bitr.eod? or rows == 0
383
+ # realign the read line on a 8-bit boundary if required
384
+ if params[:is_aligned?] and bitr.pos % 8 != 0
385
+ bitr.pos += 8 - (bitr.pos % 8)
386
+ end
387
+
388
+ # received return-to-control code
389
+ if params[:has_eob?] and bitr.peek(RTC[1]) == RTC[0]
390
+ bitr.pos += RTC[1]
391
+ break
392
+ end
393
+
394
+ # checking for the presence of EOL
395
+ if bitr.peek(EOL[1]) != EOL[0]
396
+ raise InvalidCCITTFaxDataError.new(
397
+ "No end-of-line pattern found (at bit pos #{bitr.pos}/#{bitr.size}})",
398
+ input_data: stream,
399
+ decoded_data: bitw.final.to_s
400
+ ) if params[:has_eol?]
401
+ else
402
+ bitr.pos += EOL[1]
403
+ end
404
+
405
+ begin
406
+ case
407
+ when mode == 0
408
+ decode_one_dimensional_line(bitr, bitw, columns, colors)
409
+ when mode < 0
410
+ decode_two_dimensional_line(bitr, bitw, columns, colors, prev_line)
411
+ end
412
+ rescue DecodeError => error
413
+ error.input_data = stream
414
+ error.decoded_data = bitw.final.to_s
415
+
416
+ raise error
417
+ end
418
+
419
+
420
+ rows -= 1 unless params[:has_eob?]
421
+ end
422
+
423
+ bitw.final.to_s
424
+ end
350
425
 
351
- unless mode.is_a?(::Integer) and mode <= 0
352
- raise NotImplementedError, "CCITT encoding scheme not supported"
353
- end
426
+ private
427
+
428
+ def encode_one_dimensional_line(input, output, columns, colors) #:nodoc:
429
+ output.write(*EOL)
430
+ scan_len = 0
431
+ white, _black = colors
432
+ current_color = white
433
+
434
+ # Process each bit in line.
435
+ begin
436
+ if input.read(1) == current_color
437
+ scan_len += 1
438
+ else
439
+ if current_color == white
440
+ put_white_bits(output, scan_len)
441
+ else
442
+ put_black_bits(output, scan_len)
443
+ end
444
+
445
+ current_color ^= 1
446
+ scan_len = 1
447
+ end
448
+ end while input.pos % columns != 0
449
+
450
+ if current_color == white
451
+ put_white_bits(output, scan_len)
452
+ else
453
+ put_black_bits(output, scan_len)
454
+ end
455
+
456
+ # Align encoded lign on a 8-bit boundary.
457
+ if @params.EncodedByteAlign == true and output.pos % 8 != 0
458
+ output.write(0, 8 - (output.pos % 8))
459
+ end
460
+ end
354
461
 
355
- columns = @params.has_key?(:Columns) ? @params.Columns.value : 1728
356
- unless columns.is_a?(::Integer) and columns > 0 #and columns % 8 == 0
357
- raise CCITTFaxFilterError, "Invalid value for parameter `Columns'"
358
- end
462
+ def encode_two_dimensional_line(input, output, columns, colors, prev_line) #:nodoc:
463
+ raise NotImplementedError "CCITT two-dimensional encoding scheme not supported."
464
+ end
359
465
 
360
- colors = (@params.BlackIs1 == true) ? [0,1] : [1,0]
361
- white, _black = colors
362
- params =
363
- {
364
- :is_aligned? => (@params.EncodedByteAlign == true),
365
- :has_eob? => (@params.EndOfBlock.nil? or @params.EndOfBlock == true),
366
- :has_eol? => (@params.EndOfLine == true)
367
- }
368
-
369
- unless params[:has_eob?]
370
- unless @params.has_key?(:Rows) and @params.Rows.is_a?(::Integer) and @params.Rows.value > 0
371
- raise CCITTFaxFilterError, "Invalid value for parameter `Rows'"
372
- end
373
-
374
- rows = @params.Rows.to_i
375
- end
466
+ def decode_one_dimensional_line(input, output, columns, colors) #:nodoc:
467
+ white, _black = colors
468
+ current_color = white
376
469
 
377
- bitr = Utils::BitReader.new(stream)
378
- bitw = Utils::BitWriter.new
470
+ line_length = 0
471
+ while line_length < columns
472
+ if current_color == white
473
+ bit_length = get_white_bits(input)
474
+ else
475
+ bit_length = get_black_bits(input)
476
+ end
379
477
 
380
- # Group 4 requires an imaginary white line
381
- if mode < 0
382
- prev_line = Utils::BitWriter.new
383
- write_bit_range(prev_line, white, columns)
384
- prev_line = Utils::BitReader.new(prev_line.final.to_s)
385
- end
478
+ raise InvalidCCITTFaxDataError, "Unfinished line (at bit pos #{input.pos}/#{input.size}})" if bit_length.nil?
386
479
 
387
- until bitr.eod? or rows == 0
388
- # realign the read line on a 8-bit boundary if required
389
- if params[:is_aligned?] and bitr.pos % 8 != 0
390
- bitr.pos += 8 - (bitr.pos % 8)
391
- end
392
-
393
- # received return-to-control code
394
- if params[:has_eob?] and bitr.peek(RTC[1]) == RTC[0]
395
- bitr.pos += RTC[1]
396
- break
397
- end
398
-
399
- # checking for the presence of EOL
400
- if bitr.peek(EOL[1]) != EOL[0]
401
- raise InvalidCCITTFaxDataError.new(
402
- "No end-of-line pattern found (at bit pos #{bitr.pos}/#{bitr.size}})",
403
- bitw.final.to_s
404
- ) if params[:has_eol?]
405
- else
406
- bitr.pos += EOL[1]
407
- end
408
-
409
- case
410
- when mode == 0
411
- decode_one_dimensional_line(bitr, bitw, columns, colors)
412
- when mode < 0
413
- decode_two_dimensional_line(bitr, bitw, columns, colors, prev_line)
414
- end
415
-
416
-
417
- rows -= 1 unless params[:has_eob?]
418
- end
480
+ line_length += bit_length
481
+
482
+ raise InvalidCCITTFaxDataError, "Line is too long (at bit pos #{input.pos}/#{input.size}})" if line_length > columns
419
483
 
420
- bitw.final.to_s
421
- end
422
-
423
- private
424
-
425
- def encode_one_dimensional_line(input, output, columns, colors) #:nodoc:
426
- output.write(*EOL)
427
- scan_len = 0
428
- white, black = colors
429
- current_color = white
430
-
431
- # Process each bit in line.
432
- begin
433
- if input.read(1) == current_color
434
- scan_len += 1
435
- else
436
- if current_color == white
437
- put_white_bits(output, scan_len)
438
- else
439
- put_black_bits(output, scan_len)
484
+ write_bit_range(output, current_color, bit_length)
485
+ current_color ^= 1
486
+ end
440
487
  end
441
488
 
442
- current_color ^= 1
443
- scan_len = 1
444
- end
445
- end while input.pos % columns != 0
489
+ def decode_two_dimensional_line(input, output, columns, colors, prev_line) #:nodoc:
490
+ raise NotImplementedError, "CCITT two-dimensional decoding scheme not supported."
491
+ end
446
492
 
447
- if current_color == white
448
- put_white_bits(output, scan_len)
449
- else
450
- put_black_bits(output, scan_len)
451
- end
493
+ def get_white_bits(bitr) #:nodoc:
494
+ get_color_bits(bitr, WHITE_CONFIGURATION_DECODE_TABLE, WHITE_TERMINAL_DECODE_TABLE)
495
+ end
452
496
 
453
- # Align encoded lign on a 8-bit boundary.
454
- if @params.EncodedByteAlign == true and output.pos % 8 != 0
455
- output.write(0, 8 - (output.pos % 8))
456
- end
457
- end
458
-
459
- def encode_two_dimensional_line(input, output, columns, colors, prev_line) #:nodoc:
460
- raise NotImplementedError, "CCITT two-dimensional encoding scheme not supported."
461
-
462
- white, black = colors
463
- current_color = white
464
- end
465
-
466
- def decode_one_dimensional_line(input, output, columns, colors) #:nodoc:
467
- white, black = colors
468
- current_color = white
469
-
470
- line_length = 0
471
- while line_length < columns
472
- if current_color == white
473
- bit_length = get_white_bits(input)
474
- else
475
- bit_length = get_black_bits(input)
476
- end
477
-
478
- raise InvalidCCITTFaxDataError.new(
479
- "Unfinished line (at bit pos #{input.pos}/#{input.size}})",
480
- output.final.to_s
481
- ) if bit_length.nil?
482
-
483
- line_length += bit_length
484
- raise InvalidCCITTFaxDataError.new(
485
- "Line is too long (at bit pos #{input.pos}/#{input.size}})",
486
- output.final.to_s
487
- ) if line_length > columns
488
-
489
- write_bit_range(output, current_color, bit_length)
490
- current_color ^= 1
491
- end
492
- end
493
-
494
- def decode_two_dimensional_line(input, output, columns, colors, prev_line) #:nodoc:
495
- raise NotImplementedError, "CCITT two-dimensional decoding scheme not supported."
496
- end
497
-
498
- def get_white_bits(bitr) #:nodoc:
499
- get_color_bits(bitr, WHITE_CONFIGURATION_DECODE_TABLE, WHITE_TERMINAL_DECODE_TABLE)
500
- end
501
-
502
- def get_black_bits(bitr) #:nodoc:
503
- get_color_bits(bitr, BLACK_CONFIGURATION_DECODE_TABLE, BLACK_TERMINAL_DECODE_TABLE)
504
- end
505
-
506
- def get_color_bits(bitr, config_words, term_words) #:nodoc:
507
- bits = 0
508
- check_conf = true
509
-
510
- while check_conf
511
- check_conf = false
512
- (2..13).each do |length|
513
- codeword = bitr.peek(length)
514
- config_value = config_words[[codeword, length]]
515
-
516
- if config_value
517
- bitr.pos += length
518
- bits += config_value
519
- check_conf = true if config_value == 2560
520
- break
497
+ def get_black_bits(bitr) #:nodoc:
498
+ get_color_bits(bitr, BLACK_CONFIGURATION_DECODE_TABLE, BLACK_TERMINAL_DECODE_TABLE)
521
499
  end
522
- end
523
- end
524
500
 
525
- (2..13).each do |length|
526
- codeword = bitr.peek(length)
527
- term_value = term_words[[codeword, length]]
501
+ def get_color_bits(bitr, config_words, term_words) #:nodoc:
502
+ bits = 0
503
+ check_conf = true
504
+
505
+ while check_conf
506
+ check_conf = false
507
+ (2..13).each do |length|
508
+ codeword = bitr.peek(length)
509
+ config_value = config_words[[codeword, length]]
510
+
511
+ if config_value
512
+ bitr.pos += length
513
+ bits += config_value
514
+ check_conf = true if config_value == 2560
515
+ break
516
+ end
517
+ end
518
+ end
519
+
520
+ (2..13).each do |length|
521
+ codeword = bitr.peek(length)
522
+ term_value = term_words[[codeword, length]]
523
+
524
+ if term_value
525
+ bitr.pos += length
526
+ bits += term_value
527
+
528
+ return bits
529
+ end
530
+ end
531
+
532
+ nil
533
+ end
528
534
 
529
- if term_value
530
- bitr.pos += length
531
- bits += term_value
532
-
533
- return bits
534
- end
535
- end
535
+ def lookup_bits(table, codeword, length)
536
+ table.rassoc [codeword, length]
537
+ end
536
538
 
537
- nil
538
- end
539
+ def put_white_bits(bitw, length) #:nodoc:
540
+ put_color_bits(bitw, length, WHITE_CONFIGURATION_ENCODE_TABLE, WHITE_TERMINAL_ENCODE_TABLE)
541
+ end
539
542
 
540
- def lookup_bits(table, codeword, length)
541
- table.rassoc [codeword, length]
542
- end
543
+ def put_black_bits(bitw, length) #:nodoc:
544
+ put_color_bits(bitw, length, BLACK_CONFIGURATION_ENCODE_TABLE, BLACK_TERMINAL_ENCODE_TABLE)
545
+ end
543
546
 
544
- def put_white_bits(bitw, length) #:nodoc:
545
- put_color_bits(bitw, length, WHITE_CONFIGURATION_ENCODE_TABLE, WHITE_TERMINAL_ENCODE_TABLE)
546
- end
547
+ def put_color_bits(bitw, length, config_words, term_words) #:nodoc:
548
+ while length > 2559
549
+ bitw.write(*config_words[2560])
550
+ length -= 2560
551
+ end
547
552
 
548
- def put_black_bits(bitw, length) #:nodoc:
549
- put_color_bits(bitw, length, BLACK_CONFIGURATION_ENCODE_TABLE, BLACK_TERMINAL_ENCODE_TABLE)
550
- end
553
+ if length > 63
554
+ conf_length = (length >> 6) << 6
555
+ bitw.write(*config_words[conf_length])
556
+ length -= conf_length
557
+ end
551
558
 
552
- def put_color_bits(bitw, length, config_words, term_words) #:nodoc:
553
- while length > 2559
554
- bitw.write(*config_words[2560])
555
- length -= 2560
556
- end
559
+ bitw.write(*term_words[length])
560
+ end
557
561
 
558
- if length > 63
559
- conf_length = (length >> 6) << 6
560
- bitw.write(*config_words[conf_length])
561
- length -= conf_length
562
+ def write_bit_range(bitw, bit_value, length) #:nodoc:
563
+ bitw.write((bit_value << length) - bit_value, length)
564
+ end
562
565
  end
566
+ CCF = CCITTFax
563
567
 
564
- bitw.write(*term_words[length])
565
- end
566
-
567
- def write_bit_range(bitw, bit_value, length) #:nodoc:
568
- bitw.write((bit_value << length) - bit_value, length)
569
- end
570
568
  end
571
- CCF = CCITTFax
572
-
573
- end
574
-
575
569
  end
576
-