Class: HexaPDF::Writer

Inherits:
Object
  • Object
show all
Defined in:
lib/hexapdf/writer.rb

Overview

Writes the contents of a PDF document to an IO stream.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(document, io) ⇒ Writer

Creates a new writer object for the given HexaPDF document that gets written to the IO object.



61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/hexapdf/writer.rb', line 61

def initialize(document, io)
  @document = document
  @io = io

  @io.binmode
  @io.seek(0, IO::SEEK_SET) # TODO: incremental update!

  @serializer = Serializer.new
  @serializer.encrypter = @document.encrypted? ? @document.security_handler : nil
  @rev_size = 0

  @use_xref_streams = false
end

Class Method Details

.write(document, io, incremental: false) ⇒ Object

Writes the document to the IO object and returns the last XRefSection written.

If incremental is true and the document was created from an existing PDF file, the changes are appended to a full copy of the source document.



51
52
53
54
55
56
57
# File 'lib/hexapdf/writer.rb', line 51

def self.write(document, io, incremental: false)
  if incremental && document.revisions.parser
    new(document, io).write_incremental
  else
    new(document, io).write
  end
end

Instance Method Details

#writeObject

Writes the document to the IO object and returns the file position of the start of the last cross-reference section and the last XRefSection written.



77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/hexapdf/writer.rb', line 77

def write
  move_modified_objects_into_current_revision
  write_file_header

  pos = xref_section = nil
  @document.trailer.info[:Producer] = "HexaPDF version #{HexaPDF::VERSION}"
  @document.revisions.each do |rev|
    pos, xref_section = write_revision(rev, pos)
  end

  [pos, xref_section]
end

#write_incrementalObject

Writes the complete source document unmodified to the IO and then one revision containing all changes. Returns the file position of the start of the cross-reference section and the XRefSection object of that one revision.

For this method to work the document must have been created from an existing file.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/hexapdf/writer.rb', line 95

def write_incremental
  parser = @document.revisions.parser

  _, orig_trailer = parser.load_revision(parser.startxref_offset)
  orig_trailer = @document.wrap(orig_trailer, type: :XXTrailer)
  if @document.revisions.current.trailer[:Encrypt]&.value != orig_trailer[:Encrypt]&.value
    raise HexaPDF::Error, "Used encryption cannot be modified when doing incremental writing"
  end

  parser.io.seek(0, IO::SEEK_SET)
  IO.copy_stream(parser.io, @io)
  @io << "\n"

  @rev_size = @document.revisions.current.next_free_oid
  @use_xref_streams = @document.revisions.any? {|rev| rev.trailer[:Type] == :XRef }

  revision = Revision.new(@document.revisions.current.trailer)
  @document.trailer.info[:Producer] = "HexaPDF version #{HexaPDF::VERSION}"
  if parser.file_header_version < @document.version
    @document.catalog[:Version] = @document.version.to_sym
  end
  @document.revisions.each do |rev|
    rev.each_modified_object(all: true) {|obj| revision.send(:add_without_check, obj) }
  end

  write_revision(revision, parser.startxref_offset)
end