# -*- encoding: utf-8; frozen_string_literal: true -*-
#
#--
# This file is part of HexaPDF.
#
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
# Copyright (C) 2014-2023 Thomas Leitner
#
# HexaPDF is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License version 3 as
# published by the Free Software Foundation with the addition of the
# following permission added to Section 15 as permitted in Section 7(a):
# FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
# THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
# INFRINGEMENT OF THIRD PARTY RIGHTS.
#
# HexaPDF is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with HexaPDF. If not, see .
#
# The interactive user interfaces in modified source and object code
# versions of HexaPDF must display Appropriate Legal Notices, as required
# under Section 5 of the GNU Affero General Public License version 3.
#
# In accordance with Section 7(b) of the GNU Affero General Public
# License, a covered work must retain the producer line in every PDF that
# is created or manipulated using HexaPDF.
#
# If the GNU Affero General Public License doesn't fit your need,
# commercial licenses are available at .
#++
require 'hexapdf/dictionary'
require 'hexapdf/number_tree_node'
require 'hexapdf/stream'
module HexaPDF
module Type
# Represents the PDF's catalog dictionary which is at the root of the document's object
# hierarchy.
#
# The catalog dictionary is linked via the /Root entry from the Trailer.
#
# See: PDF2.0 s7.7.2, Trailer
class Catalog < Dictionary
define_type :Catalog
define_field :Type, type: Symbol, required: true, default: type
define_field :Version, type: Symbol, version: '1.4'
define_field :Extensions, type: Dictionary, version: '1.7'
# Pages field is required but this is handled in #perform_validation
define_field :Pages, type: :Pages, indirect: true
define_field :PageLabels, type: NumberTreeNode, version: '1.3'
define_field :Names, type: :XXNames, version: '1.2'
define_field :Dests, type: Dictionary, version: '1.1'
define_field :ViewerPreferences, type: :XXViewerPreferences, version: '1.2'
define_field :PageLayout, type: Symbol, default: :SinglePage,
allowed_values: [:SinglePage, :OneColumn, :TwoColumnLeft, :TwoColumnRight,
:TwoPageLeft, :TwoPageRight]
define_field :PageMode, type: Symbol, default: :UseNone,
allowed_values: [:UseNone, :UseOutlines, :UseThumbs, :FullScreen, :UseOC, :UseAttachments]
define_field :Outlines, type: :Outlines, indirect: true
define_field :Threads, type: PDFArray, version: '1.1'
define_field :OpenAction, type: [Dictionary, PDFArray], version: '1.1'
define_field :AA, type: Dictionary, version: '1.4'
define_field :URI, type: Dictionary, version: '1.1'
define_field :AcroForm, type: :XXAcroForm, version: '1.2'
define_field :Metadata, type: Stream, indirect: true, version: '1.4'
define_field :StructTreeRoot, type: Dictionary, version: '1.3'
define_field :MarkInfo, type: :XXMarkInformation, version: '1.4'
define_field :Lang, type: String, version: '1.4'
define_field :SpiderInfo, type: Dictionary, version: '1.3'
define_field :OutputIntents, type: PDFArray, version: '1.4'
define_field :PieceInfo, type: Dictionary, version: '1.4'
define_field :OCProperties, type: :XXOCProperties, version: '1.5'
define_field :Perms, type: Dictionary, version: '1.5'
define_field :Legal, type: Dictionary, version: '1.5'
define_field :Requirements, type: PDFArray, version: '1.7'
define_field :Collection, type: Dictionary, version: '1.7'
define_field :NeedsRendering, type: Boolean, version: '1.7'
# Returns +true+ since catalog objects must always be indirect.
def must_be_indirect?
true
end
# Returns the root node of the page tree, creating it if needed.
#
# See: PageTreeNode
def pages
self[:Pages] ||= document.add({Type: :Pages})
end
# Returns the name dictionary containing all name trees of the document, creating it if
# needed.
#
# See: Names
def names
self[:Names] ||= document.add({}, type: :XXNames)
end
# Returns the document outline, creating it if needed.
#
# See: Outline
def outline
self[:Outlines] ||= document.add({}, type: :Outlines)
end
# Returns the optional content properties dictionary, creating it if needed.
#
# This is the main entry point for working with optional content, a.k.a. layers.
#
# See: OptionalContentProperties
def optional_content
self[:OCProperties] ||= document.add({OCGs: [], D: {Creator: 'HexaPDF'}}, type: :XXOCProperties)
end
# Returns the main AcroForm object.
#
# * If an AcroForm object exists, the +create+ argument is not used.
#
# * If no AcroForm object exists and +create+ is +true+, a new AcroForm object with default
# settings will be created and returned.
#
# * If no AcroForm object exists and +create+ is +false+, +nil+ is returned.
#
# See: AcroForm::Form
def acro_form(create: false)
if (form = self[:AcroForm])
form
elsif create
form = self[:AcroForm] = document.add({}, type: :XXAcroForm)
form.set_default_appearance_string
form
end
end
# Returns the page labels number tree.
#
# * If a page labels number tree exists, the +create+ argument is not used.
#
# * If no page labels number tree exists and +create+ is +true+, a new one is created.
#
# * If no page labels number tree exists and +create+ is +false+, +nil+ is returned.
#
# See: HexaPDF::Document::Pages
def page_labels(create: false)
if (object = self[:PageLabels])
object
elsif create
self[:PageLabels] = document.wrap({}, type: NumberTreeNode)
end
end
private
# Ensures that there is a valid page tree.
def perform_validation(&block)
super
unless key?(:Pages)
yield("A PDF document needs a page tree", true)
value[:Pages] = document.add({Type: :Pages})
value[:Pages].validate(&block)
end
end
end
end
end