# -*- 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 . #++ module HexaPDF module Utils # There are some structures in a PDF file, for example cross reference tables, that index data # based on object and generation numbers. However, there is a restriction that in such # structures the object numbers must be unique, e.g. there may not be entries for [1, 0] and # \[1, 1] at the same time. # # This class can be used for storing/retrieving data for such structures. class ObjectHash include Enumerable # The biggest object number that is stored in the object hash or zero if no objects are # stored. attr_reader :max_oid # Creates a new object hash. def initialize @table = {} @oids = {} @max_oid = 0 end # :call-seq: # objhash[oid, gen] = data # # Sets the data for the given object and generation numbers. # # If there is already an entry for the given object number (even if the generation number is # different), this entry will be removed. def []=(oid, gen, data) @table[oid] = data @oids[oid] = gen @max_oid = oid if oid > @max_oid end # :call-seq: # objhash[oid] -> data or nil # objhash[oid, gen] -> data or nil # # Returns the data for the given object number, or for the given object and generation # numbers. # # If there is no such data, +nil+ is returned. def [](oid, gen = nil) (gen.nil? || gen_for_oid(oid) == gen || nil) && @table[oid] end # :call-seq: # objhash.gen_for_oid(oid) -> Integer or nil # # Returns the generation number that is stored along the given object number, or +nil+ if # the object number is not used. def gen_for_oid(oid) @oids[oid] end # :call-seq: # objhash.entry?(oid) -> true or false # objhash.entry?(oid, gen) -> true or false # # Returns +true+ if there is an entry for the given object number, or for the given object # and generation numbers. def entry?(oid, gen = nil) (gen ? gen_for_oid(oid) == gen : @oids.key?(oid)) end # Deletes the entry for the given object number. def delete(oid) @table.delete(oid) @oids.delete(oid) @max_oid = oids.max || 0 if oid == @max_oid end # :call-seq: # objhash.each {|oid, gen, data| block } -> objhash # objhash.each -> Enumerator # # Calls the given block once for every entry, passing an array consisting of the object and # generation number and the associated data as arguments. def each return to_enum(__method__) unless block_given? @oids.keys.each {|oid| yield(oid, @oids[oid], @table[oid]) if @table.key?(oid) } self end # Returns all used object numbers as an array. def oids @oids.keys end end end end