class HexaPDF::Importer

The Importer class manages the process of copying objects from one Document to another.

It may seem unnecessary using an importer containing state for the task. However, by retaining some information about the already copied objects we can make sure that already imported objects don't get imported again.

Two types of indirect objects are never imported from one document to another: the catalog and page tree nodes. If the catalog was imported, the whole source document would be imported. And if one page tree node would imported, the whole page tree would be imported.

See: Document#import

Public Class Methods

for(source:, destination:) click to toggle source

Returns the Importer object for copying objects from the source to the destination document.

# File lib/hexapdf/importer.rb, line 64
def self.for(source:, destination:)
  @map ||= {}
  @map.keep_if {|_, v| v.source.weakref_alive? && v.destination.weakref_alive? }
  source = NullableWeakRef.new(source)
  destination = NullableWeakRef.new(destination)
  @map[[source.hash, destination.hash]] ||= new(source: source, destination: destination)
end
new(source:, destination:) click to toggle source

Initializes a new importer that can import objects from the source document to the destination document.

# File lib/hexapdf/importer.rb, line 78
def initialize(source:, destination:)
  @source = source
  @destination = destination
  @mapper = {}
end

Public Instance Methods

import(object) click to toggle source

Imports the given object from the source to the destination object and returns the imported object.

Note: Indirect objects are automatically added to the destination document but direct or simple objects are not.

An error is raised if the object doesn't belong to the source document.

# File lib/hexapdf/importer.rb, line 91
def import(object)
  mapped_object = @mapper[object.data]&.__getobj__ if object.kind_of?(HexaPDF::Object)
  if object.kind_of?(HexaPDF::Object) && object.document? && @source != object.document
    raise HexaPDF::Error, "Import error: Incorrect document object for importer"
  elsif mapped_object && mapped_object == @destination.object(mapped_object)
    mapped_object
  else
    duplicate(object)
  end
end

Private Instance Methods

duplicate(object) click to toggle source

Recursively duplicates the object.

PDF objects are automatically added to the destination document if they are indirect objects in the source document.

# File lib/hexapdf/importer.rb, line 108
def duplicate(object)
  case object
  when Hash
    object.transform_values {|v| duplicate(v) }
  when Array
    object.map {|v| duplicate(v) }
  when HexaPDF::Reference
    import(@source.object(object))
  when HexaPDF::Object
    if object.type == :Catalog || object.type == :Pages
      @mapper[object.data] = nil
    else
      obj = object.dup
      @mapper[object.data] = NullableWeakRef.new(obj)
      obj.document = @destination.__getobj__
      obj.instance_variable_set(:@data, obj.data.dup)
      obj.data.oid = 0
      obj.data.gen = 0
      @destination.add(obj) if object.indirect?

      obj.data.stream = obj.data.stream.dup if obj.data.stream.kind_of?(String)
      obj.data.value = duplicate(obj.data.value)
      obj.data.value.update(duplicate(object.copy_inherited_values)) if object.type == :Page
      obj
    end
  when String
    object.dup
  else
    object
  end
end