combine_pdf 0.2.21 → 0.2.27

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,223 +5,382 @@
5
5
  ## is subject to the same license.
6
6
  ########################################################
7
7
 
8
-
9
-
10
-
11
-
12
-
13
8
  module CombinePDF
14
-
15
-
16
- class PDF
17
-
18
- protected
19
-
20
- include Renderer
21
-
22
- # @private
23
- # Some PDF objects contain references to other PDF objects.
24
- #
25
- # this function adds the references contained in "object", but DOESN'T add the object itself.
26
- #
27
- # this is used for internal operations, such as injectng data using the << operator.
28
- def add_referenced(object, dup_pages = true)
29
- # add references but not root
30
- case
31
- when object.is_a?(Array)
32
- object.each {|it| add_referenced(it, dup_pages)}
33
- return true
34
- when object.is_a?(Hash)
35
- # first if statement is actually a workaround for a bug in Acrobat Reader, regarding duplicate pages.
36
- if dup_pages && object[:is_reference_only] && object[:referenced_object] && object[:referenced_object].is_a?(Hash) && object[:referenced_object][:Type] == :Page
37
- if @objects.find_index object[:referenced_object]
38
- @objects << (object[:referenced_object] = object[:referenced_object].dup)
39
- else
40
- @objects << object[:referenced_object]
41
- end
42
- elsif object[:is_reference_only] && object[:referenced_object]
43
- found_at = @objects.find_index object[:referenced_object]
44
- if found_at
45
- #if the objects are equal, they might still be different objects!
46
- # so, we need to make sure they are the same object for the pointers to effect id numbering
47
- # and formatting operations.
48
- object[:referenced_object] = @objects[found_at]
49
- # stop this path, there is no need to run over the Hash's keys and values
50
- return true
51
- else
52
- # stop if page propegation is false
53
- return true if !dup_pages && object[:referenced_object][:Type] == :Page
54
- # @objects.include? object[:referenced_object] is bound to be false
55
- # the object wasn't found - add it to the @objects array
56
- @objects << object[:referenced_object]
57
- end
58
-
59
- end
60
- object.each do |k, v|
61
- add_referenced(v, dup_pages) unless k == :Parent
62
- end
63
- else
64
- return false
65
- end
66
- true
67
- end
68
-
69
- # @private
70
- def rebuild_catalog(*with_pages)
71
- # # build page list v.1 Slow but WORKS
72
- # # Benchmark testing value: 26.708394
73
- # old_catalogs = @objects.select {|obj| obj.is_a?(Hash) && obj[:Type] == :Catalog}
74
- # old_catalogs ||= []
75
- # page_list = []
76
- # PDFOperations._each_object(old_catalogs,false) { |p| page_list << p if p.is_a?(Hash) && p[:Type] == :Page }
77
-
78
- # build page list v.2 faster, better, and works
79
- # Benchmark testing value: 0.215114
80
- page_list = pages
81
-
82
- # add pages to catalog, if requested
83
- page_list.push(*with_pages) unless with_pages.empty?
84
-
85
- # build new Pages object
86
- pages_object = {Type: :Pages, Count: page_list.length, Kids: page_list.map {|p| {referenced_object: p, is_reference_only: true} } }
87
-
88
- # rebuild/rename the names dictionary
89
- rebuild_names
90
- # build new Catalog object
91
- catalog_object = {Type: :Catalog, Pages: {referenced_object: pages_object, is_reference_only: true}, Names: {referenced_object: @names, is_reference_only: true}}
92
- catalog_object[:ViewerPreferences] = @viewer_preferences unless @viewer_preferences.empty?
93
-
94
- # rebuild/rename the forms dictionary
95
- if @forms_data.nil? || @forms_data.empty?
96
- @forms_data = nil
97
- else
98
- @forms_data = {referenced_object: actual_value(@forms_data), is_reference_only: true}
99
- catalog_object[:AcroForm] = @forms_data
100
- end
101
-
102
-
103
- # point old Pages pointers to new Pages object
104
- ## first point known pages objects - enough?
105
- pages.each {|p| p[:Parent] = { referenced_object: pages_object, is_reference_only: true} }
106
- ## or should we, go over structure? (fails)
107
- # each_object {|obj| obj[:Parent][:referenced_object] = pages_object if obj.is_a?(Hash) && obj[:Parent].is_a?(Hash) && obj[:Parent][:referenced_object] && obj[:Parent][:referenced_object][:Type] == :Pages}
108
-
109
- # remove old catalog and pages objects
110
- @objects.reject! {|obj| obj.is_a?(Hash) && (obj[:Type] == :Catalog || obj[:Type] == :Pages) }
111
-
112
- # inject new catalog and pages objects
113
- @objects << pages_object
114
- @objects << catalog_object
115
-
116
- catalog_object
117
- end
118
-
119
- def names_object
120
- @names
121
- end
122
- def forms_data
123
- @forms_data
124
- end
125
-
126
- # @private
127
- # this is an alternative to the rebuild_catalog catalog method
128
- # this method is used by the to_pdf method, for streamlining the PDF output.
129
- # there is no point is calling the method before preparing the output.
130
- def rebuild_catalog_and_objects
131
- catalog = rebuild_catalog
132
- @objects.clear
133
- @objects << @info
134
- add_referenced @info
135
- @objects << catalog
136
- add_referenced catalog[:Pages]
137
- add_referenced catalog[:Names], false
138
- catalog
139
- end
140
-
141
- def get_existing_catalogs
142
- (@objects.select {|obj| obj.is_a?(Hash) && obj[:Type] == :Catalog}) || (@objects.select {|obj| obj.is_a?(Hash) && obj[:Type] == :Page})
143
- end
144
-
145
-
146
-
147
- # end
148
- # @private
149
- def renumber_object_ids(start = nil)
150
- @set_start_id = start || @set_start_id
151
- start = @set_start_id
152
- history = {}
153
- @objects.each do |obj|
154
- obj[:indirect_reference_id] = start
155
- start += 1
156
- end
157
- end
158
- def remove_old_ids
159
- @objects.each {|obj| obj.delete(:indirect_reference_id); obj.delete(:indirect_generation_number)}
160
- end
161
-
162
- def rebuild_names name_tree = nil, base = "CombinePDF_0000000"
163
- if name_tree
164
- dic = []
165
- case name_tree
166
- when Array
167
- if name_tree[0].is_a? String
168
- (name_tree.length/2).times do |i|
169
- dic << (name_tree[i*2].clear << base.next!)
170
- dic << name_tree[(i*2) + 1]
171
- end
172
- else
173
- name_tree.each {|kid| dic.concat rebuild_names(kid, base) }
174
- end
175
- when Hash
176
- if name_tree[:Kids]
177
- dic.concat rebuild_names(name_tree[:Kids], base)
178
- elsif name_tree[:Names]
179
- dic.concat rebuild_names(name_tree[:Names], base)
180
- elsif name_tree[:referenced_object]
181
- dic.concat rebuild_names(name_tree[:referenced_object], base)
182
- end
183
- end
184
- return dic
185
- end
186
- @names.keys.each do |k|
187
- @names[k] = {referenced_object: { Names: rebuild_names(@names[k], base) } , is_reference_only: true} unless k == :Type
188
- end
189
- end
190
-
191
- # @private
192
- # this method reviews a Hash an updates it by merging Hash data,
193
- # preffering the new over the old.
194
- def self.hash_merge_new_no_page key, old_data, new_data
195
- if old_data.is_a? Hash
196
- return old_data if old_data[:Type] == :Page
197
- old_data.merge( new_data, &( @hash_merge_new_no_page_proc ||= self.method(:hash_merge_new_no_page) ) )
198
- elsif old_data.is_a? Array
199
- old_data + new_data
200
- else
201
- new_data
202
- end
203
- end
204
-
205
-
206
- private
207
-
208
- def renaming_dictionary object = nil, dictionary = {}
209
- object ||= @names
210
- case object
211
- when Array
212
- object.length.times {|i| object[i].is_a?(String) ? (dictionary[object[i]] = (dictionary.last || "Random_0001").next) : renaming_dictionary(object[i], dictionary) }
213
- when Hash
214
- object.values.each {|v| renaming_dictionary v, dictionary }
215
- end
216
- end
217
-
218
- def rename_object object, dictionary
219
- case object
220
- when Array
221
- object.length.times {|i| }
222
- when Hash
223
- end
224
- end
225
-
226
- end
9
+ class PDF
10
+ protected
11
+
12
+ include Renderer
13
+
14
+ # RECORSIVE_PROTECTION = { Parent: true, Last: true}.freeze
15
+
16
+ # @private
17
+ # Some PDF objects contain references to other PDF objects.
18
+ #
19
+ # this function adds the references contained in `@objects`.
20
+ #
21
+ # this is used for internal operations, such as injectng data using the << operator.
22
+ def add_referenced
23
+ # add references but not root
24
+ should_resolve = @objects.dup
25
+ dup_pages = nil
26
+ resolved = [].to_set
27
+ while should_resolve.any?
28
+ obj = should_resolve.pop
29
+ if obj.is_a?(Hash)
30
+ next if resolved.include? obj.object_id
31
+ resolved << obj.object_id
32
+ if obj[:referenced_object]
33
+ tmp = @objects.find_index(obj[:referenced_object])
34
+ if tmp
35
+ tmp = @objects[tmp]
36
+ obj[:referenced_object] = tmp
37
+ else
38
+ tmp = obj[:referenced_object]
39
+ should_resolve << tmp
40
+ @objects << tmp
41
+ end
42
+ else
43
+ obj.keys.each { |k| should_resolve << obj[k] unless k == :Parent || resolved.include?(obj[k].object_id) || !obj[k].is_a?(Enumerable) }
44
+ end
45
+ elsif obj.is_a?(Array)
46
+ next if resolved.include? obj.object_id
47
+ resolved << obj.object_id
48
+ should_resolve.concat obj
49
+ end
50
+ end
51
+ resolved.clear
52
+ end
53
+
54
+ # # @private
55
+ # # Some PDF objects contain references to other PDF objects.
56
+ # #
57
+ # # this function adds the references contained in "object", but DOESN'T add the object itself.
58
+ # #
59
+ # # this is used for internal operations, such as injectng data using the << operator.
60
+ # def add_referenced(object, dup_pages = true)
61
+ # # add references but not root
62
+ # if object.is_a?(Array)
63
+ # object.each { |it| add_referenced(it, dup_pages) }
64
+ # return true
65
+ # elsif object.is_a?(Hash)
66
+ # # first if statement is actually a workaround for a bug in Acrobat Reader, regarding duplicate pages.
67
+ # if dup_pages && object[:is_reference_only] && object[:referenced_object] && object[:referenced_object].is_a?(Hash) && object[:referenced_object][:Type] == :Page
68
+ # if @objects.find_index object[:referenced_object]
69
+ # @objects << (object[:referenced_object] = object[:referenced_object].dup)
70
+ # else
71
+ # @objects << object[:referenced_object]
72
+ # end
73
+ # elsif object[:is_reference_only] && object[:referenced_object]
74
+ # found_at = @objects.find_index object[:referenced_object]
75
+ # if found_at
76
+ # # if the objects are equal, they might still be different objects!
77
+ # # so, we need to make sure they are the same object for the pointers to effect id numbering
78
+ # # and formatting operations.
79
+ # object[:referenced_object] = @objects[found_at]
80
+ # # stop this path, there is no need to run over the Hash's keys and values
81
+ # return true
82
+ # else
83
+ # # stop if page propegation is false
84
+ # return true if !dup_pages && object[:referenced_object][:Type] == :Page
85
+ # # @objects.include? object[:referenced_object] is bound to be false
86
+ # # the object wasn't found - add it to the @objects array
87
+ # @objects << object[:referenced_object]
88
+ # end
89
+ #
90
+ # end
91
+ # object.each do |k, v|
92
+ # add_referenced(v, dup_pages) unless RECORSIVE_PROTECTION[k]
93
+ # end
94
+ # else
95
+ # return false
96
+ # end
97
+ # true
98
+ # end
99
+
100
+ # @private
101
+ def rebuild_catalog(*with_pages)
102
+ # # build page list v.1 Slow but WORKS
103
+ # # Benchmark testing value: 26.708394
104
+ # old_catalogs = @objects.select {|obj| obj.is_a?(Hash) && obj[:Type] == :Catalog}
105
+ # old_catalogs ||= []
106
+ # page_list = []
107
+ # PDFOperations._each_object(old_catalogs,false) { |p| page_list << p if p.is_a?(Hash) && p[:Type] == :Page }
108
+
109
+ # build page list v.2 faster, better, and works
110
+ # Benchmark testing value: 0.215114
111
+ page_list = pages
112
+
113
+ # add pages to catalog, if requested
114
+ page_list.concat(with_pages) unless with_pages.empty?
115
+
116
+ # build new Pages object
117
+ pages_object = { Type: :Pages, Count: page_list.length, Kids: page_list.map { |p| { referenced_object: p, is_reference_only: true } } }
118
+
119
+ # rebuild/rename the names dictionary
120
+ rebuild_names
121
+ # build new Catalog object
122
+ catalog_object = { Type: :Catalog,
123
+ Pages: { referenced_object: pages_object, is_reference_only: true },
124
+ Names: { referenced_object: @names, is_reference_only: true },
125
+ Outlines: { referenced_object: @outlines, is_reference_only: true } }
126
+ catalog_object[:ViewerPreferences] = @viewer_preferences unless @viewer_preferences.empty?
127
+
128
+ # rebuild/rename the forms dictionary
129
+ if @forms_data.nil? || @forms_data.empty?
130
+ @forms_data = nil
131
+ else
132
+ @forms_data = { referenced_object: (@forms_data[:referenced_object] || @forms_data), is_reference_only: true }
133
+ catalog_object[:AcroForm] = @forms_data
134
+ end
135
+
136
+ # point old Pages pointers to new Pages object
137
+ ## first point known pages objects - enough?
138
+ pages.each { |p| p[:Parent] = { referenced_object: pages_object, is_reference_only: true } }
139
+ ## or should we, go over structure? (fails)
140
+ # each_object {|obj| obj[:Parent][:referenced_object] = pages_object if obj.is_a?(Hash) && obj[:Parent].is_a?(Hash) && obj[:Parent][:referenced_object] && obj[:Parent][:referenced_object][:Type] == :Pages}
141
+
142
+ # remove old catalog and pages objects
143
+ @objects.reject! { |obj| obj.is_a?(Hash) && (obj[:Type] == :Catalog || obj[:Type] == :Pages) }
144
+
145
+ # inject new catalog and pages objects
146
+ @objects << pages_object
147
+ @objects << catalog_object
148
+
149
+ catalog_object
150
+ end
151
+
152
+ def names_object
153
+ @names
154
+ end
155
+
156
+ def outlines_object
157
+ @outlines
158
+ end
159
+ # def forms_data
160
+ # @forms_data
161
+ # end
162
+
163
+ # @private
164
+ # this is an alternative to the rebuild_catalog catalog method
165
+ # this method is used by the to_pdf method, for streamlining the PDF output.
166
+ # there is no point is calling the method before preparing the output.
167
+ def rebuild_catalog_and_objects
168
+ catalog = rebuild_catalog
169
+ @objects.clear
170
+ @objects << @info
171
+ @objects << catalog
172
+ # fix Acrobat Reader issue with page reference uniqueness (must be unique or older Acrobat Reader fails)
173
+ catalog[:Pages][:referenced_object][:Kids].each do |page|
174
+ tmp = page[:referenced_object]
175
+ tmp = page[:referenced_object] = tmp.dup if @objects.include? tmp
176
+ @objects << tmp
177
+ end
178
+ # adds every referenced object to the @objects (root), addition is performed as pointers rather then copies
179
+ # puts (Benchmark.measure do
180
+ add_referenced
181
+ # end)
182
+ # @objects << @info
183
+ # add_referenced @info
184
+ # add_referenced catalog
185
+ # add_referenced catalog[:Pages]
186
+ # add_referenced catalog[:Names], false
187
+ # add_referenced catalog[:Outlines], false
188
+ # add_referenced catalog[:AcroForm], false
189
+ catalog
190
+ end
191
+
192
+ def get_existing_catalogs
193
+ (@objects.select { |obj| obj.is_a?(Hash) && obj[:Type] == :Catalog }) || (@objects.select { |obj| obj.is_a?(Hash) && obj[:Type] == :Page })
194
+ end
195
+
196
+ # end
197
+ # @private
198
+ def renumber_object_ids(start = nil)
199
+ @set_start_id = start || @set_start_id
200
+ start = @set_start_id
201
+ history = {}
202
+ @objects.each do |obj|
203
+ obj[:indirect_reference_id] = start
204
+ start += 1
205
+ end
206
+ end
207
+
208
+ def remove_old_ids
209
+ @objects.each { |obj| obj.delete(:indirect_reference_id); obj.delete(:indirect_generation_number) }
210
+ end
211
+
212
+ POSSIBLE_NAME_TREES = [:Dests, :AP, :Pages, :IDS, :Templates, :URLS, :Pages].to_set.freeze
213
+
214
+ def rebuild_names(name_tree = nil, base = 'CombinePDF_0000000')
215
+ if name_tree
216
+ return nil unless name_tree.is_a?(Hash)
217
+ name_tree = name_tree[:referenced_object] || name_tree
218
+ dic = []
219
+ # map a names tree and return a valid name tree. Do not recourse.
220
+ should_resolve = [name_tree[:Kids], name_tree[:Names]]
221
+ resolved = [].to_set
222
+ while should_resolve.any?
223
+ pos = should_resolve.pop
224
+ if pos.is_a? Array
225
+ next if resolved.include?(pos.object_id)
226
+ if pos[0].is_a? String
227
+ (pos.length / 2).times do |i|
228
+ dic << (pos[i * 2].clear << base.next!)
229
+ dic << (pos[(i * 2) + 1].is_a?(Array) ? { is_reference_only: true, referenced_object: { indirect_without_dictionary: pos[(i * 2) + 1] } } : pos[(i * 2) + 1])
230
+ # dic << pos[(i * 2) + 1]
231
+ end
232
+ else
233
+ should_resolve.concat pos
234
+ end
235
+ elsif pos.is_a? Hash
236
+ pos = pos[:referenced_object] || pos
237
+ next if resolved.include?(pos.object_id)
238
+ should_resolve << pos[:Kids] if pos[:Kids]
239
+ should_resolve << pos[:Names] if pos[:Names]
240
+ end
241
+ resolved << pos.object_id
242
+ end
243
+ return { referenced_object: { Names: dic }, is_reference_only: true }
244
+ end
245
+ @names ||= @names[:referenced_object]
246
+ new_names = { Type: :Names }.dup
247
+ POSSIBLE_NAME_TREES.each do |ntree|
248
+ if @names[ntree]
249
+ new_names[ntree] = rebuild_names(@names[ntree], base)
250
+ @names[ntree].clear
251
+ end
252
+ end
253
+ @names.clear
254
+ @names = new_names
255
+ end
256
+
257
+ # @private
258
+ # this method reviews a Hash an updates it by merging Hash data,
259
+ # preffering the new over the old.
260
+ def self.hash_merge_new_no_page(_key, old_data, new_data)
261
+ if old_data.is_a? Hash
262
+ return old_data if old_data[:Type] == :Page
263
+ old_data.merge(new_data, &(@hash_merge_new_no_page_proc ||= method(:hash_merge_new_no_page)))
264
+ elsif old_data.is_a? Array
265
+ old_data + new_data
266
+ else
267
+ new_data
268
+ end
269
+ end
270
+
271
+ # Merges 2 outlines by appending one to the end or start of the other.
272
+ # old_data - the main outline, which is also the one that will be used in the resulting PDF.
273
+ # new_data - the outline to be appended
274
+ # position - an integer representing the position where a PDF is being inserted.
275
+ # This method only differentiates between inserted at the beginning, or not.
276
+ # Not at the beginning, means the new outline will be added to the end of the original outline.
277
+ # An outline base node (tree base) has :Type, :Count, :First, :Last
278
+ # Every node within the outline base node's :First or :Last can have also have the following pointers to other nodes:
279
+ # :First or :Last (only if the node has a subtree / subsection)
280
+ # :Parent (the node's parent)
281
+ # :Prev, :Next (previous and next node)
282
+ # Non-node-pointer data in these nodes:
283
+ # :Title - the node's title displayed in the PDF outline
284
+ # :Count - Number of nodes in it's subtree (0 if no subtree)
285
+ # :Dest - node link destination (if the node is linking to something)
286
+ def merge_outlines(old_data, new_data, position)
287
+ old_data = actual_object(old_data)
288
+ new_data = actual_object(new_data)
289
+ if old_data.nil? || old_data.empty? || old_data[:First].nil?
290
+ # old_data is a reference to the actual object,
291
+ # so if we update old_data, we're done, no need to take any further action
292
+ old_data.update new_data
293
+ elsif new_data.nil? || new_data.empty? || new_data[:First].nil?
294
+ return old_data
295
+ else
296
+ new_data = new_data.dup # avoid old data corruption
297
+ # number of outline nodes, after the merge
298
+ old_data[:Count] = old_data[:Count].to_i + new_data[:Count].to_i
299
+ # walk the Hash here ...
300
+ # I'm just using the start / end insert-position for now...
301
+ # first - is going to be the start of the outline base node's :First, after the merge
302
+ # last - is going to be the end of the outline base node's :Last, after the merge
303
+ # median - the start of what will be appended to the end of the outline base node's :First
304
+ # parent - the outline base node of the resulting merged outline
305
+ # FIXME implement the possibility to insert somewhere in the middle of the outline
306
+ prev = nil
307
+ pos = first = actual_object(((position != 0) ? old_data : new_data)[:First])
308
+ last = actual_object(((position != 0) ? new_data : old_data)[:Last])
309
+ median = { is_reference_only: true, referenced_object: actual_object(((position != 0) ? new_data : old_data)[:First]) }
310
+ old_data[:First] = { is_reference_only: true, referenced_object: first }
311
+ old_data[:Last] = { is_reference_only: true, referenced_object: last }
312
+ parent = { is_reference_only: true, referenced_object: old_data }
313
+ while pos
314
+ # walking through old_data here and updating the :Parent as we go,
315
+ # this updates the inserted new_data :Parent's as well once it is appended and the
316
+ # loop keeps walking the appended data.
317
+ pos[:Parent] = parent if pos[:Parent]
318
+ # connect the two outlines
319
+ # if there is no :Next, the end of the outline base node's :First is reached and this is
320
+ # where the new data gets appended, the same way you would append to a two-way linked list.
321
+ if pos[:Next].nil?
322
+ median[:referenced_object][:Prev] = { is_reference_only: true, referenced_object: prev } if median
323
+ pos[:Next] = median
324
+ # midian becomes 'nil' because this loop keeps going after the appending is done,
325
+ # to update the parents of the appended tree and we wouldn't want to keep appending it infinitely.
326
+ median = nil
327
+ end
328
+ # iterating over the outlines main nodes (this is not going into subtrees)
329
+ # while keeping every rotations previous node saved
330
+ prev = pos
331
+ pos = actual_object(pos[:Next])
332
+ end
333
+ # make sure the last object doesn't have the :Next and the first no :Prev property
334
+ prev.delete :Next
335
+ actual_object(old_data[:First]).delete :Prev
336
+ end
337
+ end
338
+
339
+ # Prints the whole outline hash to a file,
340
+ # with basic indentation and replacing raw streams with "RAW STREAM"
341
+ # (subbing doesn't allways work that great for big streams)
342
+ # outline - outline hash
343
+ # file - "filename.filetype" string
344
+ def print_outline_to_file(outline, file)
345
+ outline_subbed_str = outline.to_s.gsub(/\:raw_stream_content=\>"(?:(?!"}).)*+"\}\}/, ':raw_stream_content=> RAW STREAM}}')
346
+ brace_cnt = 0
347
+ formatted_outline_str = ''
348
+ outline_subbed_str.each_char do |c|
349
+ if c == '{'
350
+ formatted_outline_str << "\n" << "\t" * brace_cnt << c
351
+ brace_cnt += 1
352
+ elsif c == '}'
353
+ brace_cnt -= 1
354
+ brace_cnt = 0 if brace_cnt < 0
355
+ formatted_outline_str << c << "\n" << "\t" * brace_cnt
356
+ elsif c == '\n'
357
+ formatted_outline_str << c << "\t" * brace_cnt
358
+ else
359
+ formatted_outline_str << c
360
+ end
361
+ end
362
+ formatted_outline_str << "\n" * 10
363
+ File.open(file, 'w') { |file| file.write(formatted_outline_str) }
364
+ end
365
+
366
+ private
367
+
368
+ def renaming_dictionary(object = nil, dictionary = {})
369
+ object ||= @names
370
+ case object
371
+ when Array
372
+ object.length.times { |i| object[i].is_a?(String) ? (dictionary[object[i]] = (dictionary.last || 'Random_0001').next) : renaming_dictionary(object[i], dictionary) }
373
+ when Hash
374
+ object.values.each { |v| renaming_dictionary v, dictionary }
375
+ end
376
+ end
377
+
378
+ def rename_object(object, _dictionary)
379
+ case object
380
+ when Array
381
+ object.length.times { |i| }
382
+ when Hash
383
+ end
384
+ end
385
+ end
227
386
  end