hexapdf 0.22.0 → 0.23.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 +50 -0
- data/lib/hexapdf/cli/form.rb +26 -3
- data/lib/hexapdf/cli/inspect.rb +12 -3
- data/lib/hexapdf/cli/modify.rb +23 -3
- data/lib/hexapdf/composer.rb +24 -2
- data/lib/hexapdf/document/destinations.rb +396 -0
- data/lib/hexapdf/document.rb +38 -89
- data/lib/hexapdf/layout/frame.rb +8 -9
- data/lib/hexapdf/layout/style.rb +280 -7
- data/lib/hexapdf/layout/text_box.rb +10 -2
- data/lib/hexapdf/layout/text_layouter.rb +6 -1
- data/lib/hexapdf/revision.rb +8 -1
- data/lib/hexapdf/revisions.rb +151 -50
- data/lib/hexapdf/task/optimize.rb +21 -11
- data/lib/hexapdf/type/acro_form/text_field.rb +8 -0
- data/lib/hexapdf/type/catalog.rb +9 -1
- data/lib/hexapdf/type/names.rb +13 -0
- data/lib/hexapdf/type/xref_stream.rb +2 -1
- data/lib/hexapdf/utils/sorted_tree_node.rb +3 -1
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/writer.rb +15 -2
- data/test/hexapdf/document/test_destinations.rb +338 -0
- data/test/hexapdf/encryption/test_security_handler.rb +2 -2
- data/test/hexapdf/layout/test_frame.rb +15 -1
- data/test/hexapdf/layout/test_text_box.rb +16 -0
- data/test/hexapdf/layout/test_text_layouter.rb +7 -0
- data/test/hexapdf/task/test_optimize.rb +17 -4
- data/test/hexapdf/test_composer.rb +24 -1
- data/test/hexapdf/test_document.rb +30 -133
- data/test/hexapdf/test_parser.rb +1 -1
- data/test/hexapdf/test_revision.rb +14 -0
- data/test/hexapdf/test_revisions.rb +137 -29
- data/test/hexapdf/test_writer.rb +43 -14
- data/test/hexapdf/type/acro_form/test_text_field.rb +17 -0
- data/test/hexapdf/type/test_catalog.rb +8 -0
- data/test/hexapdf/type/test_names.rb +20 -0
- data/test/hexapdf/type/test_xref_stream.rb +2 -1
- data/test/hexapdf/utils/test_sorted_tree_node.rb +11 -1
- metadata +5 -2
data/lib/hexapdf/document.rb
CHANGED
@@ -106,6 +106,7 @@ module HexaPDF
|
|
106
106
|
autoload(:Images, 'hexapdf/document/images')
|
107
107
|
autoload(:Files, 'hexapdf/document/files')
|
108
108
|
autoload(:Signatures, 'hexapdf/document/signatures')
|
109
|
+
autoload(:Destinations, 'hexapdf/document/destinations')
|
109
110
|
|
110
111
|
# :call-seq:
|
111
112
|
# Document.open(filename, **docargs) -> doc
|
@@ -184,22 +185,9 @@ module HexaPDF
|
|
184
185
|
# For references to unknown objects, +nil+ is returned but free objects are represented by a
|
185
186
|
# PDF Null object, not by +nil+!
|
186
187
|
#
|
187
|
-
# See:
|
188
|
+
# See: Revisions#object
|
188
189
|
def object(ref)
|
189
|
-
|
190
|
-
while i >= 0
|
191
|
-
return @revisions[i].object(ref) if @revisions[i].object?(ref)
|
192
|
-
i -= 1
|
193
|
-
end
|
194
|
-
nil
|
195
|
-
end
|
196
|
-
|
197
|
-
# Dereferences the given object.
|
198
|
-
#
|
199
|
-
# Return the object itself if it is not a reference, or the indirect object specified by the
|
200
|
-
# reference.
|
201
|
-
def deref(obj)
|
202
|
-
obj.kind_of?(Reference) ? object(obj) : obj
|
190
|
+
@revisions.object(ref)
|
203
191
|
end
|
204
192
|
|
205
193
|
# :call-seq:
|
@@ -212,74 +200,51 @@ module HexaPDF
|
|
212
200
|
# Even though this method might return +true+ for some references, #object may return +nil+
|
213
201
|
# because this method takes *all* revisions into account. Also see the discussion on #each for
|
214
202
|
# more information.
|
203
|
+
#
|
204
|
+
# See: Revisions#object?
|
215
205
|
def object?(ref)
|
216
|
-
@revisions.
|
206
|
+
@revisions.object?(ref)
|
207
|
+
end
|
208
|
+
|
209
|
+
# Dereferences the given object.
|
210
|
+
#
|
211
|
+
# Return the object itself if it is not a reference, or the indirect object specified by the
|
212
|
+
# reference.
|
213
|
+
def deref(obj)
|
214
|
+
obj.kind_of?(Reference) ? object(obj) : obj
|
217
215
|
end
|
218
216
|
|
219
217
|
# :call-seq:
|
220
|
-
# doc.add(obj,
|
218
|
+
# doc.add(obj, **wrap_opts) -> indirect_object
|
221
219
|
#
|
222
|
-
# Adds the object to the
|
223
|
-
# object.
|
220
|
+
# Adds the object to the document and returns the wrapped indirect object.
|
224
221
|
#
|
225
222
|
# The object can either be a native Ruby object (Hash, Array, Integer, ...) or a
|
226
223
|
# HexaPDF::Object. If it is not the latter, #wrap is called with the object and the
|
227
224
|
# additional keyword arguments.
|
228
225
|
#
|
229
|
-
#
|
230
|
-
|
231
|
-
def add(obj, revision: :current, **wrap_opts)
|
226
|
+
# See: Revisions#add_object
|
227
|
+
def add(obj, **wrap_opts)
|
232
228
|
obj = wrap(obj, **wrap_opts) unless obj.kind_of?(HexaPDF::Object)
|
233
229
|
|
234
|
-
revision = (revision == :current ? @revisions.current : @revisions.revision(revision))
|
235
|
-
if revision.nil?
|
236
|
-
raise ArgumentError, "Invalid revision index specified"
|
237
|
-
end
|
238
|
-
|
239
230
|
if obj.document? && obj.document != self
|
240
231
|
raise HexaPDF::Error, "Can't add object that is already attached to another document"
|
241
232
|
end
|
242
233
|
obj.document = self
|
243
234
|
|
244
|
-
|
245
|
-
if rev_obj.equal?(obj)
|
246
|
-
return obj
|
247
|
-
else
|
248
|
-
raise HexaPDF::Error, "Can't add object because the specified revision already has " \
|
249
|
-
"an object with object number #{obj.oid}"
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
obj.oid = @revisions.map(&:next_free_oid).max unless obj.indirect?
|
254
|
-
|
255
|
-
revision.add(obj)
|
235
|
+
@revisions.add_object(obj)
|
256
236
|
end
|
257
237
|
|
258
238
|
# :call-seq:
|
259
|
-
# doc.delete(ref
|
260
|
-
# doc.delete(oid
|
239
|
+
# doc.delete(ref)
|
240
|
+
# doc.delete(oid)
|
261
241
|
#
|
262
242
|
# Deletes the indirect object specified by an exact reference or by an object number from the
|
263
243
|
# document.
|
264
244
|
#
|
265
|
-
#
|
266
|
-
|
267
|
-
|
268
|
-
#
|
269
|
-
# :all:: Delete the object from all revisions.
|
270
|
-
# :current:: Delete the object only from the current revision.
|
271
|
-
#
|
272
|
-
# mark_as_free:: If +true+, objects are only marked as free objects instead of being actually
|
273
|
-
# deleted.
|
274
|
-
def delete(ref, revision: :all, mark_as_free: true)
|
275
|
-
case revision
|
276
|
-
when :current
|
277
|
-
@revisions.current.delete(ref, mark_as_free: mark_as_free)
|
278
|
-
when :all
|
279
|
-
@revisions.each {|rev| rev.delete(ref, mark_as_free: mark_as_free) }
|
280
|
-
else
|
281
|
-
raise ArgumentError, "Unsupported option revision: #{revision}"
|
282
|
-
end
|
245
|
+
# See: Revisions#delete_object
|
246
|
+
def delete(ref)
|
247
|
+
@revisions.delete_object(ref)
|
283
248
|
end
|
284
249
|
|
285
250
|
# :call-seq:
|
@@ -414,42 +379,20 @@ module HexaPDF
|
|
414
379
|
end
|
415
380
|
|
416
381
|
# :call-seq:
|
417
|
-
# doc.each(only_current: true, only_loaded: false) {|obj| block }
|
418
|
-
# doc.each(only_current: true, only_loaded: false) {|obj, rev| block }
|
382
|
+
# doc.each(only_current: true, only_loaded: false) {|obj| block }
|
383
|
+
# doc.each(only_current: true, only_loaded: false) {|obj, rev| block }
|
419
384
|
# doc.each(only_current: true, only_loaded: false) -> Enumerator
|
420
385
|
#
|
421
|
-
#
|
422
|
-
# object in the PDF document. The block may either accept only the object or the object and the
|
423
|
-
# revision it is in.
|
424
|
-
#
|
425
|
-
# By default, only the current version of each object is returned which implies that each object
|
426
|
-
# number is yielded exactly once. If the +only_current+ option is +false+, all stored objects
|
427
|
-
# from newest to oldest are returned, not only the current version of each object.
|
386
|
+
# Yields every object and the revision it is in.
|
428
387
|
#
|
429
|
-
#
|
430
|
-
# revisions
|
388
|
+
# If +only_current+ is +true+, only the current version of each object is yielded, otherwise
|
389
|
+
# all objects from all revisions.
|
431
390
|
#
|
432
|
-
#
|
433
|
-
# two (different) objects with oid/gen [3,0].
|
391
|
+
# If +only_loaded+ is +true+, only the already loaded objects are yielded.
|
434
392
|
#
|
435
|
-
#
|
436
|
-
# generation numbers in different revisions, e.g. one object with oid/gen [3,0] and one with
|
437
|
-
# oid/gen [3,1].
|
393
|
+
# For details see Revisions#each_object
|
438
394
|
def each(only_current: true, only_loaded: false, &block)
|
439
|
-
|
440
|
-
return to_enum(__method__, only_current: only_current, only_loaded: only_loaded)
|
441
|
-
end
|
442
|
-
|
443
|
-
yield_rev = (block.arity == 2)
|
444
|
-
oids = {}
|
445
|
-
@revisions.reverse_each do |rev|
|
446
|
-
rev.each(only_loaded: only_loaded) do |obj|
|
447
|
-
next if only_current && oids.include?(obj.oid)
|
448
|
-
(yield_rev ? yield(obj, rev) : yield(obj))
|
449
|
-
oids[obj.oid] = true
|
450
|
-
end
|
451
|
-
end
|
452
|
-
self
|
395
|
+
@revisions.each_object(only_current: only_current, only_loaded: only_loaded, &block)
|
453
396
|
end
|
454
397
|
|
455
398
|
# :call-seq:
|
@@ -529,6 +472,12 @@ module HexaPDF
|
|
529
472
|
@fonts ||= Fonts.new(self)
|
530
473
|
end
|
531
474
|
|
475
|
+
# Returns the Destinations object that provides convenience methods for working with destination
|
476
|
+
# objects.
|
477
|
+
def destinations
|
478
|
+
@destinations ||= Destinations.new(self)
|
479
|
+
end
|
480
|
+
|
532
481
|
# Returns the main AcroForm object for dealing with interactive forms.
|
533
482
|
#
|
534
483
|
# See HexaPDF::Type::Catalog#acro_form for details on the arguments.
|
data/lib/hexapdf/layout/frame.rb
CHANGED
@@ -252,14 +252,6 @@ module HexaPDF
|
|
252
252
|
else
|
253
253
|
create_rectangle(x, y, x + width, y + height)
|
254
254
|
end
|
255
|
-
when :float
|
256
|
-
x = @x + @fit_data.margin_left
|
257
|
-
x += @fit_data.available_width - width if box.style.position_hint == :right
|
258
|
-
y = @y - height - @fit_data.margin_top
|
259
|
-
# We use the real margins from the box because they either have the desired effect or just
|
260
|
-
# extend the rectangle outside the frame.
|
261
|
-
rectangle = create_rectangle(x - (margin&.left || 0), y - (margin&.bottom || 0),
|
262
|
-
x + width + (margin&.right || 0), @y)
|
263
255
|
when :flow
|
264
256
|
x = 0
|
265
257
|
y = @y - height
|
@@ -280,7 +272,14 @@ module HexaPDF
|
|
280
272
|
@x + @fit_data.margin_left
|
281
273
|
end
|
282
274
|
y = @y - height - @fit_data.margin_top
|
283
|
-
rectangle =
|
275
|
+
rectangle = if box.style.position == :float
|
276
|
+
# We use the real margins from the box because they either have the desired
|
277
|
+
# effect or just extend the rectangle outside the frame.
|
278
|
+
create_rectangle(x - (margin&.left || 0), y - (margin&.bottom || 0),
|
279
|
+
x + width + (margin&.right || 0), @y)
|
280
|
+
else
|
281
|
+
create_rectangle(left, y - (margin&.bottom || 0), left + self.width, @y)
|
282
|
+
end
|
284
283
|
end
|
285
284
|
|
286
285
|
box.draw(canvas, x, y)
|