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
@@ -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
-