Class: Pdfrb::Document
- Inherits:
-
Object
show all
- Defined in:
- lib/pdfrb/document.rb,
lib/pdfrb/document/form.rb,
lib/pdfrb/document/info.rb,
lib/pdfrb/document/files.rb,
lib/pdfrb/document/fonts.rb,
lib/pdfrb/document/pages.rb,
lib/pdfrb/document/colors.rb,
lib/pdfrb/document/images.rb,
lib/pdfrb/document/layers.rb,
lib/pdfrb/document/stamps.rb,
lib/pdfrb/document/display.rb,
lib/pdfrb/document/outline.rb,
lib/pdfrb/document/metadata.rb,
lib/pdfrb/document/shadings.rb,
lib/pdfrb/document/portfolio.rb,
lib/pdfrb/document/structure.rb,
lib/pdfrb/document/encryption.rb,
lib/pdfrb/document/annotations.rb,
lib/pdfrb/document/page_labels.rb,
lib/pdfrb/document/destinations.rb,
lib/pdfrb/document/form_xobject.rb,
lib/pdfrb/document/graphics_state.rb,
lib/pdfrb/document/output_intents.rb,
lib/pdfrb/document/associated_files.rb
Overview
Top-level PDF document facade. Owns:
* The IO it was read from (or nil for in-memory).
* An oid -> Object table for new/modified objects.
* A wrap() pipeline that upgrades raw Hashes to typed
Dictionary subclasses based on /Type or an explicit type.
* On read: an XrefSection + ObjectReader for lazy resolution.
* A revisions stack (TODO 30 — incremental updates).
Defined Under Namespace
Classes: Annotations, AssociatedFiles, Colors, Destinations, Display, Encryption, Files, Fonts, Form, FormXObject, GraphicsState, Images, Info, Layers, Metadata, Outline, OutlineEntry, OutputIntents, PageLabels, Pages, Portfolio, Shadings, Stamps, Structure
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
-
#add(value, type: nil) ⇒ Object
-
#annotations ⇒ Object
-
#associated_files ⇒ Object
-
#catalog ⇒ Object
-
#colors ⇒ Object
-
#create_form_xobject(name: nil, bbox: nil, matrix: nil) ⇒ Object
-
#destinations ⇒ Object
-
#dispatch_message(message, *args) ⇒ Object
-
#display ⇒ Object
-
#each_indirect_object ⇒ Object
Yield every indirect object in this document: modified/new ones first (they shadow loaded ones), then every loaded entry the xref knows about.
-
#each_revision ⇒ Object
Walk every revision in the document's incremental-update chain, from the latest (most recent) revision backward via /Prev.
-
#encryption ⇒ Object
-
#files ⇒ Object
-
#fonts ⇒ Object
-
#forget(oid) ⇒ Object
Drop a modified/new object from the in-memory table so it's no longer emitted by the writer.
-
#form ⇒ Object
-
#graphics_state ⇒ Object
-
#images ⇒ Object
-
#info ⇒ Object
-
#initialize(io: nil, config: {}) ⇒ Document
constructor
A new instance of Document.
-
#layers ⇒ Object
-
#load_xmp_packet ⇒ Object
-
#metadata ⇒ Object
-
#object(reference) ⇒ Object
(also: #dereference)
Resolve a Reference to its Object.
-
#outline ⇒ Object
-
#output_intents ⇒ Object
-
#page_labels ⇒ Object
-
#pages ⇒ Object
---- Facade accessors (Phase 13) ---- Each returns a memoised helper object that knows how to mutate this document.
-
#portfolio ⇒ Object
-
#register_listener(message, &block) ⇒ Object
-
#register_override(obj) ⇒ Object
Replace an indirect object in the @objects table.
-
#revision_count ⇒ Object
Number of revisions in the document.
-
#shadings ⇒ Object
-
#stamps ⇒ Object
-
#structure ⇒ Object
-
#trailer ⇒ Object
Convenience accessor for the document Catalog dict.
-
#wrap(data, type: nil, oid: 0, gen: 0) ⇒ Object
-
#write(path = nil, io: nil) ⇒ Object
Write this document to path (or any IO via +io:).
-
#xmp ⇒ Object
Constructor Details
#initialize(io: nil, config: {}) ⇒ Document
Returns a new instance of Document.
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
# File 'lib/pdfrb/document.rb', line 36
def initialize(io: nil, config: {})
Pdfrb::Model::Type.eager_load!
@config = Configuration.new(config)
@io = io
@objects = {} @next_oid = 1
@listeners = {} @xref = nil
@object_reader = nil
@version = "1.4"
@empty_trailer = nil
@revisions = []
if io
read_from_io(io)
else
seed_empty_structure
end
end
|
Instance Attribute Details
#config ⇒ Object
Returns the value of attribute config.
56
57
58
|
# File 'lib/pdfrb/document.rb', line 56
def config
@config
end
|
#io ⇒ Object
Returns the value of attribute io.
56
57
58
|
# File 'lib/pdfrb/document.rb', line 56
def io
@io
end
|
#next_oid ⇒ Object
Returns the value of attribute next_oid.
56
57
58
|
# File 'lib/pdfrb/document.rb', line 56
def next_oid
@next_oid
end
|
#version ⇒ Object
Returns the value of attribute version.
56
57
58
|
# File 'lib/pdfrb/document.rb', line 56
def version
@version
end
|
#xref ⇒ Object
Returns the value of attribute xref.
56
57
58
|
# File 'lib/pdfrb/document.rb', line 56
def xref
@xref
end
|
Class Method Details
.open(path, **opts) ⇒ Object
58
59
60
61
62
63
64
65
66
|
# File 'lib/pdfrb/document.rb', line 58
def self.open(path, **opts)
if block_given?
File.open(path, "rb") { |f| yield new(io: f, **opts) }
else
bytes = File.binread(path)
require "stringio"
new(io: StringIO.new(bytes), **opts)
end
end
|
Instance Method Details
#add(value, type: nil) ⇒ Object
78
79
80
81
82
83
|
# File 'lib/pdfrb/document.rb', line 78
def add(value, type: nil)
oid = allocate_oid
obj = wrap(value, type: type, oid: oid, gen: 0)
register(obj)
obj
end
|
#annotations ⇒ Object
137
138
139
|
# File 'lib/pdfrb/document.rb', line 137
def annotations
@annotations ||= Document::Annotations.new(self)
end
|
#associated_files ⇒ Object
#catalog ⇒ Object
231
232
233
234
235
236
237
238
|
# File 'lib/pdfrb/document.rb', line 231
def catalog
return @catalog if defined?(@catalog) && @catalog
ref = trailer ? trailer[:Root] : nil
return nil unless ref
@catalog = object(ref)
end
|
#colors ⇒ Object
151
|
# File 'lib/pdfrb/document.rb', line 151
def colors; @colors ||= Document::Colors.new(self); end
|
194
195
196
|
# File 'lib/pdfrb/document.rb', line 194
def create_form_xobject(name: nil, bbox: nil, matrix: nil)
FormXObject.new(self, name: name, bbox: bbox, matrix: matrix)
end
|
#destinations ⇒ Object
133
134
135
|
# File 'lib/pdfrb/document.rb', line 133
def destinations
@destinations ||= Document::Destinations.new(self)
end
|
#dispatch_message(message, *args) ⇒ Object
103
104
105
106
107
|
# File 'lib/pdfrb/document.rb', line 103
def dispatch_message(message, *args)
return unless @listeners.key?(message)
@listeners[message].each { |blk| blk.call(*args) }
end
|
#display ⇒ Object
171
|
# File 'lib/pdfrb/document.rb', line 171
def display; @display ||= Document::Display.new(self); end
|
#each_indirect_object ⇒ Object
Yield every indirect object in this document: modified/new ones
first (they shadow loaded ones), then every loaded entry the
xref knows about.
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
|
# File 'lib/pdfrb/document.rb', line 243
def each_indirect_object
return enum_for(:each_indirect_object) unless block_given?
seen = Set.new
@objects.each_value do |obj|
next unless obj.indirect?
seen << obj.oid
yield obj
end
return if @xref.nil?
@xref.entries.each_key do |oid|
next if seen.include?(oid)
next if oid.zero?
obj = object(Pdfrb::Model::Reference.new(oid, 0))
next if obj.nil? || !obj.indirect?
yield obj
end
end
|
#each_revision ⇒ Object
Walk every revision in the document's incremental-update chain,
from the latest (most recent) revision backward via /Prev.
Each revision yields a (revision_index, xref, trailer) tuple
where revision_index 0 is the latest. Documents without
incremental updates yield a single tuple.
Useful for forensic inspection, version-aware rendering, and
debugging "ghost" objects that were modified in a later
revision.
275
276
277
278
279
280
281
|
# File 'lib/pdfrb/document.rb', line 275
def each_revision
return enum_for(:each_revision) unless block_given?
return (yield 0, @xref, trailer) if @revisions.empty? && @xref
@revisions.each_with_index { |(xref, tr), i| yield i, xref, tr }
end
|
#encryption ⇒ Object
167
|
# File 'lib/pdfrb/document.rb', line 167
def encryption; @encryption ||= Document::Encryption.new(self); end
|
#files ⇒ Object
125
126
127
|
# File 'lib/pdfrb/document.rb', line 125
def files
@files ||= Document::Files.new(self)
end
|
#fonts ⇒ Object
117
118
119
|
# File 'lib/pdfrb/document.rb', line 117
def fonts
@fonts ||= Document::Fonts.new(self)
end
|
#forget(oid) ⇒ Object
Drop a modified/new object from the in-memory table so it's no
longer emitted by the writer. Used by Task::Optimize when a
stream has been deduplicated against a canonical sibling.
Returns the dropped object (or nil if not present).
209
210
211
|
# File 'lib/pdfrb/document.rb', line 209
def forget(oid)
@objects.delete(oid)
end
|
147
|
# File 'lib/pdfrb/document.rb', line 147
def form; @form ||= Document::Form.new(self); end
|
#graphics_state ⇒ Object
153
|
# File 'lib/pdfrb/document.rb', line 153
def graphics_state; @graphics_state ||= Document::GraphicsState.new(self); end
|
#images ⇒ Object
121
122
123
|
# File 'lib/pdfrb/document.rb', line 121
def images
@images ||= Document::Images.new(self)
end
|
#info ⇒ Object
169
|
# File 'lib/pdfrb/document.rb', line 169
def info; @info ||= Document::Info.new(self); end
|
#layers ⇒ Object
155
|
# File 'lib/pdfrb/document.rb', line 155
def layers; @layers ||= Document::Layers.new(self); end
|
#load_xmp_packet ⇒ Object
186
187
188
189
190
|
# File 'lib/pdfrb/document.rb', line 186
def load_xmp_packet
Pdfrb::XMP::Packet.new
rescue LoadError
nil
end
|
129
130
131
|
# File 'lib/pdfrb/document.rb', line 129
def metadata
@metadata ||= Document::Metadata.new(self)
end
|
#object(reference) ⇒ Object
Also known as:
dereference
Resolve a Reference to its Object. New/modified objects take
precedence over xref-loaded ones; otherwise consult the
ObjectReader (which caches per oid).
88
89
90
91
92
93
94
95
96
|
# File 'lib/pdfrb/document.rb', line 88
def object(reference)
return reference unless reference.is_a?(Pdfrb::Model::Reference)
modified = @objects[reference.oid]
return modified if modified
return nil if @object_reader.nil?
@object_reader.load_oid(reference.oid)
end
|
#outline ⇒ Object
141
142
143
|
# File 'lib/pdfrb/document.rb', line 141
def outline
@outline ||= Document::Outline.new(self)
end
|
#output_intents ⇒ Object
161
|
# File 'lib/pdfrb/document.rb', line 161
def output_intents; @output_intents ||= Document::OutputIntents.new(self); end
|
#page_labels ⇒ Object
163
|
# File 'lib/pdfrb/document.rb', line 163
def page_labels; @page_labels ||= Document::PageLabels.new(self); end
|
#pages ⇒ Object
---- Facade accessors (Phase 13) ----
Each returns a memoised helper object that knows how to mutate
this document. Defined under Pdfrb::Document::*.
113
114
115
|
# File 'lib/pdfrb/document.rb', line 113
def pages
@pages ||= Document::Pages.new(self)
end
|
#portfolio ⇒ Object
159
|
# File 'lib/pdfrb/document.rb', line 159
def portfolio; @portfolio ||= Document::Portfolio.new(self); end
|
#register_listener(message, &block) ⇒ Object
99
100
101
|
# File 'lib/pdfrb/document.rb', line 99
def register_listener(message, &block)
(@listeners[message] ||= []) << block
end
|
#register_override(obj) ⇒ Object
Replace an indirect object in the @objects table. Used by the
Importer when promoting a Dictionary stub to a Stream (so cycles
resolve correctly). Idempotent.
201
202
203
|
# File 'lib/pdfrb/document.rb', line 201
def register_override(obj)
@objects[obj.oid] = obj
end
|
#revision_count ⇒ Object
Number of revisions in the document. 1 for a freshly written
PDF; >1 for incrementally-updated PDFs.
285
286
287
|
# File 'lib/pdfrb/document.rb', line 285
def revision_count
@revisions.empty? ? 1 : @revisions.length
end
|
#shadings ⇒ Object
149
|
# File 'lib/pdfrb/document.rb', line 149
def shadings; @shadings ||= Document::Shadings.new(self); end
|
#stamps ⇒ Object
165
|
# File 'lib/pdfrb/document.rb', line 165
def stamps; @stamps ||= Document::Stamps.new(self); end
|
#structure ⇒ Object
145
|
# File 'lib/pdfrb/document.rb', line 145
def structure; @structure ||= Document::Structure.new(self); end
|
#trailer ⇒ Object
Convenience accessor for the document Catalog dict. Loads from
trailer's /Root, which itself is loaded lazily from the xref.
225
226
227
228
229
|
# File 'lib/pdfrb/document.rb', line 225
def trailer
return @trailer if defined?(@trailer) && @trailer
@trailer = read_trailer
end
|
#wrap(data, type: nil, oid: 0, gen: 0) ⇒ Object
68
69
70
71
72
73
74
75
76
|
# File 'lib/pdfrb/document.rb', line 68
def wrap(data, type: nil, oid: 0, gen: 0)
target_class = resolve_target_class(data, type)
value = unwrap_value(data)
if target_class <= Pdfrb::Model::Object
target_class.new(value, oid: oid, gen: gen, document: self)
else
target_class.new(value)
end
end
|
#write(path = nil, io: nil) ⇒ Object
Write this document to path (or any IO via +io:).
214
215
216
217
218
219
220
221
|
# File 'lib/pdfrb/document.rb', line 214
def write(path = nil, io: nil)
target = io || (path && File.open(path, "wb"))
raise ArgumentError, "write needs a path or io:" unless target
Pdfrb::Writer.write(self, target)
target.close if path && io.nil? && target.is_a?(IO)
self
end
|
#xmp ⇒ Object
173
174
175
176
177
178
179
180
181
182
183
184
|
# File 'lib/pdfrb/document.rb', line 173
def xmp
return @xmp if @xmp
packet = load_xmp_packet
return nil unless packet
title = metadata[:Title]
packet.title = title if title
author = metadata[:Author]
packet.author = author if author
@xmp = packet
end
|