module Prawn::Templates::ObjectStoreExtensions
Public Instance Methods
imports all objects required to render a page from another PDF. The objects are added to the current object store, but NOT linked anywhere.
The object ID of the root Page object is returned, it's up to the calling code to link that into the document structure somewhere. If this isn't done the imported objects will just be removed when the store is compacted.
Imports nothing and returns nil if the requested page number doesn't exist. page_num is 1 indexed, so 1 indicates the first page.
# File lib/prawn/templates.rb, line 97 def import_page(input, page_num) @loaded_objects = {} if template_id = indexed_template(input, page_num) return template_id end io = if input.respond_to?(:seek) && input.respond_to?(:read) input elsif File.file?(input.to_s) StringIO.new(File.binread(input.to_s)) else fail ArgumentError, "input must be an IO-like object or a filename" end hash = indexed_hash(input, io) ref = hash.page_references[page_num - 1] if ref.nil? nil else index_template(input, page_num, load_object_graph(hash, ref).identifier) end rescue PDF::Reader::MalformedPDFError, PDF::Reader::InvalidObjectError => e msg = "Error reading template file. If you are sure it's a valid PDF, it may be a bug.\n#{e.message}" raise PDF::Core::Errors::TemplateError, msg rescue PDF::Reader::UnsupportedFeatureError msg = "Template file contains unsupported PDF features" raise PDF::Core::Errors::TemplateError, msg end
Private Instance Methods
returns a nested array of object IDs for all pages in this object store.
# File lib/prawn/templates.rb, line 172 def get_page_objects(obj) if obj.data[:Type] == :Page obj.identifier elsif obj.data[:Type] == :Pages obj.data[:Kids].map { |kid| get_page_objects(kid) } end end
An index for the read object hash of a pdf template so that the object hash does not need to be parsed multiple times when using different pages of the pdf as page templates
# File lib/prawn/templates.rb, line 151 def hash_index @hash_index ||= {} end
indexes the identifier for a page from a template
# File lib/prawn/templates.rb, line 144 def index_template(input, page_number, id) (template_index[indexing_key(input)] ||= {})[page_number] ||= id end
reads and indexes a new IO for a template if the IO has been indexed already then the parsed object hash is returned directly
# File lib/prawn/templates.rb, line 158 def indexed_hash(input, io) hash_index[indexing_key(input)] ||= PDF::Reader::ObjectHash.new(io) end
returns the indexed object graph identifier for a template page if it exists
# File lib/prawn/templates.rb, line 138 def indexed_template(input, page_number) key = indexing_key(input) template_index[key] && template_index[key][page_number] end
the index key for the input. uses object_id so that both a string filename or an IO stream can be indexed and reused provided the same object gets used in multiple page template calls.
# File lib/prawn/templates.rb, line 166 def indexing_key(input) input.object_id end
takes a source PDF and uses it as a template for this document.
# File lib/prawn/templates.rb, line 182 def load_file(template) unless (template.respond_to?(:seek) && template.respond_to?(:read)) || File.file?(template) fail ArgumentError, "#{template} does not exist" end hash = PDF::Reader::ObjectHash.new(template) src_info = hash.trailer[:Info] src_root = hash.trailer[:Root] @min_version = hash.pdf_version.to_f if hash.trailer[:Encrypt] msg = "Template file is an encrypted PDF, it can't be used as a template" fail PDF::Core::Errors::TemplateError, msg end if src_info @info = load_object_graph(hash, src_info).identifier end if src_root @root = load_object_graph(hash, src_root).identifier end rescue PDF::Reader::MalformedPDFError, PDF::Reader::InvalidObjectError => e msg = "Error reading template file. If you are sure it's a valid PDF, it may be a bug.\n#{e.message}" raise PDF::Core::Errors::TemplateError, msg rescue PDF::Reader::UnsupportedFeatureError msg = "Template file contains unsupported PDF features" raise PDF::Core::Errors::TemplateError, msg end
recurse down an object graph from a source PDF, importing all the indirect objects we find.
hash is the PDF::Reader::ObjectHash to extract objects from, object is the object to extract.
# File lib/prawn/templates.rb, line 219 def load_object_graph(hash, object) @loaded_objects ||= {} case object when ::Hash then object.each { |key, value| object[key] = load_object_graph(hash, value) } object when Array then object.map { |item| load_object_graph(hash, item) } when PDF::Reader::Reference then unless @loaded_objects.key?(object.id) @loaded_objects[object.id] = ref(nil) new_obj = load_object_graph(hash, hash[object]) if new_obj.kind_of?(PDF::Reader::Stream) stream_dict = load_object_graph(hash, new_obj.hash) @loaded_objects[object.id].data = stream_dict @loaded_objects[object.id] << new_obj.data else @loaded_objects[object.id].data = new_obj end end @loaded_objects[object.id] when PDF::Reader::Stream # Stream is a subclass of string, so this is here to prevent the stream # being wrapped in a LiteralString object when String is_utf8?(object) ? object : PDF::Core::ByteString.new(object) else object end end
An index for page templates so that their loaded object graph can be reused without multiple loading
# File lib/prawn/templates.rb, line 132 def template_index @template_index ||= {} end