Class: Pdfrb::Document
- Inherits:
-
Object
show all
- Includes:
- PdfA
- 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/pdf_a.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,
lib/pdfrb/document/fonts/subsetting.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
Modules: PdfA
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
Constant Summary
Constants included
from PdfA
PdfA::SRGB_IDENTIFIER, PdfA::SRGB_REGISTRY
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
-
#decrypt! ⇒ 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.
-
#encrypt! ⇒ Object
-
#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)
-
#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.
-
#resolve(value) ⇒ Object
Resolve a Reference to its Object.
-
#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
Methods included from PdfA
#enable_pdf_a!, #pdfa_conformance, #pdfa_part
Constructor Details
#initialize(io: nil, config: {}) ⇒ Document
Returns a new instance of Document.
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
# File 'lib/pdfrb/document.rb', line 39
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.
59
60
61
|
# File 'lib/pdfrb/document.rb', line 59
def config
@config
end
|
#io ⇒ Object
Returns the value of attribute io.
59
60
61
|
# File 'lib/pdfrb/document.rb', line 59
def io
@io
end
|
#next_oid ⇒ Object
Returns the value of attribute next_oid.
59
60
61
|
# File 'lib/pdfrb/document.rb', line 59
def next_oid
@next_oid
end
|
#version ⇒ Object
Returns the value of attribute version.
59
60
61
|
# File 'lib/pdfrb/document.rb', line 59
def version
@version
end
|
#xref ⇒ Object
Returns the value of attribute xref.
59
60
61
|
# File 'lib/pdfrb/document.rb', line 59
def xref
@xref
end
|
Class Method Details
.open(path, **opts) ⇒ Object
61
62
63
64
65
66
67
68
69
|
# File 'lib/pdfrb/document.rb', line 61
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
81
82
83
84
85
86
|
# File 'lib/pdfrb/document.rb', line 81
def add(value, type: nil)
oid = allocate_oid
obj = wrap(value, type: type, oid: oid, gen: 0)
register(obj)
obj
end
|
#annotations ⇒ Object
147
148
149
|
# File 'lib/pdfrb/document.rb', line 147
def annotations
@annotations ||= Document::Annotations.new(self)
end
|
#associated_files ⇒ Object
#catalog ⇒ Object
250
251
252
253
254
255
256
257
|
# File 'lib/pdfrb/document.rb', line 250
def catalog
return @catalog if defined?(@catalog) && @catalog
ref = trailer ? trailer[:Root] : nil
return nil unless ref
@catalog = object(ref)
end
|
#colors ⇒ Object
161
|
# File 'lib/pdfrb/document.rb', line 161
def colors; @colors ||= Document::Colors.new(self); end
|
212
213
214
|
# File 'lib/pdfrb/document.rb', line 212
def create_form_xobject(name: nil, bbox: nil, matrix: nil)
FormXObject.new(self, name: name, bbox: bbox, matrix: matrix)
end
|
#decrypt! ⇒ Object
183
184
185
|
# File 'lib/pdfrb/document.rb', line 183
def decrypt!
encryption.decrypt!
end
|
#destinations ⇒ Object
143
144
145
|
# File 'lib/pdfrb/document.rb', line 143
def destinations
@destinations ||= Document::Destinations.new(self)
end
|
#dispatch_message(message, *args) ⇒ Object
113
114
115
116
117
|
# File 'lib/pdfrb/document.rb', line 113
def dispatch_message(message, *args)
return unless @listeners.key?(message)
@listeners[message].each { |blk| blk.call(*args) }
end
|
#display ⇒ Object
189
|
# File 'lib/pdfrb/document.rb', line 189
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.
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
|
# File 'lib/pdfrb/document.rb', line 262
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.
294
295
296
297
298
299
300
|
# File 'lib/pdfrb/document.rb', line 294
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
|
#encrypt! ⇒ Object
179
180
181
|
# File 'lib/pdfrb/document.rb', line 179
def encrypt!(**)
encryption.encrypt!(**)
end
|
#encryption ⇒ Object
177
|
# File 'lib/pdfrb/document.rb', line 177
def encryption; @encryption ||= Document::Encryption.new(self); end
|
#files ⇒ Object
135
136
137
|
# File 'lib/pdfrb/document.rb', line 135
def files
@files ||= Document::Files.new(self)
end
|
#fonts ⇒ Object
127
128
129
|
# File 'lib/pdfrb/document.rb', line 127
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).
227
228
229
|
# File 'lib/pdfrb/document.rb', line 227
def forget(oid)
@objects.delete(oid)
end
|
157
|
# File 'lib/pdfrb/document.rb', line 157
def form; @form ||= Document::Form.new(self); end
|
#graphics_state ⇒ Object
163
|
# File 'lib/pdfrb/document.rb', line 163
def graphics_state; @graphics_state ||= Document::GraphicsState.new(self); end
|
#images ⇒ Object
131
132
133
|
# File 'lib/pdfrb/document.rb', line 131
def images
@images ||= Document::Images.new(self)
end
|
#info ⇒ Object
187
|
# File 'lib/pdfrb/document.rb', line 187
def info; @info ||= Document::Info.new(self); end
|
#layers ⇒ Object
165
|
# File 'lib/pdfrb/document.rb', line 165
def layers; @layers ||= Document::Layers.new(self); end
|
#load_xmp_packet ⇒ Object
204
205
206
207
208
|
# File 'lib/pdfrb/document.rb', line 204
def load_xmp_packet
Pdfrb::XMP::Packet.new
rescue LoadError
nil
end
|
139
140
141
|
# File 'lib/pdfrb/document.rb', line 139
def metadata
@metadata ||= Document::Metadata.new(self)
end
|
#object(reference) ⇒ Object
Also known as:
dereference
98
99
100
101
102
103
104
105
106
|
# File 'lib/pdfrb/document.rb', line 98
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
151
152
153
|
# File 'lib/pdfrb/document.rb', line 151
def outline
@outline ||= Document::Outline.new(self)
end
|
#output_intents ⇒ Object
171
|
# File 'lib/pdfrb/document.rb', line 171
def output_intents; @output_intents ||= Document::OutputIntents.new(self); end
|
#page_labels ⇒ Object
173
|
# File 'lib/pdfrb/document.rb', line 173
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::*.
123
124
125
|
# File 'lib/pdfrb/document.rb', line 123
def pages
@pages ||= Document::Pages.new(self)
end
|
#portfolio ⇒ Object
169
|
# File 'lib/pdfrb/document.rb', line 169
def portfolio; @portfolio ||= Document::Portfolio.new(self); end
|
#register_listener(message, &block) ⇒ Object
109
110
111
|
# File 'lib/pdfrb/document.rb', line 109
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.
219
220
221
|
# File 'lib/pdfrb/document.rb', line 219
def register_override(obj)
@objects[obj.oid] = obj
end
|
#resolve(value) ⇒ Object
Resolve a Reference to its Object. New/modified objects take
precedence over xref-loaded ones; otherwise consult the
ObjectReader (which caches per oid).
Resolve a value to the object it denotes: References are
dereferenced against this document; anything else is returned
unchanged. The single seam for "give me the thing itself".
94
95
96
|
# File 'lib/pdfrb/document.rb', line 94
def resolve(value)
value.is_a?(Pdfrb::Model::Reference) ? object(value) : value
end
|
#revision_count ⇒ Object
Number of revisions in the document. 1 for a freshly written
PDF; >1 for incrementally-updated PDFs.
304
305
306
|
# File 'lib/pdfrb/document.rb', line 304
def revision_count
@revisions.empty? ? 1 : @revisions.length
end
|
#shadings ⇒ Object
159
|
# File 'lib/pdfrb/document.rb', line 159
def shadings; @shadings ||= Document::Shadings.new(self); end
|
#stamps ⇒ Object
175
|
# File 'lib/pdfrb/document.rb', line 175
def stamps; @stamps ||= Document::Stamps.new(self); end
|
#structure ⇒ Object
155
|
# File 'lib/pdfrb/document.rb', line 155
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.
244
245
246
247
248
|
# File 'lib/pdfrb/document.rb', line 244
def trailer
return @trailer if defined?(@trailer) && @trailer
@trailer = read_trailer
end
|
#wrap(data, type: nil, oid: 0, gen: 0) ⇒ Object
71
72
73
74
75
76
77
78
79
|
# File 'lib/pdfrb/document.rb', line 71
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:).
232
233
234
235
236
237
238
239
240
|
# File 'lib/pdfrb/document.rb', line 232
def write(path = nil, io: nil)
target = io || (path && File.open(path, "wb"))
raise ArgumentError, "write needs a path or io:" unless target
fonts.subset_fonts! if config["writer.subset_fonts"] != false
Pdfrb::Writer.write(self, target)
target.close if path && io.nil? && target.is_a?(IO)
self
end
|
#xmp ⇒ Object
191
192
193
194
195
196
197
198
199
200
201
202
|
# File 'lib/pdfrb/document.rb', line 191
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
|