origamindee 3.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 (139) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +89 -0
  3. data/COPYING.LESSER +165 -0
  4. data/README.md +131 -0
  5. data/bin/config/pdfcop.conf.yml +236 -0
  6. data/bin/pdf2pdfa +87 -0
  7. data/bin/pdf2ruby +333 -0
  8. data/bin/pdfcop +476 -0
  9. data/bin/pdfdecompress +97 -0
  10. data/bin/pdfdecrypt +91 -0
  11. data/bin/pdfencrypt +113 -0
  12. data/bin/pdfexplode +223 -0
  13. data/bin/pdfextract +277 -0
  14. data/bin/pdfmetadata +143 -0
  15. data/bin/pdfsh +12 -0
  16. data/bin/shell/console.rb +128 -0
  17. data/bin/shell/hexdump.rb +59 -0
  18. data/bin/shell/irbrc +69 -0
  19. data/examples/README.md +34 -0
  20. data/examples/attachments/attachment.rb +38 -0
  21. data/examples/attachments/nested_document.rb +51 -0
  22. data/examples/encryption/encryption.rb +28 -0
  23. data/examples/events/events.rb +72 -0
  24. data/examples/flash/flash.rb +37 -0
  25. data/examples/flash/helloworld.swf +0 -0
  26. data/examples/forms/javascript.rb +54 -0
  27. data/examples/forms/xfa.rb +115 -0
  28. data/examples/javascript/hello_world.rb +22 -0
  29. data/examples/javascript/js_emulation.rb +54 -0
  30. data/examples/loop/goto.rb +32 -0
  31. data/examples/loop/named.rb +33 -0
  32. data/examples/signature/signature.rb +65 -0
  33. data/examples/uri/javascript.rb +56 -0
  34. data/examples/uri/open-uri.rb +21 -0
  35. data/examples/uri/submitform.rb +47 -0
  36. data/lib/origami/3d.rb +364 -0
  37. data/lib/origami/acroform.rb +321 -0
  38. data/lib/origami/actions.rb +318 -0
  39. data/lib/origami/annotations.rb +711 -0
  40. data/lib/origami/array.rb +242 -0
  41. data/lib/origami/boolean.rb +90 -0
  42. data/lib/origami/catalog.rb +418 -0
  43. data/lib/origami/collections.rb +144 -0
  44. data/lib/origami/compound.rb +161 -0
  45. data/lib/origami/destinations.rb +252 -0
  46. data/lib/origami/dictionary.rb +192 -0
  47. data/lib/origami/encryption.rb +1084 -0
  48. data/lib/origami/extensions/fdf.rb +347 -0
  49. data/lib/origami/extensions/ppklite.rb +422 -0
  50. data/lib/origami/filespec.rb +197 -0
  51. data/lib/origami/filters/ascii.rb +211 -0
  52. data/lib/origami/filters/ccitt/tables.rb +267 -0
  53. data/lib/origami/filters/ccitt.rb +357 -0
  54. data/lib/origami/filters/crypt.rb +38 -0
  55. data/lib/origami/filters/dct.rb +54 -0
  56. data/lib/origami/filters/flate.rb +69 -0
  57. data/lib/origami/filters/jbig2.rb +57 -0
  58. data/lib/origami/filters/jpx.rb +47 -0
  59. data/lib/origami/filters/lzw.rb +170 -0
  60. data/lib/origami/filters/predictors.rb +292 -0
  61. data/lib/origami/filters/runlength.rb +129 -0
  62. data/lib/origami/filters.rb +364 -0
  63. data/lib/origami/font.rb +196 -0
  64. data/lib/origami/functions.rb +79 -0
  65. data/lib/origami/graphics/colors.rb +230 -0
  66. data/lib/origami/graphics/instruction.rb +98 -0
  67. data/lib/origami/graphics/path.rb +182 -0
  68. data/lib/origami/graphics/patterns.rb +174 -0
  69. data/lib/origami/graphics/render.rb +62 -0
  70. data/lib/origami/graphics/state.rb +149 -0
  71. data/lib/origami/graphics/text.rb +225 -0
  72. data/lib/origami/graphics/xobject.rb +918 -0
  73. data/lib/origami/graphics.rb +38 -0
  74. data/lib/origami/header.rb +75 -0
  75. data/lib/origami/javascript.rb +713 -0
  76. data/lib/origami/linearization.rb +330 -0
  77. data/lib/origami/metadata.rb +172 -0
  78. data/lib/origami/name.rb +135 -0
  79. data/lib/origami/null.rb +65 -0
  80. data/lib/origami/numeric.rb +181 -0
  81. data/lib/origami/obfuscation.rb +245 -0
  82. data/lib/origami/object.rb +760 -0
  83. data/lib/origami/optionalcontent.rb +183 -0
  84. data/lib/origami/outline.rb +54 -0
  85. data/lib/origami/outputintents.rb +85 -0
  86. data/lib/origami/page.rb +722 -0
  87. data/lib/origami/parser.rb +269 -0
  88. data/lib/origami/parsers/fdf.rb +56 -0
  89. data/lib/origami/parsers/pdf/lazy.rb +176 -0
  90. data/lib/origami/parsers/pdf/linear.rb +122 -0
  91. data/lib/origami/parsers/pdf.rb +118 -0
  92. data/lib/origami/parsers/ppklite.rb +57 -0
  93. data/lib/origami/pdf.rb +1108 -0
  94. data/lib/origami/reference.rb +134 -0
  95. data/lib/origami/signature.rb +702 -0
  96. data/lib/origami/stream.rb +705 -0
  97. data/lib/origami/string.rb +444 -0
  98. data/lib/origami/template/patterns.rb +56 -0
  99. data/lib/origami/template/widgets.rb +151 -0
  100. data/lib/origami/trailer.rb +190 -0
  101. data/lib/origami/tree.rb +62 -0
  102. data/lib/origami/version.rb +23 -0
  103. data/lib/origami/webcapture.rb +100 -0
  104. data/lib/origami/xfa/config.rb +453 -0
  105. data/lib/origami/xfa/connectionset.rb +146 -0
  106. data/lib/origami/xfa/datasets.rb +49 -0
  107. data/lib/origami/xfa/localeset.rb +42 -0
  108. data/lib/origami/xfa/package.rb +59 -0
  109. data/lib/origami/xfa/pdf.rb +73 -0
  110. data/lib/origami/xfa/signature.rb +42 -0
  111. data/lib/origami/xfa/sourceset.rb +43 -0
  112. data/lib/origami/xfa/stylesheet.rb +44 -0
  113. data/lib/origami/xfa/template.rb +1691 -0
  114. data/lib/origami/xfa/xdc.rb +42 -0
  115. data/lib/origami/xfa/xfa.rb +146 -0
  116. data/lib/origami/xfa/xfdf.rb +43 -0
  117. data/lib/origami/xfa/xmpmeta.rb +43 -0
  118. data/lib/origami/xfa.rb +62 -0
  119. data/lib/origami/xreftable.rb +557 -0
  120. data/lib/origami.rb +47 -0
  121. data/test/dataset/calc.pdf +85 -0
  122. data/test/dataset/crypto.pdf +36 -0
  123. data/test/dataset/empty.pdf +49 -0
  124. data/test/test_actions.rb +27 -0
  125. data/test/test_annotations.rb +68 -0
  126. data/test/test_forms.rb +30 -0
  127. data/test/test_native_types.rb +83 -0
  128. data/test/test_object_tree.rb +33 -0
  129. data/test/test_pages.rb +60 -0
  130. data/test/test_pdf.rb +20 -0
  131. data/test/test_pdf_attachment.rb +34 -0
  132. data/test/test_pdf_create.rb +24 -0
  133. data/test/test_pdf_encrypt.rb +102 -0
  134. data/test/test_pdf_parse.rb +134 -0
  135. data/test/test_pdf_parse_lazy.rb +69 -0
  136. data/test/test_pdf_sign.rb +97 -0
  137. data/test/test_streams.rb +184 -0
  138. data/test/test_xrefs.rb +67 -0
  139. metadata +280 -0
