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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +66 -0
- data/README.md +112 -0
- data/bin/config/pdfcop.conf.yml +232 -233
- data/bin/gui/about.rb +27 -37
- data/bin/gui/config.rb +108 -117
- data/bin/gui/file.rb +416 -365
- data/bin/gui/gtkhex.rb +1138 -1153
- data/bin/gui/hexview.rb +55 -57
- data/bin/gui/imgview.rb +48 -51
- data/bin/gui/menu.rb +388 -386
- data/bin/gui/properties.rb +114 -130
- data/bin/gui/signing.rb +571 -617
- data/bin/gui/textview.rb +77 -95
- data/bin/gui/treeview.rb +382 -387
- data/bin/gui/walker.rb +227 -232
- data/bin/gui/xrefs.rb +56 -60
- data/bin/pdf2pdfa +53 -57
- data/bin/pdf2ruby +212 -228
- data/bin/pdfcop +338 -348
- data/bin/pdfdecompress +58 -65
- data/bin/pdfdecrypt +56 -60
- data/bin/pdfencrypt +75 -80
- data/bin/pdfexplode +185 -182
- data/bin/pdfextract +201 -218
- data/bin/pdfmetadata +83 -82
- data/bin/pdfsh +4 -5
- data/bin/pdfwalker +1 -2
- data/bin/shell/.irbrc +45 -82
- data/bin/shell/console.rb +105 -130
- data/bin/shell/hexdump.rb +40 -64
- data/examples/README.md +34 -0
- data/examples/attachments/attachment.rb +38 -0
- data/examples/attachments/nested_document.rb +51 -0
- data/examples/encryption/encryption.rb +28 -0
- data/{samples/actions/triggerevents/trigger.rb → examples/events/events.rb} +13 -16
- data/examples/flash/flash.rb +37 -0
- data/{samples → examples}/flash/helloworld.swf +0 -0
- data/examples/forms/javascript.rb +54 -0
- data/examples/forms/xfa.rb +115 -0
- data/examples/javascript/hello_world.rb +22 -0
- data/examples/javascript/js_emulation.rb +54 -0
- data/examples/loop/goto.rb +32 -0
- data/examples/loop/named.rb +33 -0
- data/examples/signature/signature.rb +65 -0
- data/examples/uri/javascript.rb +56 -0
- data/examples/uri/open-uri.rb +21 -0
- data/examples/uri/submitform.rb +47 -0
- data/lib/origami.rb +29 -42
- data/lib/origami/3d.rb +350 -225
- data/lib/origami/acroform.rb +262 -288
- data/lib/origami/actions.rb +268 -288
- data/lib/origami/annotations.rb +697 -722
- data/lib/origami/array.rb +258 -184
- data/lib/origami/boolean.rb +74 -84
- data/lib/origami/catalog.rb +397 -434
- data/lib/origami/collections.rb +144 -0
- data/lib/origami/destinations.rb +233 -194
- data/lib/origami/dictionary.rb +253 -232
- data/lib/origami/encryption.rb +1274 -1243
- data/lib/origami/export.rb +232 -268
- data/lib/origami/extensions/fdf.rb +307 -220
- data/lib/origami/extensions/ppklite.rb +368 -435
- data/lib/origami/filespec.rb +197 -0
- data/lib/origami/filters.rb +301 -295
- data/lib/origami/filters/ascii.rb +177 -180
- data/lib/origami/filters/ccitt.rb +528 -535
- data/lib/origami/filters/crypt.rb +26 -35
- data/lib/origami/filters/dct.rb +46 -52
- data/lib/origami/filters/flate.rb +95 -94
- data/lib/origami/filters/jbig2.rb +49 -55
- data/lib/origami/filters/jpx.rb +38 -44
- data/lib/origami/filters/lzw.rb +189 -183
- data/lib/origami/filters/predictors.rb +221 -235
- data/lib/origami/filters/runlength.rb +103 -104
- data/lib/origami/font.rb +173 -186
- data/lib/origami/functions.rb +67 -81
- data/lib/origami/graphics.rb +25 -21
- data/lib/origami/graphics/colors.rb +178 -187
- data/lib/origami/graphics/instruction.rb +79 -85
- data/lib/origami/graphics/path.rb +142 -148
- data/lib/origami/graphics/patterns.rb +160 -167
- data/lib/origami/graphics/render.rb +43 -50
- data/lib/origami/graphics/state.rb +138 -153
- data/lib/origami/graphics/text.rb +188 -205
- data/lib/origami/graphics/xobject.rb +819 -815
- data/lib/origami/header.rb +63 -78
- data/lib/origami/javascript.rb +596 -597
- data/lib/origami/linearization.rb +285 -290
- data/lib/origami/metadata.rb +139 -148
- data/lib/origami/name.rb +112 -148
- data/lib/origami/null.rb +53 -62
- data/lib/origami/numeric.rb +162 -175
- data/lib/origami/obfuscation.rb +186 -174
- data/lib/origami/object.rb +593 -573
- data/lib/origami/outline.rb +42 -47
- data/lib/origami/outputintents.rb +73 -82
- data/lib/origami/page.rb +703 -592
- data/lib/origami/parser.rb +238 -290
- data/lib/origami/parsers/fdf.rb +41 -33
- data/lib/origami/parsers/pdf.rb +75 -95
- data/lib/origami/parsers/pdf/lazy.rb +137 -0
- data/lib/origami/parsers/pdf/linear.rb +64 -66
- data/lib/origami/parsers/ppklite.rb +34 -70
- data/lib/origami/pdf.rb +1030 -1005
- data/lib/origami/reference.rb +102 -102
- data/lib/origami/signature.rb +591 -609
- data/lib/origami/stream.rb +668 -551
- data/lib/origami/string.rb +397 -373
- data/lib/origami/template/patterns.rb +56 -0
- data/lib/origami/template/widgets.rb +151 -0
- data/lib/origami/trailer.rb +144 -158
- data/lib/origami/tree.rb +62 -0
- data/lib/origami/version.rb +23 -0
- data/lib/origami/webcapture.rb +88 -79
- data/lib/origami/xfa.rb +2863 -2882
- data/lib/origami/xreftable.rb +472 -384
- data/test/dataset/calc.pdf +85 -0
- data/test/dataset/crypto.pdf +82 -0
- data/test/dataset/empty.pdf +49 -0
- data/test/test_actions.rb +27 -0
- data/test/test_annotations.rb +90 -0
- data/test/test_pages.rb +31 -0
- data/test/test_pdf.rb +16 -0
- data/test/test_pdf_attachment.rb +34 -0
- data/test/test_pdf_create.rb +24 -0
- data/test/test_pdf_encrypt.rb +95 -0
- data/test/test_pdf_parse.rb +96 -0
- data/test/test_pdf_sign.rb +58 -0
- data/test/test_streams.rb +182 -0
- data/test/test_xrefs.rb +67 -0
- metadata +88 -58
- data/README +0 -67
- data/bin/pdf2graph +0 -121
- data/bin/pdfcocoon +0 -104
- data/lib/origami/file.rb +0 -233
- data/samples/README.txt +0 -45
- data/samples/actions/launch/calc.rb +0 -87
- data/samples/actions/launch/winparams.rb +0 -22
- data/samples/actions/loop/loopgoto.rb +0 -24
- data/samples/actions/loop/loopnamed.rb +0 -21
- data/samples/actions/named/named.rb +0 -31
- data/samples/actions/samba/smbrelay.rb +0 -26
- data/samples/actions/webbug/submitform.js +0 -26
- data/samples/actions/webbug/webbug-browser.rb +0 -68
- data/samples/actions/webbug/webbug-js.rb +0 -67
- data/samples/actions/webbug/webbug-reader.rb +0 -90
- data/samples/attachments/attach.rb +0 -40
- data/samples/attachments/attached.txt +0 -1
- data/samples/crypto/crypto.rb +0 -28
- data/samples/digsig/signed.rb +0 -46
- data/samples/exploits/cve-2008-2992-utilprintf.rb +0 -87
- data/samples/exploits/cve-2009-0927-geticon.rb +0 -65
- data/samples/exploits/exploit_customdictopen.rb +0 -55
- data/samples/exploits/getannots.rb +0 -69
- data/samples/flash/flash.rb +0 -31
- data/samples/javascript/attached.txt +0 -1
- data/samples/javascript/js.rb +0 -52
- data/templates/patterns.rb +0 -66
- data/templates/widgets.rb +0 -173
- data/templates/xdp.rb +0 -92
- data/test/ts_pdf.rb +0 -50
data/lib/origami/dictionary.rb
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
1
|
=begin
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
GNU Lesser General Public License for more details.
|
|
19
|
-
|
|
20
|
-
You should have received a copy of the GNU Lesser General Public License
|
|
21
|
-
along with Origami. If not, see <http://www.gnu.org/licenses/>.
|
|
2
|
+
|
|
3
|
+
This file is part of Origami, PDF manipulation framework for Ruby
|
|
4
|
+
Copyright (C) 2016 Guillaume Delugré.
|
|
5
|
+
|
|
6
|
+
Origami is free software: you can redistribute it and/or modify
|
|
7
|
+
it under the terms of the GNU Lesser General Public License as published by
|
|
8
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
(at your option) any later version.
|
|
10
|
+
|
|
11
|
+
Origami is distributed in the hope that it will be useful,
|
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
GNU Lesser General Public License for more details.
|
|
15
|
+
|
|
16
|
+
You should have received a copy of the GNU Lesser General Public License
|
|
17
|
+
along with Origami. If not, see <http://www.gnu.org/licenses/>.
|
|
22
18
|
|
|
23
19
|
=end
|
|
24
20
|
|
|
@@ -26,247 +22,272 @@ module Origami
|
|
|
26
22
|
|
|
27
23
|
class InvalidDictionaryObjectError < InvalidObjectError #:nodoc:
|
|
28
24
|
end
|
|
29
|
-
|
|
25
|
+
|
|
30
26
|
#
|
|
31
27
|
# Class representing a Dictionary Object.
|
|
32
28
|
# Dictionaries are containers associating a Name to an embedded Object.
|
|
33
29
|
#
|
|
34
30
|
class Dictionary < Hash
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
val.strings_cache.clear
|
|
71
|
-
val.names_cache.clear
|
|
72
|
-
val.xref_cache.clear
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
self[k.to_o] = val unless k.nil?
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def self.parse(stream, parser = nil) #:nodoc:
|
|
80
|
-
|
|
81
|
-
offset = stream.pos
|
|
82
|
-
|
|
83
|
-
if stream.skip(@@regexp_open).nil?
|
|
84
|
-
raise InvalidDictionaryObjectError, "No token '#{TOKENS.first}' found"
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
pairs = {}
|
|
88
|
-
while stream.skip(@@regexp_close).nil? do
|
|
89
|
-
key = Name.parse(stream, parser)
|
|
90
|
-
|
|
91
|
-
type = Object.typeof(stream)
|
|
92
|
-
if type.nil?
|
|
93
|
-
raise InvalidDictionaryObjectError, "Invalid object for field #{key.to_s}"
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
value = type.parse(stream, parser)
|
|
97
|
-
pairs[key] = value
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
dict =
|
|
101
|
-
if Origami::OPTIONS[:enable_type_guessing]
|
|
102
|
-
guessed_type = self.guess_type(pairs)
|
|
103
|
-
|
|
104
|
-
if Origami::OPTIONS[:enable_type_propagation]
|
|
105
|
-
guessed_type.new(
|
|
106
|
-
Hash[
|
|
107
|
-
pairs.map {|key, value|
|
|
108
|
-
hint_type = guessed_type.hint_type(key.value)
|
|
109
|
-
if hint_type.is_a?(::Array) and not value.is_a?(Reference) # Choose best match
|
|
110
|
-
hint_type.find {|type| type.native_type == value.native_type}
|
|
31
|
+
include Origami::Object
|
|
32
|
+
using TypeConversion
|
|
33
|
+
|
|
34
|
+
TOKENS = %w{ << >> } #:nodoc:
|
|
35
|
+
@@regexp_open = Regexp.new(WHITESPACES + TOKENS.first + WHITESPACES)
|
|
36
|
+
@@regexp_close = Regexp.new(WHITESPACES + TOKENS.last + WHITESPACES)
|
|
37
|
+
|
|
38
|
+
@@cast_fingerprints = {}
|
|
39
|
+
@@cast_keys = []
|
|
40
|
+
|
|
41
|
+
attr_reader :strings_cache, :names_cache, :xref_cache
|
|
42
|
+
|
|
43
|
+
#
|
|
44
|
+
# Creates a new Dictionary.
|
|
45
|
+
# _hash_:: The hash representing the new Dictionary.
|
|
46
|
+
#
|
|
47
|
+
def initialize(hash = {}, parser = nil)
|
|
48
|
+
raise TypeError, "Expected type Hash, received #{hash.class}." unless hash.is_a?(Hash)
|
|
49
|
+
super()
|
|
50
|
+
|
|
51
|
+
@strings_cache = []
|
|
52
|
+
@names_cache = []
|
|
53
|
+
@xref_cache = {}
|
|
54
|
+
|
|
55
|
+
hash.each_pair do |k,v|
|
|
56
|
+
next if k.nil?
|
|
57
|
+
|
|
58
|
+
# Turns the values into Objects.
|
|
59
|
+
key, value = k.to_o, v.to_o
|
|
60
|
+
|
|
61
|
+
if Origami::OPTIONS[:enable_type_guessing]
|
|
62
|
+
hint_type = guess_value_type(key, value)
|
|
63
|
+
|
|
64
|
+
if hint_type.is_a?(Class) and hint_type < value.class
|
|
65
|
+
value = value.cast_to(hint_type, parser)
|
|
111
66
|
end
|
|
112
67
|
|
|
113
|
-
if hint_type
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
[key, value]
|
|
118
|
-
else
|
|
119
|
-
[key, value]
|
|
68
|
+
if hint_type and parser and Origami::OPTIONS[:enable_type_propagation]
|
|
69
|
+
if value.is_a?(Reference)
|
|
70
|
+
parser.defer_type_cast(value, hint_type)
|
|
71
|
+
end
|
|
120
72
|
end
|
|
121
|
-
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Cache keys and values for fast search.
|
|
76
|
+
cache_key(key)
|
|
77
|
+
cache_value(value)
|
|
78
|
+
|
|
79
|
+
self[key] = value
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def self.parse(stream, parser = nil) #:nodoc:
|
|
84
|
+
offset = stream.pos
|
|
85
|
+
|
|
86
|
+
if stream.skip(@@regexp_open).nil?
|
|
87
|
+
raise InvalidDictionaryObjectError, "No token '#{TOKENS.first}' found"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
hash = {}
|
|
91
|
+
while stream.skip(@@regexp_close).nil? do
|
|
92
|
+
key = Name.parse(stream, parser)
|
|
93
|
+
|
|
94
|
+
type = Object.typeof(stream)
|
|
95
|
+
raise InvalidDictionaryObjectError, "Invalid object for field #{key.to_s}" if type.nil?
|
|
96
|
+
|
|
97
|
+
value = type.parse(stream, parser)
|
|
98
|
+
hash[key] = value
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if Origami::OPTIONS[:enable_type_guessing] and not (@@cast_keys & hash.keys).empty?
|
|
102
|
+
dict_type = self.guess_type(hash)
|
|
122
103
|
else
|
|
123
|
-
|
|
104
|
+
dict_type = self
|
|
124
105
|
end
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
dict
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
alias to_h to_hash
|
|
135
|
-
|
|
136
|
-
def to_s(indent = 1) #:nodoc:
|
|
137
|
-
if indent > 0
|
|
138
|
-
content = TOKENS.first + EOL
|
|
139
|
-
self.each_pair do |key,value|
|
|
140
|
-
content << "\t" * indent + key.to_s + " " + (value.is_a?(Dictionary) ? value.to_s(indent + 1) : value.to_s) + EOL
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
content << "\t" * (indent - 1) + TOKENS.last
|
|
144
|
-
else
|
|
145
|
-
content = TOKENS.first.dup
|
|
146
|
-
self.each_pair do |key,value|
|
|
147
|
-
content << "#{key.to_s} #{value.is_a?(Dictionary) ? value.to_s(0) : value.to_s}"
|
|
148
|
-
end
|
|
149
|
-
content << TOKENS.last
|
|
106
|
+
|
|
107
|
+
# Creates the Dictionary.
|
|
108
|
+
dict = dict_type.new(hash, parser)
|
|
109
|
+
|
|
110
|
+
dict.file_offset = offset
|
|
111
|
+
dict
|
|
150
112
|
end
|
|
151
113
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
114
|
+
def to_s(indent: 1, tab: "\t") #:nodoc:
|
|
115
|
+
if indent > 0
|
|
116
|
+
content = TOKENS.first + EOL
|
|
117
|
+
self.each_pair do |key,value|
|
|
118
|
+
content << tab * indent << key.to_s << ' '
|
|
119
|
+
content << (value.is_a?(Dictionary) ? value.to_s(indent: indent+1) : value.to_s)
|
|
120
|
+
content << EOL
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
content << tab * (indent - 1) << TOKENS.last
|
|
124
|
+
else
|
|
125
|
+
content = TOKENS.first.dup
|
|
126
|
+
self.each_pair do |key,value|
|
|
127
|
+
content << "#{key.to_s} #{value.is_a?(Dictionary) ? value.to_s(indent: 0) : value.to_s}"
|
|
128
|
+
end
|
|
129
|
+
content << TOKENS.last
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
super(content)
|
|
158
133
|
end
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
def []=(key,val)
|
|
166
|
-
unless key.is_a?(Symbol) or key.is_a?(Name)
|
|
167
|
-
fail "Expecting a Name for a Dictionary entry, found #{key.class} instead."
|
|
134
|
+
|
|
135
|
+
def map!(&b)
|
|
136
|
+
self.each_pair do |k,v|
|
|
137
|
+
self[k] = b.call(v)
|
|
138
|
+
end
|
|
168
139
|
end
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
val = val.to_o
|
|
173
|
-
super(key,val)
|
|
174
|
-
|
|
175
|
-
key.parent = self
|
|
176
|
-
val.parent = self unless val.is_indirect? or val.parent.equal?(self)
|
|
177
|
-
|
|
178
|
-
val
|
|
179
|
-
else
|
|
180
|
-
delete(key)
|
|
140
|
+
|
|
141
|
+
def merge(dict)
|
|
142
|
+
Dictionary.new(super(dict))
|
|
181
143
|
end
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
cast.no, cast.generation = self.no, self.generation
|
|
202
|
-
if self.is_indirect?
|
|
203
|
-
cast.set_indirect(true)
|
|
204
|
-
cast.set_pdf(self.pdf)
|
|
205
|
-
cast.file_offset = self.file_offset # cast can replace self
|
|
144
|
+
|
|
145
|
+
def []=(key,val)
|
|
146
|
+
unless key.is_a?(Symbol) or key.is_a?(Name)
|
|
147
|
+
fail "Expecting a Name for a Dictionary entry, found #{key.class} instead."
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
key = key.to_o
|
|
151
|
+
if val.nil?
|
|
152
|
+
delete(key)
|
|
153
|
+
return
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
val = val.to_o
|
|
157
|
+
super(key,val)
|
|
158
|
+
|
|
159
|
+
key.parent = self
|
|
160
|
+
val.parent = self unless val.indirect? or val.parent.equal?(self)
|
|
161
|
+
|
|
162
|
+
val
|
|
206
163
|
end
|
|
207
164
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
165
|
+
def [](key)
|
|
166
|
+
super(key.to_o)
|
|
167
|
+
end
|
|
211
168
|
|
|
212
|
-
|
|
213
|
-
|
|
169
|
+
def key?(key)
|
|
170
|
+
super(key.to_o)
|
|
171
|
+
end
|
|
172
|
+
alias include? key?
|
|
173
|
+
alias has_key? key?
|
|
214
174
|
|
|
215
|
-
|
|
175
|
+
def delete(key)
|
|
176
|
+
super(key.to_o)
|
|
177
|
+
end
|
|
216
178
|
|
|
217
|
-
|
|
179
|
+
def cast_to(type, parser = nil)
|
|
180
|
+
super(type)
|
|
218
181
|
|
|
219
|
-
|
|
220
|
-
|
|
182
|
+
cast = type.new(self, parser)
|
|
183
|
+
cast.parent = self.parent
|
|
184
|
+
cast.no, cast.generation = self.no, self.generation
|
|
185
|
+
if self.indirect?
|
|
186
|
+
cast.set_indirect(true)
|
|
187
|
+
cast.set_document(self.document)
|
|
188
|
+
cast.file_offset = self.file_offset # cast can replace self
|
|
189
|
+
end
|
|
221
190
|
|
|
222
|
-
|
|
223
|
-
self[field.to_s[0..-2].to_sym] = args.first
|
|
224
|
-
else
|
|
225
|
-
obj = self[field];
|
|
226
|
-
obj.is_a?(Reference) ? obj.solve : obj
|
|
191
|
+
cast
|
|
227
192
|
end
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
copy[k] = v.copy
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
copy.parent = @parent
|
|
237
|
-
copy.no, copy.generation = @no, @generation
|
|
238
|
-
copy.set_indirect(true) if is_indirect?
|
|
239
|
-
copy.set_pdf(@pdf) if is_indirect?
|
|
240
|
-
copy
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
def self.native_type; Dictionary end
|
|
244
|
-
|
|
245
|
-
def self.add_type_info(typeclass, key, value) #:nodoc:
|
|
246
|
-
if not @@cast_fingerprints.has_key?(typeclass) and typeclass.superclass != Dictionary and
|
|
247
|
-
@@cast_fingerprints.has_key?(typeclass.superclass)
|
|
248
|
-
@@cast_fingerprints[typeclass] = @@cast_fingerprints[typeclass.superclass].dup
|
|
193
|
+
|
|
194
|
+
alias each each_value
|
|
195
|
+
|
|
196
|
+
def to_h
|
|
197
|
+
Hash[self.to_a.map!{|k, v| [ k.value, v.value ]}]
|
|
249
198
|
end
|
|
199
|
+
alias value to_h
|
|
250
200
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
end
|
|
201
|
+
def method_missing(field, *args) #:nodoc:
|
|
202
|
+
raise NoMethodError, "No method `#{field}' for #{self.class}" unless field.to_s[0,1] =~ /[A-Z]/
|
|
254
203
|
|
|
255
|
-
|
|
256
|
-
|
|
204
|
+
if field.to_s[-1,1] == '='
|
|
205
|
+
self[field.to_s[0..-2].to_sym] = args.first
|
|
206
|
+
else
|
|
207
|
+
obj = self[field];
|
|
208
|
+
obj.is_a?(Reference) ? obj.solve : obj
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def copy
|
|
213
|
+
copy = self.class.new
|
|
214
|
+
self.each_pair do |k,v|
|
|
215
|
+
copy[k] = v.copy
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
copy.parent = @parent
|
|
219
|
+
copy.no, copy.generation = @no, @generation
|
|
220
|
+
copy.set_indirect(true) if self.indirect?
|
|
221
|
+
copy.set_document(@document) if self.indirect?
|
|
257
222
|
|
|
258
|
-
|
|
259
|
-
best_type = typeclass if keys.all? { |k,v|
|
|
260
|
-
hash.has_key?(k) and hash[k] == v
|
|
261
|
-
} and typeclass < best_type
|
|
223
|
+
copy
|
|
262
224
|
end
|
|
263
225
|
|
|
264
|
-
|
|
265
|
-
|
|
226
|
+
def self.native_type; Dictionary end
|
|
227
|
+
|
|
228
|
+
def self.add_type_info(klass, key, value) #:nodoc:
|
|
229
|
+
raise TypeError, "Invalid class #{klass}" unless klass.is_a?(Class) and klass < Dictionary
|
|
230
|
+
|
|
231
|
+
key, value = key.to_o, value.to_o
|
|
232
|
+
|
|
233
|
+
# Inherit the superclass type information.
|
|
234
|
+
if not @@cast_fingerprints.key?(klass) and @@cast_fingerprints.key?(klass.superclass)
|
|
235
|
+
@@cast_fingerprints[klass] = @@cast_fingerprints[klass.superclass].dup
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
@@cast_fingerprints[klass] ||= {}
|
|
239
|
+
@@cast_fingerprints[klass][key] = value
|
|
266
240
|
|
|
267
|
-
|
|
241
|
+
@@cast_keys.push(key) unless @@cast_keys.include?(key)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def self.guess_type(hash) #:nodoc:
|
|
245
|
+
best_type = self
|
|
246
|
+
|
|
247
|
+
@@cast_fingerprints.each_pair do |klass, keys|
|
|
248
|
+
next unless klass < best_type
|
|
249
|
+
|
|
250
|
+
best_type = klass if keys.all? { |k,v| hash[k] == v }
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
best_type
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def self.hint_type(name); nil end #:nodoc:
|
|
257
|
+
|
|
258
|
+
private
|
|
259
|
+
|
|
260
|
+
def cache_key(key)
|
|
261
|
+
@names_cache.push(key)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def cache_value(value)
|
|
265
|
+
case value
|
|
266
|
+
when String then @strings_cache.push(value)
|
|
267
|
+
when Name then @names_cache.push(value)
|
|
268
|
+
when Reference then
|
|
269
|
+
(@xref_cache[value] ||= []).push(self)
|
|
270
|
+
when Dictionary, Array
|
|
271
|
+
@strings_cache.concat(value.strings_cache)
|
|
272
|
+
@names_cache.concat(value.names_cache)
|
|
273
|
+
@xref_cache.update(value.xref_cache) do |_ref, cache1, cache2|
|
|
274
|
+
cache1.concat(cache2)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
value.strings_cache.clear
|
|
278
|
+
value.names_cache.clear
|
|
279
|
+
value.xref_cache.clear
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def guess_value_type(key, value)
|
|
284
|
+
hint_type = self.class.hint_type(key.value)
|
|
285
|
+
if hint_type.is_a?(::Array) and not value.is_a?(Reference) # Choose best match
|
|
286
|
+
hint_type = hint_type.find {|type| type < value.class }
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
hint_type
|
|
290
|
+
end
|
|
291
|
+
end
|
|
268
292
|
|
|
269
|
-
end #class
|
|
270
|
-
|
|
271
293
|
end
|
|
272
|
-
# Origami
|