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/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, 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.
-
#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.
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
# File 'lib/pdfrb/document.rb', line 35
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.
55
56
57
|
# File 'lib/pdfrb/document.rb', line 55
def config
@config
end
|
#io ⇒ Object
Returns the value of attribute io.
55
56
57
|
# File 'lib/pdfrb/document.rb', line 55
def io
@io
end
|
#next_oid ⇒ Object
Returns the value of attribute next_oid.
55
56
57
|
# File 'lib/pdfrb/document.rb', line 55
def next_oid
@next_oid
end
|
#version ⇒ Object
Returns the value of attribute version.
55
56
57
|
# File 'lib/pdfrb/document.rb', line 55
def version
@version
end
|
#xref ⇒ Object
Returns the value of attribute xref.
55
56
57
|
# File 'lib/pdfrb/document.rb', line 55
def xref
@xref
end
|
Class Method Details
.open(path, **opts) ⇒ Object
57
58
59
60
61
62
63
64
65
|
# File 'lib/pdfrb/document.rb', line 57
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
77
78
79
80
81
82
|
# File 'lib/pdfrb/document.rb', line 77
def add(value, type: nil)
oid = allocate_oid
obj = wrap(value, type: type, oid: oid, gen: 0)
register(obj)
obj
end
|
#annotations ⇒ Object
136
137
138
|
# File 'lib/pdfrb/document.rb', line 136
def annotations
@annotations ||= Document::Annotations.new(self)
end
|
#associated_files ⇒ Object
#catalog ⇒ Object
228
229
230
231
232
233
234
235
|
# File 'lib/pdfrb/document.rb', line 228
def catalog
return @catalog if defined?(@catalog) && @catalog
ref = trailer ? trailer[:Root] : nil
return nil unless ref
@catalog = object(ref)
end
|
#colors ⇒ Object
150
|
# File 'lib/pdfrb/document.rb', line 150
def colors; @colors ||= Document::Colors.new(self); end
|
191
192
193
|
# File 'lib/pdfrb/document.rb', line 191
def create_form_xobject(name: nil, bbox: nil, matrix: nil)
FormXObject.new(self, name: name, bbox: bbox, matrix: matrix)
end
|
#destinations ⇒ Object
132
133
134
|
# File 'lib/pdfrb/document.rb', line 132
def destinations
@destinations ||= Document::Destinations.new(self)
end
|
#dispatch_message(message, *args) ⇒ Object
102
103
104
105
106
|
# File 'lib/pdfrb/document.rb', line 102
def dispatch_message(message, *args)
return unless @listeners.key?(message)
@listeners[message].each { |blk| blk.call(*args) }
end
|
#display ⇒ Object
168
|
# File 'lib/pdfrb/document.rb', line 168
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.
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
|
# File 'lib/pdfrb/document.rb', line 240
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.
272
273
274
275
276
277
278
|
# File 'lib/pdfrb/document.rb', line 272
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
|
#files ⇒ Object
124
125
126
|
# File 'lib/pdfrb/document.rb', line 124
def files
@files ||= Document::Files.new(self)
end
|
#fonts ⇒ Object
116
117
118
|
# File 'lib/pdfrb/document.rb', line 116
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).
206
207
208
|
# File 'lib/pdfrb/document.rb', line 206
def forget(oid)
@objects.delete(oid)
end
|
146
|
# File 'lib/pdfrb/document.rb', line 146
def form; @form ||= Document::Form.new(self); end
|
#graphics_state ⇒ Object
152
|
# File 'lib/pdfrb/document.rb', line 152
def graphics_state; @graphics_state ||= Document::GraphicsState.new(self); end
|
#images ⇒ Object
120
121
122
|
# File 'lib/pdfrb/document.rb', line 120
def images
@images ||= Document::Images.new(self)
end
|
#info ⇒ Object
166
|
# File 'lib/pdfrb/document.rb', line 166
def info; @info ||= Document::Info.new(self); end
|
#layers ⇒ Object
154
|
# File 'lib/pdfrb/document.rb', line 154
def layers; @layers ||= Document::Layers.new(self); end
|
#load_xmp_packet ⇒ Object
183
184
185
186
187
|
# File 'lib/pdfrb/document.rb', line 183
def load_xmp_packet
Pdfrb::XMP::Packet.new
rescue LoadError
nil
end
|
128
129
130
|
# File 'lib/pdfrb/document.rb', line 128
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).
87
88
89
90
91
92
93
94
95
|
# File 'lib/pdfrb/document.rb', line 87
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
140
141
142
|
# File 'lib/pdfrb/document.rb', line 140
def outline
@outline ||= Document::Outline.new(self)
end
|
#output_intents ⇒ Object
160
|
# File 'lib/pdfrb/document.rb', line 160
def output_intents; @output_intents ||= Document::OutputIntents.new(self); end
|
#page_labels ⇒ Object
162
|
# File 'lib/pdfrb/document.rb', line 162
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::*.
112
113
114
|
# File 'lib/pdfrb/document.rb', line 112
def pages
@pages ||= Document::Pages.new(self)
end
|
#portfolio ⇒ Object
158
|
# File 'lib/pdfrb/document.rb', line 158
def portfolio; @portfolio ||= Document::Portfolio.new(self); end
|
#register_listener(message, &block) ⇒ Object
98
99
100
|
# File 'lib/pdfrb/document.rb', line 98
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.
198
199
200
|
# File 'lib/pdfrb/document.rb', line 198
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.
282
283
284
|
# File 'lib/pdfrb/document.rb', line 282
def revision_count
@revisions.empty? ? 1 : @revisions.length
end
|
#shadings ⇒ Object
148
|
# File 'lib/pdfrb/document.rb', line 148
def shadings; @shadings ||= Document::Shadings.new(self); end
|
#stamps ⇒ Object
164
|
# File 'lib/pdfrb/document.rb', line 164
def stamps; @stamps ||= Document::Stamps.new(self); end
|
#structure ⇒ Object
144
|
# File 'lib/pdfrb/document.rb', line 144
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.
222
223
224
225
226
|
# File 'lib/pdfrb/document.rb', line 222
def trailer
return @trailer if defined?(@trailer) && @trailer
@trailer = read_trailer
end
|
#wrap(data, type: nil, oid: 0, gen: 0) ⇒ Object
67
68
69
70
71
72
73
74
75
|
# File 'lib/pdfrb/document.rb', line 67
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:).
211
212
213
214
215
216
217
218
|
# File 'lib/pdfrb/document.rb', line 211
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
170
171
172
173
174
175
176
177
178
179
180
181
|
# File 'lib/pdfrb/document.rb', line 170
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
|