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 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
|
# encoding: utf-8
# prawn/core/page.rb : Implements low-level representation of a PDF page
#
# Copyright February 2010, Gregory Brown. All Rights Reserved.
#
# This is free software. Please see the LICENSE and COPYING files for details.
#
require 'prawn/document/graphics_state'
module Prawn
module Core
class Page #:nodoc:
include Prawn::Core::Page::GraphicsState
attr_accessor :document, :content, :dictionary, :margins, :stack
def initialize(document, options={})
@document = document
@margins = options[:margins] || { :left => 36,
:right => 36,
:top => 36,
:bottom => 36 }
@stack = Prawn::GraphicStateStack.new(options[:graphic_state])
if options[:object_id]
init_from_object(options)
else
init_new_page(options)
end
end
def layout
return @layout if @layout
mb = dictionary.data[:MediaBox]
if mb[3] > mb[2]
:portrait
else
:landscape
end
end
def size
@size || dimensions[2,2]
end
def in_stamp_stream?
!!@stamp_stream
end
def stamp_stream(dictionary)
@stamp_stream = ""
@stamp_dictionary = dictionary
graphic_stack_size = stack.stack.size
document.save_graphics_state
document.send(:freeze_stamp_graphics)
yield if block_given?
until graphic_stack_size == stack.stack.size
document.restore_graphics_state
end
@stamp_dictionary.data[:Length] = @stamp_stream.length + 1
@stamp_dictionary << @stamp_stream
@stamp_stream = nil
@stamp_dictionary = nil
end
def content
@stamp_stream || document.state.store[@content]
end
# As per the PDF spec, each page can have multiple content streams. This will
# add a fresh, empty content stream this the page, mainly for use in loading
# template files.
#
def new_content_stream
return if in_stamp_stream?
unless dictionary.data[:Contents].is_a?(Array)
dictionary.data[:Contents] = [content]
end
@content = document.ref(:Length => 0)
dictionary.data[:Contents] << document.state.store[@content]
document.open_graphics_state
end
def dictionary
@stamp_dictionary || document.state.store[@dictionary]
end
def resources
if dictionary.data[:Resources]
document.deref(dictionary.data[:Resources])
else
dictionary.data[:Resources] = {}
end
end
def fonts
if resources[:Font]
document.deref(resources[:Font])
else
resources[:Font] = {}
end
end
def xobjects
if resources[:XObject]
document.deref(resources[:XObject])
else
resources[:XObject] = {}
end
end
def ext_gstates
if resources[:ExtGState]
document.deref(resources[:ExtGState])
else
resources[:ExtGState] = {}
end
end
def finalize
if dictionary.data[:Contents].is_a?(Array)
dictionary.data[:Contents].each do |stream|
stream.compress_stream if document.compression_enabled?
stream.data[:Length] = stream.stream.size
end
else
content.compress_stream if document.compression_enabled?
content.data[:Length] = content.stream.size
end
end
def imported_page?
@imported_page
end
def dimensions
return inherited_dictionary_value(:MediaBox) if imported_page?
coords = Prawn::Document::PageGeometry::SIZES[size] || size
[0,0] + case(layout)
when :portrait
coords
when :landscape
coords.reverse
else
raise Prawn::Errors::InvalidPageLayout,
"Layout must be either :portrait or :landscape"
end
end
private
def init_from_object(options)
@dictionary = options[:object_id].to_i
dictionary.data[:Parent] = document.state.store.pages
unless dictionary.data[:Contents].is_a?(Array) # content only on leafs
@content = dictionary.data[:Contents].identifier
end
@stamp_stream = nil
@stamp_dictionary = nil
@imported_page = true
end
def init_new_page(options)
@size = options[:size] || "LETTER"
@layout = options[:layout] || :portrait
@content = document.ref(:Length => 0)
content << "q" << "\n"
@dictionary = document.ref(:Type => :Page,
:Parent => document.state.store.pages,
:MediaBox => dimensions,
:Contents => content)
resources[:ProcSet] = [:PDF, :Text, :ImageB, :ImageC, :ImageI]
@stamp_stream = nil
@stamp_dictionary = nil
end
# some entries in the Page dict can be inherited from parent Pages dicts.
#
# Starting with the current page dict, this method will walk up the
# inheritance chain return the first value that is found for key
#
# inherited_dictionary_value(:MediaBox)
# => [ 0, 0, 595, 842 ]
#
def inherited_dictionary_value(key, local_dict = nil)
local_dict ||= dictionary.data
if local_dict.has_key?(key)
local_dict[key]
elsif local_dict.has_key?(:Parent)
inherited_dictionary_value(key, local_dict[:Parent].data)
else
nil
end
end
end
end
end
|