Class: HexaPDF::Type::PageTreeNode

Inherits:
Dictionary show all
Defined in:
lib/hexapdf/type/page_tree_node.rb

Overview

Represents a node in the page tree of the PDF’s document.

The page tree is a tree structure containing page tree nodes for the root and intermediate nodes and page objects for the leaf nodes (see Page). The root node of the page tree is linked via the /Pages entry in the Catalog.

All operations except #add_page on the page tree are rather expensive because page tree nodes and page objects can be mixed. This means that for finding a page at a specific index we have to go through all objects that come before it.

Page indices are zero-based, not one-based. Therefore the first page has an index of 0!

Since the page tree needs a certain structure it is not advised to directly modify page tree nodes. The validation feature can correct most problems but until the page tree is in order the methods may not work correctly!

Newly created pages use the ‘page.default_media_box’ configuration option for the /MediaBox value. If an inherited /Resources dictionary does not exist, an empty one is created for the page.

See: PDF2.0 s7.7.3.2, Page

Constant Summary

Constants included from DictionaryFields

DictionaryFields::Boolean, DictionaryFields::PDFByteString, DictionaryFields::PDFDate

Instance Attribute Summary

Attributes inherited from Object

#data, #document, #must_be_indirect

Instance Method Summary collapse

Methods inherited from Dictionary

#[], #[]=, define_field, define_type, #delete, #each, each_field, #empty?, field, #key?, #to_hash, type, #type

Methods inherited from Object

#<=>, #==, #cache, #cached?, #clear_cache, deep_copy, #deep_copy, #document?, #eql?, field, #gen, #gen=, #hash, #indirect?, #initialize, #inspect, make_direct, #null?, #oid, #oid=, #type, #validate, #value, #value=

Constructor Details

This class inherits a constructor from HexaPDF::Object

Instance Method Details

#add_page(page = nil) ⇒ Object

Adds the page or a new empty page at the end and returns it.

See: #insert_page



158
159
160
# File 'lib/hexapdf/type/page_tree_node.rb', line 158

def add_page(page = nil)
  insert_page(-1, page)
end

#delete_page(page) ⇒ Object

:call-seq:

pages.delete_page(page)
pages.delete_page(index)

Deletes the given page or the page at the position specified by the zero-based index from the page tree and the document.

Negative indices count backwards from the end, i.e. -1 is the last page.



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/hexapdf/type/page_tree_node.rb', line 170

def delete_page(page)
  page = self.page(page) if page.kind_of?(Integer)
  return unless page && !page.null? && page[:Parent]

  parent = page[:Parent]
  index = parent[:Kids].index(page)

  if index
    ancestors = page.ancestor_nodes
    return nil unless ancestors.include?(self)

    page[:Parent][:Kids].delete_at(index)
    page.delete(:Parent)
    document.delete(page)
    ancestors.each {|node| node[:Count] -= 1 }
  else
    raise HexaPDF::Error, "Given page not found in page tree"
  end
end

#each_page(&block) ⇒ Object

:call-seq:

pages.each_page {|page| block }   -> pages
pages.each_page                   -> Enumerator

Iterates over all pages that are beneath this page tree node, from the first to the last page.



238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/hexapdf/type/page_tree_node.rb', line 238

def each_page(&block)
  return to_enum(__method__) unless block_given?

  self[:Kids].each do |kid|
    if kid.type == :Page
      yield(kid)
    else
      kid.each_page(&block)
    end
  end

  self
end

#insert_page(index, page = nil) ⇒ Object

Inserts the page or a new empty page at the zero-based index and returns it.

Negative indices count backwards from the end, i.e. -1 is the last page. When using negative indices, the page will be inserted after that element. So using an index of -1 will insert the page after the last page.

Must be called on the root of the page tree, otherwise the /Count entries are not correctly updated!

If an existing page is inserted, it may be necessary to use Page#copy_inherited_values before insertion so that the page dictionary contains all necessary information.



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
# File 'lib/hexapdf/type/page_tree_node.rb', line 125

def insert_page(index, page = nil)
  page ||= new_page
  index = self[:Count] + index + 1 if index < 0

  if index >= self[:Count]
    self[:Kids] << page
    page[:Parent] = self
    page[:Resources] ||= {}
  else
    self[:Kids].each_with_index do |kid, kid_index|
      if index == 0
        self[:Kids].insert(kid_index, page)
        page[:Parent] = self
        break
      elsif kid.type == :Page
        index -= 1
      elsif index <= kid[:Count]
        kid.insert_page(index, page)
        break
      else
        index -= kid[:Count]
      end
    end
  end

  self[:Count] += 1

  page
end

#move_page(page, to_index) ⇒ Object

:call-seq:

pages.move_page(page, to_index)
pages.move_page(index, to_index)

Moves the given page or the page at the position specified by the zero-based index to the to_index position.

If the page that should be moved, doesn’t exist or is invalid, an error is raised.

Negative indices count backwards from the end, i.e. -1 is the last page. When using a negative index, the page will be moved after that element. So using an index of -1 will move the page after the last page.



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/hexapdf/type/page_tree_node.rb', line 202

def move_page(page, to_index)
  page = self.page(page) if page.kind_of?(Integer)
  if page.nil? || page.null? || !page[:Parent] ||
      !(ancestors = page.ancestor_nodes).include?(self)
    raise HexaPDF::Error, "The page to be moved doesn't exist in this page tree"
  end

  parent = page[:Parent]
  prev_index = page.index
  prev_kid_index = parent[:Kids].index(page)
  if to_index < 0
    to_index = self[:Count] + to_index + 1
  elsif prev_index < to_index
    to_index += 1
  end

  insert_page(to_index, page)

  ancestors.each {|node| node[:Count] -= 1 }
  if page[:Parent] == parent
    if prev_index < to_index
      parent[:Kids].delete_at(prev_kid_index)
    else
      parent[:Kids].delete_at(prev_kid_index + 1)
    end
  else
    parent[:Kids].delete(page)
  end
end

#must_be_indirect?Boolean

Returns true since page tree objects must always be indirect.

Returns:



80
81
82
# File 'lib/hexapdf/type/page_tree_node.rb', line 80

def must_be_indirect?
  true
end

#page(index) ⇒ Object

Returns the page for the zero-based index or nil if no such page exists.

Negative indices count backwards from the end, i.e. -1 is the last page.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/hexapdf/type/page_tree_node.rb', line 95

def page(index)
  index = self[:Count] + index if index < 0
  return nil if index < 0 || index >= self[:Count]

  self[:Kids].each do |kid|
    if kid.type == :Page
      if index == 0
        return kid
      else
        index -= 1
      end
    elsif index < kid[:Count]
      return kid.page(index)
    else
      index -= kid[:Count]
    end
  end
end

#page_countObject

Returns the number of pages under this page tree.

Note: If this methods is not called on the root object of the page tree, the returned number is not the total number of pages in the document!



88
89
90
# File 'lib/hexapdf/type/page_tree_node.rb', line 88

def page_count
  self[:Count]
end