@@ -0,0 +1,161 @@
1
+ =begin
2
+
3
+ This file is part of Origami, PDF manipulation framework for Ruby
4
+ Copyright (C) 2017 Guillaume Delugré.
5
+
6
+ Origami is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ Origami is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ =end
20
+
21
+ require 'set'
22
+
23
+ module Origami
24
+
25
+ #
26
+ # Module for maintaining internal caches of objects for fast lookup.
27
+ #
28
+ module ObjectCache
29
+ attr_reader :strings_cache, :names_cache, :xref_cache
30
+
31
+ def initialize(*args)
32
+ super(*args)
33
+
34
+ init_caches
35
+ end
36
+
37
+ def rebuild_caches
38
+ self.each do |*items|
39
+ items.each do |object|
40
+ object.rebuild_caches if object.is_a?(CompoundObject)
41
+ cache_object(object)
42
+ end
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def init_caches
49
+ @strings_cache = Set.new
50
+ @names_cache = Set.new
51
+ @xref_cache = {}
52
+ end
53
+
54
+ def cache_object(object)
55
+ case object
56
+ when String then cache_string(object)
57
+ when Name then cache_name(object)
58
+ when Reference then cache_reference(object)
59
+ when CompoundObject then cache_compound(object)
60
+ end
61
+
62
+ object
63
+ end
64
+
65
+ def cache_compound(object)
66
+ @strings_cache.merge(object.strings_cache)
67
+ @names_cache.merge(object.names_cache)
68
+ @xref_cache.update(object.xref_cache) do |_, cache1, cache2|
69
+ cache1.concat(cache2)
70
+ end
71
+
72
+ object.strings_cache.clear
73
+ object.names_cache.clear
74
+ object.xref_cache.clear
75
+ end
76
+
77
+ def cache_string(str)
78
+ @strings_cache.add(str)
79
+ end
80
+
81
+ def cache_name(name)
82
+ @names_cache.add(name)
83
+ end
84
+
85
+ def cache_reference(ref)
86
+ @xref_cache[ref] ||= []
87
+ @xref_cache[ref].push(self)
88
+ end
89
+ end
90
+
91
+ #
92
+ # Module for objects containing other objects.
93
+ #
94
+ module CompoundObject
95
+ include Origami::Object
96
+ include ObjectCache
97
+ using TypeConversion
98
+
99
+ #
100
+ # Returns true if the item is present in the compound object.
101
+ #
102
+ def include?(item)
103
+ super(item.to_o)
104
+ end
105
+
106
+ #
107
+ # Removes the item from the compound object if present.
108
+ #
109
+ def delete(item)
110
+ obj = super(item.to_o)
111
+ unlink_object(obj) unless obj.nil?
112
+ end
113
+
114
+ #
115
+ # Creates a deep copy of the compound object.
116
+ # This method can be quite expensive as nested objects are copied too.
117
+ #
118
+ def copy
119
+ obj = self.update_values(&:copy)
120
+
121
+ transfer_attributes(obj)
122
+ end
123
+
124
+ #
125
+ # Returns a new compound object with updated values based on the provided block.
126
+ #
127
+ def update_values(&b)
128
+ return enum_for(__method__) unless block_given?
129
+ return self.class.new self.transform_values(&b) if self.respond_to?(:transform_values)
130
+ return self.class.new self.map(&b) if self.respond_to?(:map)
131
+
132
+ raise NotImplementedError, "This object does not implement this method"
133
+ end
134
+
135
+ #
136
+ # Modifies the compound object's values based on the provided block.
137
+ #
138
+ def update_values!(&b)
139
+ return enum_for(__method__) unless block_given?
140
+ return self.transform_values!(&b) if self.respond_to?(:transform_values!)
141
+ return self.map!(&b) if self.respond_to?(:map!)
142
+
143
+ raise NotImplementedError, "This object does not implement this method"
144
+ end
145
+
146
+ private
147
+
148
+ def link_object(item)
149
+ obj = item.to_o
150
+ obj.parent = self unless obj.indirect?
151
+
152
+ cache_object(obj)
153
+ end
154
+
155
+ def unlink_object(obj)
156
+ obj.parent = nil
157
+ obj
158
+ end
159
+ end
160
+
161
+ end
@@ -0,0 +1,252 @@
1
+ =begin
2
+
3
+ This file is part of Origami, PDF manipulation framework for Ruby
4
+ Copyright (C) 2016 Guillaume Delugré.
5
+
6
+ Origami is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ Origami is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ =end
20
+
21
+ module Origami
22
+
23
+ class PDF
24
+ #
25
+ # Lookup destination in the destination name directory.
26
+ #
27
+ def get_destination_by_name(name)
28
+ resolve_name Names::DESTINATIONS, name
29
+ end
30
+
31
+ #
32
+ # Calls block for each named destination.
33
+ #
34
+ def each_named_dest(&b)
35
+ each_name(Names::DESTINATIONS, &b)
36
+ end
37
+ end
38
+
39
+ #
40
+ # A destination represents a specified location into the document.
41
+ #
42
+ class Destination < Origami::Array
43
+ attr_reader :page, :top, :left, :right, :bottom, :zoom
44
+
45
+ #
46
+ # Class representing a Destination zooming on a part of a document.
47
+ #
48
+ class Zoom < Destination
49
+
50
+ def initialize(array)
51
+ super(array)
52
+
53
+ @page, _, @left, @top, @zoom = array
54
+ end
55
+
56
+ #
57
+ # Creates a new zoom Destination.
58
+ # _page_:: The destination Page.
59
+ # _left_, _top_:: Coords in the Page.
60
+ # _zoom_:: Zoom factor.
61
+ #
62
+ def self.[](page, left: 0, top: 0, zoom: 0)
63
+ self.new([page, :XYZ, left, top, zoom])
64
+ end
65
+ end
66
+
67
+ def self.Zoom(page, left: 0, top: 0, zoom: 0)
68
+ Zoom[page, left: left, top: top, zoom: zoom]
69
+ end
70
+
71
+
72
+ #
73
+ # Class representing a Destination showing a Page globally.
74
+ #
75
+ class GlobalFit < Destination
76
+
77
+ def initialize(array)
78
+ super(array)
79
+
80
+ @page, _ = array
81
+ end
82
+
83
+ #
84
+ # Creates a new global fit Destination.
85
+ # _page_:: The destination Page.
86
+ #
87
+ def self.[](page)
88
+ self.new([page, :Fit])
89
+ end
90
+ end
91
+
92
+ def self.GlobalFit(page)
93
+ GlobalFit[page]
94
+ end
95
+
96
+ #
97
+ # Class representing a Destination fitting a Page horizontally.
98
+ #
99
+ class HorizontalFit < Destination
100
+
101
+ def initialize(array)
102
+ super(array)
103
+
104
+ @page, _, @top = array
105
+ end
106
+
107
+ #
108
+ # Creates a new horizontal fit destination.
109
+ # _page_:: The destination Page.
110
+ # _top_:: The vertical coord in the Page.
111
+ #
112
+ def self.[](page, top: 0)
113
+ self.new([page, :FitH, top])
114
+ end
115
+ end
116
+
117
+ def self.HorizontalFit(page, top: 0)
118
+ HorizontalFit[page, top: top]
119
+ end
120
+
121
+ #
122
+ # Class representing a Destination fitting a Page vertically.
123
+ # _page_:: The destination Page.
124
+ # _left_:: The horizontal coord in the Page.
125
+ #
126
+ class VerticalFit < Destination
127
+
128
+ def initialize(array)
129
+ super(array)
130
+
131
+ @page, _, @left = array
132
+ end
133
+
134
+ def self.[](page, left: 0)
135
+ self.new([page, :FitV, left])
136
+ end
137
+ end
138
+
139
+ def self.VerticalFit(page, left: 0)
140
+ VerticalFit[page, left: left]
141
+ end
142
+
143
+ #
144
+ # Class representing a Destination fitting the view on a rectangle in a Page.
145
+ #
146
+ class RectangleFit < Destination
147
+
148
+ def initialize(array)
149
+ super(array)
150
+
151
+ @page, _, @left, @bottom, @right, @top = array
152
+ end
153
+
154
+ #
155
+ # Creates a new rectangle fit Destination.
156
+ # _page_:: The destination Page.
157
+ # _left_, _bottom_, _right_, _top_:: The rectangle to fit in.
158
+ #
159
+ def self.[](page, left: 0, bottom: 0, right: 0, top: 0)
160
+ self.new([page, :FitR, left, bottom, right, top])
161
+ end
162
+ end
163
+
164
+ def self.RectangleFit(page, left: 0, bottom: 0, right: 0, top: 0)
165
+ RectangleFit[page, left: left, bottom: bottom, right: right, top: top]
166
+ end
167
+
168
+ #
169
+ # Class representing a Destination fitting the bounding box of a Page.
170
+ #
171
+ class GlobalBoundingBoxFit < Destination
172
+
173
+ def initialize(array)
174
+ super(array)
175
+
176
+ @page, _, = array
177
+ end
178
+
179
+ #
180
+ # Creates a new bounding box fit Destination.
181
+ # _page_:: The destination Page.
182
+ #
183
+ def self.[](page)
184
+ self.new([page, :FitB])
185
+ end
186
+ end
187
+
188
+ def self.GlobalBoundingBoxFit(page)
189
+ GlobalBoundingBoxFit[page]
190
+ end
191
+
192
+ #
193
+ # Class representing a Destination fitting horizontally the bouding box a Page.
194
+ #
195
+ class HorizontalBoudingBoxFit < Destination
196
+
197
+ def initialize(array)
198
+ super(array)
199
+
200
+ @page, _, @top = array
201
+ end
202
+
203
+ #
204
+ # Creates a new horizontal bounding box fit Destination.
205
+ # _page_:: The destination Page.
206
+ # _top_:: The vertical coord.
207
+ #
208
+ def self.[](page, top: 0)
209
+ self.new([page, :FitBH, top])
210
+ end
211
+ end
212
+
213
+ def self.HorizontalBoudingBoxFit(page, top: 0)
214
+ HorizontalBoudingBoxFit[page, top: top]
215
+ end
216
+
217
+ #
218
+ # Class representing a Destination fitting vertically the bounding box of a Page.
219
+ #
220
+ class VerticalBoundingBoxFit < Destination
221
+
222
+ def initialize(array)
223
+ super(array)
224
+
225
+ @page, _, @left = array
226
+ end
227
+
228
+ #
229
+ # Creates a new vertical bounding box fit Destination.
230
+ # _page_:: The destination Page.
231
+ # _left_:: The horizontal coord.
232
+ #
233
+ def self.[](page, left: 0)
234
+ self.new([page, :FitBV, left])
235
+ end
236
+ end
237
+
238
+ def self.VerticalBoundingBoxFit(page, left: 0)
239
+ VerticalBoundingBoxFit[page, left: left]
240
+ end
241
+ end
242
+
243
+ #
244
+ # This kind of Dictionary is used in named destinations.
245
+ #
246
+ class DestinationDictionary < Dictionary
247
+ include StandardObject
248
+
249
+ field :D, :Type => Destination, :Required => true
250
+ end
251
+
252
+ end
@@ -0,0 +1,192 @@
1
+ =begin
2
+
3
+ This file is part of Origami, PDF manipulation framework for Ruby
4
+ Copyright (C) 2016 Guillaume Delugré.
5
+
6
+ Origami is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ Origami is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ =end
20
+
21
+ module Origami
22
+
23
+ class InvalidDictionaryObjectError < InvalidObjectError #:nodoc:
24
+ end
25
+
26
+ #
27
+ # Class representing a Dictionary Object.
28
+ # Dictionaries are containers associating a Name to an embedded Object.
29
+ #
30
+ class Dictionary < Hash
31
+ include CompoundObject
32
+ include FieldAccessor
33
+ using TypeConversion
34
+ extend TypeGuessing
35
+
36
+ TOKENS = %w{ << >> } #:nodoc:
37
+ @@regexp_open = Regexp.new(WHITESPACES + TOKENS.first + WHITESPACES)
38
+ @@regexp_close = Regexp.new(WHITESPACES + TOKENS.last + WHITESPACES)
39
+
40
+ #
41
+ # Creates a new Dictionary.
42
+ # _hash_:: The hash representing the new Dictionary.
43
+ #
44
+ def initialize(hash = {}, parser = nil)
45
+ raise TypeError, "Expected type Hash, received #{hash.class}." unless hash.is_a?(Hash)
46
+ super()
47
+
48
+ hash.each_pair do |k,v|
49
+ next if k.nil?
50
+
51
+ # Turns the values into Objects.
52
+ key, value = k.to_o, v.to_o
53
+
54
+ if Origami::OPTIONS[:enable_type_guessing]
55
+ hint_type = guess_value_type(key, value)
56
+
57
+ if hint_type.is_a?(Class) and hint_type < value.class
58
+ value = value.cast_to(hint_type, parser)
59
+ end
60
+
61
+ if hint_type and parser and Origami::OPTIONS[:enable_type_propagation]
62
+ if value.is_a?(Reference)
63
+ parser.defer_type_cast(value, hint_type)
64
+ end
65
+ end
66
+ end
67
+
68
+ self[key] = value
69
+ end
70
+ end
71
+
72
+ def self.parse(stream, parser = nil) #:nodoc:
73
+ scanner = Parser.init_scanner(stream)
74
+ offset = scanner.pos
75
+
76
+ if scanner.skip(@@regexp_open).nil?
77
+ raise InvalidDictionaryObjectError, "No token '#{TOKENS.first}' found"
78
+ end
79
+
80
+ hash = {}
81
+ while scanner.skip(@@regexp_close).nil? do
82
+ key = Name.parse(scanner, parser)
83
+
84
+ type = Object.typeof(scanner)
85
+ raise InvalidDictionaryObjectError, "Invalid object for field #{key}" if type.nil?
86
+
87
+ value = type.parse(scanner, parser)
88
+ hash[key] = value
89
+ end
90
+
91
+ if Origami::OPTIONS[:enable_type_guessing]
92
+ dict_type = self.guess_type(hash)
93
+ else
94
+ dict_type = self
95
+ end
96
+
97
+ # Creates the Dictionary.
98
+ dict = dict_type.new(hash, parser)
99
+
100
+ dict.file_offset = offset
101
+ dict
102
+ end
103
+
104
+ def to_s(indent: 1, tab: "\t", eol: $/) #:nodoc:
105
+ nl = eol
106
+ tab, nl = '', '' if indent == 0
107
+
108
+ content = TOKENS.first + nl
109
+ self.each_pair do |key,value|
110
+ content << "#{tab * indent}#{key} "
111
+
112
+ content <<
113
+ if value.is_a?(Dictionary)
114
+ value.to_s(eol: eol, indent: (indent == 0) ? 0 : indent + 1)
115
+ else
116
+ value.to_s(eol: eol)
117
+ end
118
+
119
+ content << nl
120
+ end
121
+
122
+ content << tab * (indent - 1) if indent > 0
123
+ content << TOKENS.last
124
+
125
+ super(content, eol: eol)
126
+ end
127
+
128
+ #
129
+ # Returns a new Dictionary object with values modified by given block.
130
+ #
131
+ def transform_values(&b)
132
+ self.class.new self.map { |k, v|
133
+ [ k.to_sym, b.call(v) ]
134
+ }.to_h
135
+ end
136
+
137
+ #
138
+ # Modifies the values of the Dictionary, leaving keys unchanged.
139
+ #
140
+ def transform_values!(&b)
141
+ self.each_pair do |k, v|
142
+ self[k] = b.call(unlink_object(v))
143
+ end
144
+ end
145
+
146
+ #
147
+ # Merges the content of the Dictionary with another Dictionary.
148
+ #
149
+ def merge(dict)
150
+ self.class.new(super(dict))
151
+ end
152
+
153
+ def []=(key,val)
154
+ unless key.is_a?(Symbol) or key.is_a?(Name)
155
+ raise TypeError, "Expecting a Name for a Dictionary entry, found #{key.class} instead."
156
+ end
157
+
158
+ if val.nil?
159
+ self.delete(key)
160
+ return
161
+ end
162
+
163
+ super(link_object(key), link_object(val))
164
+ end
165
+
166
+ def [](key)
167
+ super(key.to_o)
168
+ end
169
+
170
+ alias key? include?
171
+ alias has_key? key?
172
+
173
+ def to_h
174
+ Hash[self.to_a.map!{|k, v| [ k.value, v.value ]}]
175
+ end
176
+ alias value to_h
177
+
178
+ def self.hint_type(_name); nil end #:nodoc:
179
+
180
+ private
181
+
182
+ def guess_value_type(key, value)
183
+ hint_type = self.class.hint_type(key.value)
184
+ if hint_type.is_a?(::Array) and not value.is_a?(Reference) # Choose best match
185
+ hint_type = hint_type.find {|type| type < value.class }
186
+ end
187
+
188
+ hint_type
189
+ end
190
+ end
191
+
192
+ end