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
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)
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
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
140
141
142
|
# File 'lib/pdfrb/document.rb', line 140
def annotations
@annotations ||= Document::Annotations.new(self)
end
|
#associated_files ⇒ Object
#catalog ⇒ Object
243
244
245
246
247
248
249
250
|
# File 'lib/pdfrb/document.rb', line 243
def catalog
return @catalog if defined?(@catalog) && @catalog
ref = trailer ? trailer[:Root] : nil
return nil unless ref
@catalog = object(ref)
end
|
#colors ⇒ Object
154
|
# File 'lib/pdfrb/document.rb', line 154
def colors; @colors ||= Document::Colors.new(self); end
|
205
206
207
|
# File 'lib/pdfrb/document.rb', line 205
def create_form_xobject(name: nil, bbox: nil, matrix: nil)
FormXObject.new(self, name: name, bbox: bbox, matrix: matrix)
end
|
#decrypt! ⇒ Object
176
177
178
|
# File 'lib/pdfrb/document.rb', line 176
def decrypt!
encryption.decrypt!
end
|
#destinations ⇒ Object
136
137
138
|
# File 'lib/pdfrb/document.rb', line 136
def destinations
@destinations ||= Document::Destinations.new(self)
end
|
#dispatch_message(message, *args) ⇒ Object
106
107
108
109
110
|
# File 'lib/pdfrb/document.rb', line 106
def dispatch_message(message, *args)
return unless @listeners.key?(message)
@listeners[message].each { |blk| blk.call(*args) }
end
|
#display ⇒ Object
182
|
# File 'lib/pdfrb/document.rb', line 182
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.
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
|
# File 'lib/pdfrb/document.rb', line 255
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.
287
288
289
290
291
292
293
|
# File 'lib/pdfrb/document.rb', line 287
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
172
173
174
|
# File 'lib/pdfrb/document.rb', line 172
def encrypt!(**)
encryption.encrypt!(**)
end
|
#encryption ⇒ Object
170
|
# File 'lib/pdfrb/document.rb', line 170
def encryption; @encryption ||= Document::Encryption.new(self); end
|
#files ⇒ Object
128
129
130
|
# File 'lib/pdfrb/document.rb', line 128
def files
@files ||= Document::Files.new(self)
end
|
#fonts ⇒ Object
120
121
122
|
# File 'lib/pdfrb/document.rb', line 120
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).
220
221
222
|
# File 'lib/pdfrb/document.rb', line 220
def forget(oid)
@objects.delete(oid)
end
|
150
|
# File 'lib/pdfrb/document.rb', line 150
def form; @form ||= Document::Form.new(self); end
|
#graphics_state ⇒ Object
156
|
# File 'lib/pdfrb/document.rb', line 156
def graphics_state; @graphics_state ||= Document::GraphicsState.new(self); end
|
#images ⇒ Object
124
125
126
|
# File 'lib/pdfrb/document.rb', line 124
def images
@images ||= Document::Images.new(self)
end
|
#info ⇒ Object
180
|
# File 'lib/pdfrb/document.rb', line 180
def info; @info ||= Document::Info.new(self); end
|
#layers ⇒ Object
158
|
# File 'lib/pdfrb/document.rb', line 158
def layers; @layers ||= Document::Layers.new(self); end
|
#load_xmp_packet ⇒ Object
197
198
199
200
201
|
# File 'lib/pdfrb/document.rb', line 197
def load_xmp_packet
Pdfrb::XMP::Packet.new
rescue LoadError
nil
end
|
132
133
134
|
# File 'lib/pdfrb/document.rb', line 132
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).
91
92
93
94
95
96
97
98
99
|
# File 'lib/pdfrb/document.rb', line 91
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
144
145
146
|
# File 'lib/pdfrb/document.rb', line 144
def outline
@outline ||= Document::Outline.new(self)
end
|
#output_intents ⇒ Object
164
|
# File 'lib/pdfrb/document.rb', line 164
def output_intents; @output_intents ||= Document::OutputIntents.new(self); end
|
#page_labels ⇒ Object
166
|
# File 'lib/pdfrb/document.rb', line 166
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::*.
116
117
118
|
# File 'lib/pdfrb/document.rb', line 116
def pages
@pages ||= Document::Pages.new(self)
end
|
#portfolio ⇒ Object
162
|
# File 'lib/pdfrb/document.rb', line 162
def portfolio; @portfolio ||= Document::Portfolio.new(self); end
|
#register_listener(message, &block) ⇒ Object
102
103
104
|
# File 'lib/pdfrb/document.rb', line 102
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.
212
213
214
|
# File 'lib/pdfrb/document.rb', line 212
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.
297
298
299
|
# File 'lib/pdfrb/document.rb', line 297
def revision_count
@revisions.empty? ? 1 : @revisions.length
end
|
#shadings ⇒ Object
152
|
# File 'lib/pdfrb/document.rb', line 152
def shadings; @shadings ||= Document::Shadings.new(self); end
|
#stamps ⇒ Object
168
|
# File 'lib/pdfrb/document.rb', line 168
def stamps; @stamps ||= Document::Stamps.new(self); end
|
#structure ⇒ Object
148
|
# File 'lib/pdfrb/document.rb', line 148
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.
237
238
239
240
241
|
# File 'lib/pdfrb/document.rb', line 237
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:).
225
226
227
228
229
230
231
232
233
|
# File 'lib/pdfrb/document.rb', line 225
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
184
185
186
187
188
189
190
191
192
193
194
195
|
# File 'lib/pdfrb/document.rb', line 184
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
|