def add_referenced()
      # an existing object map
      resolved = {}.dup
      existing = {}.dup
      should_resolve = [].dup
      #set all existing objects as resolved and register their children for future resolution
      @objects.each { |obj| existing[obj] = obj ; resolved[obj.object_id] = obj; should_resolve << obj.values}
      # loop until should_resolve is empty
      while should_resolve.any?
        obj = should_resolve.pop
        next if resolved[obj.object_id] # the object exists
        if obj.is_a?(Hash)
          referenced = obj[:referenced_object]
          if referenced && referenced.any?
            tmp = resolved[referenced.object_id]
            if !tmp && referenced[:raw_stream_content]
              tmp = existing[referenced[:raw_stream_content]]
              # Avoid endless recursion by limiting it to a number of layers (default == 2)
              tmp = nil unless equal_layers(tmp, referenced)
            end
            if tmp
              obj[:referenced_object] = tmp
            else
              resolved[obj.object_id] = referenced
              #        existing[referenced] = referenced
              existing[referenced[:raw_stream_content]] = referenced
              should_resolve << referenced
              @objects << referenced
            end
          else
            resolved[obj.object_id] = obj
            obj.keys.each { |k| should_resolve << obj[k] unless !obj[k].is_a?(Enumerable) || resolved[obj[k].object_id] }
          end
        elsif obj.is_a?(Array)
          resolved[obj.object_id] = obj
          should_resolve.concat obj
        end
      end
      resolved.clear
      existing.clear
    end
def add_referenced()
      # an existing object map
      resolved = {}.dup
      existing = {}.dup
      should_resolve = [].dup
      #set all existing objects as resolved and register their children for future resolution
      @objects.each { |obj| existing[obj] = obj ; resolved[obj.object_id] = obj; should_resolve << obj.values}
      # loop until should_resolve is empty
      while should_resolve.any?
        obj = should_resolve.pop
        next if resolved[obj.object_id] # the object exists
        if obj.is_a?(Hash)
          referenced = obj[:referenced_object]
          if referenced && referenced.any?
            tmp = resolved[referenced.object_id]
            if !tmp && referenced[:raw_stream_content]
              tmp = existing[referenced[:raw_stream_content]]
              # Avoid endless recursion by limiting it to a number of layers (default == 2)
              tmp = nil unless equal_layers(tmp, referenced)
            end
            if tmp
              obj[:referenced_object] = tmp
            else
              resolved[obj.object_id] = referenced
              #        existing[referenced] = referenced
              existing[referenced[:raw_stream_content]] = referenced
              should_resolve << referenced
              @objects << referenced
            end
          else
            resolved[obj.object_id] = obj
            obj.keys.each { |k| should_resolve << obj[k] unless !obj[k].is_a?(Enumerable) || resolved[obj[k].object_id] }
          end
        elsif obj.is_a?(Array)
          resolved[obj.object_id] = obj
          should_resolve.concat obj
        end
      end
      resolved.clear
      existing.clear
    end
RECORSIVE_PROTECTION = { Parent: true, Last: true}.freeze
 @private
 Some PDF objects contain references to other PDF objects.

 this function adds the references contained in these objects.

 this is used for internal operations, such as injectng data using the << operator.