lib/hexapdf/document.rb in hexapdf-0.22.0 vs lib/hexapdf/document.rb in hexapdf-0.23.0

- old
+ new

@@ -104,10 +104,11 @@ autoload(:Pages, 'hexapdf/document/pages') autoload(:Fonts, 'hexapdf/document/fonts') autoload(:Images, 'hexapdf/document/images') autoload(:Files, 'hexapdf/document/files') autoload(:Signatures, 'hexapdf/document/signatures') + autoload(:Destinations, 'hexapdf/document/destinations') # :call-seq: # Document.open(filename, **docargs) -> doc # Document.open(filename, **docargs) {|doc| block} -> obj # @@ -182,106 +183,70 @@ # given object number. # # For references to unknown objects, +nil+ is returned but free objects are represented by a # PDF Null object, not by +nil+! # - # See: PDF1.7 s7.3.9 + # See: Revisions#object def object(ref) - i = @revisions.size - 1 - while i >= 0 - return @revisions[i].object(ref) if @revisions[i].object?(ref) - i -= 1 - end - nil + @revisions.object(ref) end - # Dereferences the given object. - # - # Return the object itself if it is not a reference, or the indirect object specified by the - # reference. - def deref(obj) - obj.kind_of?(Reference) ? object(obj) : obj - end - # :call-seq: # doc.object?(ref) -> true or false # doc.object?(oid) -> true or false # # Returns +true+ if the the document contains an indirect object for the given exact reference # or for the given object number. # # Even though this method might return +true+ for some references, #object may return +nil+ # because this method takes *all* revisions into account. Also see the discussion on #each for # more information. + # + # See: Revisions#object? def object?(ref) - @revisions.any? {|rev| rev.object?(ref) } + @revisions.object?(ref) end + # Dereferences the given object. + # + # Return the object itself if it is not a reference, or the indirect object specified by the + # reference. + def deref(obj) + obj.kind_of?(Reference) ? object(obj) : obj + end + # :call-seq: - # doc.add(obj, revision: :current, **wrap_opts) -> indirect_object + # doc.add(obj, **wrap_opts) -> indirect_object # - # Adds the object to the specified revision of the document and returns the wrapped indirect - # object. + # Adds the object to the document and returns the wrapped indirect object. # # The object can either be a native Ruby object (Hash, Array, Integer, ...) or a # HexaPDF::Object. If it is not the latter, #wrap is called with the object and the # additional keyword arguments. # - # If the +revision+ option is +:current+, the current revision is used. Otherwise +revision+ - # should be a revision index. - def add(obj, revision: :current, **wrap_opts) + # See: Revisions#add_object + def add(obj, **wrap_opts) obj = wrap(obj, **wrap_opts) unless obj.kind_of?(HexaPDF::Object) - revision = (revision == :current ? @revisions.current : @revisions.revision(revision)) - if revision.nil? - raise ArgumentError, "Invalid revision index specified" - end - if obj.document? && obj.document != self raise HexaPDF::Error, "Can't add object that is already attached to another document" end obj.document = self - if obj.indirect? && (rev_obj = revision.object(obj.oid)) - if rev_obj.equal?(obj) - return obj - else - raise HexaPDF::Error, "Can't add object because the specified revision already has " \ - "an object with object number #{obj.oid}" - end - end - - obj.oid = @revisions.map(&:next_free_oid).max unless obj.indirect? - - revision.add(obj) + @revisions.add_object(obj) end # :call-seq: - # doc.delete(ref, revision: :all) - # doc.delete(oid, revision: :all) + # doc.delete(ref) + # doc.delete(oid) # # Deletes the indirect object specified by an exact reference or by an object number from the # document. # - # Options: - # - # revision:: Specifies from which revisions the object should be deleted: - # - # :all:: Delete the object from all revisions. - # :current:: Delete the object only from the current revision. - # - # mark_as_free:: If +true+, objects are only marked as free objects instead of being actually - # deleted. - def delete(ref, revision: :all, mark_as_free: true) - case revision - when :current - @revisions.current.delete(ref, mark_as_free: mark_as_free) - when :all - @revisions.each {|rev| rev.delete(ref, mark_as_free: mark_as_free) } - else - raise ArgumentError, "Unsupported option revision: #{revision}" - end + # See: Revisions#delete_object + def delete(ref) + @revisions.delete_object(ref) end # :call-seq: # doc.import(obj) -> imported_object # @@ -412,46 +377,24 @@ object end end # :call-seq: - # doc.each(only_current: true, only_loaded: false) {|obj| block } -> doc - # doc.each(only_current: true, only_loaded: false) {|obj, rev| block } -> doc + # doc.each(only_current: true, only_loaded: false) {|obj| block } + # doc.each(only_current: true, only_loaded: false) {|obj, rev| block } # doc.each(only_current: true, only_loaded: false) -> Enumerator # - # Calls the given block once for every object, or, if +only_loaded+ is +true+, for every loaded - # object in the PDF document. The block may either accept only the object or the object and the - # revision it is in. + # Yields every object and the revision it is in. # - # By default, only the current version of each object is returned which implies that each object - # number is yielded exactly once. If the +only_current+ option is +false+, all stored objects - # from newest to oldest are returned, not only the current version of each object. + # If +only_current+ is +true+, only the current version of each object is yielded, otherwise + # all objects from all revisions. # - # The +only_current+ option can make a difference because the document can contain multiple - # revisions: + # If +only_loaded+ is +true+, only the already loaded objects are yielded. # - # * Multiple revisions may contain objects with the same object and generation numbers, e.g. - # two (different) objects with oid/gen [3,0]. - # - # * Additionally, there may also be objects with the same object number but different - # generation numbers in different revisions, e.g. one object with oid/gen [3,0] and one with - # oid/gen [3,1]. + # For details see Revisions#each_object def each(only_current: true, only_loaded: false, &block) - unless block_given? - return to_enum(__method__, only_current: only_current, only_loaded: only_loaded) - end - - yield_rev = (block.arity == 2) - oids = {} - @revisions.reverse_each do |rev| - rev.each(only_loaded: only_loaded) do |obj| - next if only_current && oids.include?(obj.oid) - (yield_rev ? yield(obj, rev) : yield(obj)) - oids[obj.oid] = true - end - end - self + @revisions.each_object(only_current: only_current, only_loaded: only_loaded, &block) end # :call-seq: # doc.register_listener(name, callable) -> callable # doc.register_listener(name) {|*args| block} -> block @@ -525,9 +468,15 @@ end # Returns the Fonts object that provides convenience methods for working with fonts. def fonts @fonts ||= Fonts.new(self) + end + + # Returns the Destinations object that provides convenience methods for working with destination + # objects. + def destinations + @destinations ||= Destinations.new(self) end # Returns the main AcroForm object for dealing with interactive forms. # # See HexaPDF::Type::Catalog#acro_form for details on the arguments.