Class: HexaPDF::Revisions
- Inherits:
-
Object
- Object
- HexaPDF::Revisions
- Includes:
- Enumerable
- Defined in:
- lib/hexapdf/revisions.rb
Overview
Manages the revisions of a PDF document.
A PDF document has one revision when it is created. Later, new revisions are added when changes are made. This allows for adding information/content to a PDF file without changing the original content.
The order of the revisions is important. In HexaPDF the oldest revision always has index 0 and the newest revision the highest index. This is also the order in which the revisions get written.
See: PDF1.7 s7.5.6, HexaPDF::Revision
Class Method Summary collapse
-
.from_io(document, io) ⇒ Object
Loads all revisions for the document from the given IO and returns the created Revisions object.
Instance Method Summary collapse
-
#add ⇒ Object
Adds a new empty revision to the document and returns it.
-
#current ⇒ Object
Returns the current revision.
-
#delete(index_or_rev) ⇒ Object
:call-seq: revisions.delete(index) -> rev or nil revisions.delete(oid) -> rev or nil.
-
#each(&block) ⇒ Object
:call-seq: revisions.each {|rev| block } -> revisions revisions.each -> Enumerator.
-
#initialize(document, initial_revisions: nil) ⇒ Revisions
constructor
Creates a new revisions object for the given PDF document.
-
#merge(range = 0..-1)) ⇒ Object
:call-seq: revisions.merge(range = 0..-1) -> revisions.
-
#revision(index) ⇒ Object
(also: #[])
Returns the revision at the specified index.
-
#size ⇒ Object
Returns the number of HexaPDF::Revision objects managed by this object.
Constructor Details
#initialize(document, initial_revisions: nil) ⇒ Revisions
Creates a new revisions object for the given PDF document.
Options:
- initial_revisions
-
An array of revisions that should initially be used. If this option is not specified, a single empty revision is added.
98 99 100 101 102 103 104 105 106 |
# File 'lib/hexapdf/revisions.rb', line 98 def initialize(document, initial_revisions: nil) @document = document @revisions = [] if initial_revisions @revisions += initial_revisions else add end end |
Class Method Details
.from_io(document, io) ⇒ Object
Loads all revisions for the document from the given IO and returns the created Revisions object.
If the io
object is nil
, an empty Revisions object is returned.
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/hexapdf/revisions.rb', line 60 def from_io(document, io) return new(document) if io.nil? parser = Parser.new(io, document) object_loader = lambda {|xref_entry| parser.load_object(xref_entry)} revisions = [] xref_section, trailer = parser.load_revision(parser.startxref_offset) revisions << Revision.new(document.wrap(trailer, type: :XXTrailer), xref_section: xref_section, loader: object_loader) while (prev = revisions[0].trailer.value[:Prev]) # PDF1.7 s7.5.5 states that :Prev needs to be indirect, Adobe's reference 3.4.4 says it # should be direct. Adobe's POV is followed here. Same with :XRefStm. xref_section, trailer = parser.load_revision(prev) stm = revisions[0].trailer.value[:XRefStm] stm_xref_section, = parser.load_revision(stm) if stm xref_section.merge!(stm_xref_section) if stm revisions.unshift(Revision.new(document.wrap(trailer, type: :XXTrailer), xref_section: xref_section, loader: object_loader)) end document.version = parser.file_header_version new(document, initial_revisions: revisions) end |
Instance Method Details
#add ⇒ Object
Adds a new empty revision to the document and returns it.
125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/hexapdf/revisions.rb', line 125 def add if @revisions.empty? trailer = {} else trailer = current.trailer.value.dup trailer.delete(:Prev) trailer.delete(:XRefStm) end rev = Revision.new(@document.wrap(trailer, type: :XXTrailer)) @revisions.push(rev) rev end |
#current ⇒ Object
Returns the current revision.
115 116 117 |
# File 'lib/hexapdf/revisions.rb', line 115 def current @revisions.last end |
#delete(index_or_rev) ⇒ Object
:call-seq:
revisions.delete(index) -> rev or nil
revisions.delete(oid) -> rev or nil
Deletes a revision from the document, either by index or by specifying the revision object itself.
Returns the deleted revision object, or nil
if the index was out of range or no matching revision was found.
Regarding the index: The oldest revision has index 0 and the current revision the highest index!
151 152 153 154 155 156 157 158 159 |
# File 'lib/hexapdf/revisions.rb', line 151 def delete(index_or_rev) if @revisions.length == 1 raise HexaPDF::Error, "A document must have a least one revision, can't delete last one" elsif index_or_rev.kind_of?(Integer) @revisions.delete_at(index_or_rev) else @revisions.delete(index_or_rev) end end |
#each(&block) ⇒ Object
:call-seq:
revisions.each {|rev| block } -> revisions
revisions.each -> Enumerator
Iterates over all revisions from oldest to current one.
186 187 188 189 190 |
# File 'lib/hexapdf/revisions.rb', line 186 def each(&block) return to_enum(__method__) unless block_given? @revisions.each(&block) self end |
#merge(range = 0..-1)) ⇒ Object
:call-seq:
revisions.merge(range = 0..-1) -> revisions
Merges the revisions specified by the given range into one. Objects from newer revisions overwrite those from older ones.
166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/hexapdf/revisions.rb', line 166 def merge(range = 0..-1) @revisions[range].reverse.each_cons(2) do |rev, prev_rev| prev_rev.trailer.value.replace(rev.trailer.value) rev.each do |obj| if obj.data != prev_rev.object(obj)&.data prev_rev.delete(obj.oid, mark_as_free: false) prev_rev.add(obj) end end end _first, *other = *@revisions[range] other.each {|rev| @revisions.delete(rev)} self end |
#revision(index) ⇒ Object Also known as: []
Returns the revision at the specified index.
109 110 111 |
# File 'lib/hexapdf/revisions.rb', line 109 def revision(index) @revisions[index] end |
#size ⇒ Object
Returns the number of HexaPDF::Revision objects managed by this object.
120 121 122 |
# File 'lib/hexapdf/revisions.rb', line 120 def size @revisions.size end |