1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
# frozen_string_literal: true
module PDF
module Core
# Low-level PDF document representation mostly for keeping intermediate
# state while document is being constructed.
#
# @api private
class DocumentState
# @param options [Hash<Symbol, any>]
# @option options :info [Hash] Document's information dictionary
# @option options :print_scaling [:none, nil] Viewr preference for
# printing scaling
# @option options :trailer [Hash] ({}) File trailer
# @option options :compress [Boolean] (false) Whether to compress streams
# @option options :encrypt [Boolean] (false) Whether to encrypt the
# document
# @option options :encryption_key [String] (nil) Encryption key. Must be
# provided if `:encrypt` is `true`
def initialize(options)
normalize_metadata(options)
@store =
if options[:print_scaling]
PDF::Core::ObjectStore.new(
info: options[:info],
print_scaling: options[:print_scaling],
)
else
PDF::Core::ObjectStore.new(info: options[:info])
end
@version = 1.3
@pages = []
@page = nil
@trailer = options.fetch(:trailer, {})
@compress = options.fetch(:compress, false)
@encrypt = options.fetch(:encrypt, false)
@encryption_key = options[:encryption_key]
@skip_encoding = options.fetch(:skip_encoding, false)
@before_render_callbacks = []
@on_page_create_callback = nil
end
# Object store
# @return [PDF::Core::ObjectStore]
attr_accessor :store
# PDF version used in this document
# @return [Float]
attr_accessor :version
# Document pages
# @return [Array<PDF::Core::Page>]
attr_accessor :pages
# Current page
# @return [PDF::Core::Page]
attr_accessor :page
# Document trailer dict
# @return [Hash]
attr_accessor :trailer
# Whether to compress streams
# @return [Boolean]
attr_accessor :compress
# Whether to encrypt document
# @return [Boolean]
attr_accessor :encrypt
# Encryption key
# @return [String, nil]
attr_accessor :encryption_key
# @deprecated Unused
attr_accessor :skip_encoding
# Before render callbacks
# @return [Array<Proc>]
attr_accessor :before_render_callbacks
# A block to call when a new page is created
# @return [Proc, nil]
attr_accessor :on_page_create_callback
# Loads pages from object store. Only does it when there are no pages
# loaded and there are some pages in the store.
#
# @return [0] if no pages were loaded
# @return [Array<PDF::Core::Page>] if pages were laded
def populate_pages_from_store(document)
return 0 if @store.page_count <= 0 || !@pages.empty?
count = (1..@store.page_count)
@pages =
count.map { |index|
orig_dict_id = @store.object_id_for_page(index)
PDF::Core::Page.new(document, object_id: orig_dict_id)
}
end
# Adds Prawn metadata to document info
#
# @param options [Hash]
# @return [Hash] Document `info` hash
def normalize_metadata(options)
options[:info] ||= {}
options[:info][:Creator] ||= 'Prawn'
options[:info][:Producer] ||= 'Prawn'
options[:info]
end
# Insert a page at the specified position.
#
# @param page [PDF::Core::Page]
# @param page_number [Integer]
# @return [void]
def insert_page(page, page_number)
pages.insert(page_number, page)
store.pages.data[:Kids].insert(page_number, page.dictionary)
store.pages.data[:Count] += 1
end
# Execute page creation callback if one is defined
#
# @param doc [Prawn::Document]
# @return [void]
def on_page_create_action(doc)
on_page_create_callback[doc] if on_page_create_callback
end
# Executes before render callbacks
#
# @param _doc [Prawn::Document] Unused
# @return [void]
def before_render_actions(_doc)
before_render_callbacks.each { |c| c.call(self) }
end
# Number of pages in the document
#
# @return [Integer]
def page_count
pages.length
end
# Renders document body to the output
#
# @param output [#<<]
# @return [void]
def render_body(output)
store.each do |ref|
ref.offset = output.size
output <<
if @encrypt
ref.encrypted_object(@encryption_key)
else
ref.object
end
end
end
end
end
end
|