lib/hexapdf/document/metadata.rb in hexapdf-0.37.2 vs lib/hexapdf/document/metadata.rb in hexapdf-0.38.0

- old
+ new

@@ -78,10 +78,14 @@ # The following types for XMP properties are supported: # # String:: # Maps to the XMP simple string value. Values need to be of type String. # + # Integer:: + # Maps to the XMP integer core value type and gets formatted as string. Values need to be of + # type Integer. + # # Date:: # Maps to the XMP simple string value, correctly formatted. Values need to be of type Time, # Date, or DateTime # # URI:: @@ -121,10 +125,11 @@ "rdf" => "http://www.w3.org/1999/02/22-rdf-syntax-ns#", "xmp" => "http://ns.adobe.com/xap/1.0/", "pdf" => "http://ns.adobe.com/pdf/1.3/", "dc" => "http://purl.org/dc/elements/1.1/", "x" => "adobe:ns:meta/", + "pdfaid" => "http://www.aiim.org/pdfa/ns/id/", }.freeze # Contains a mapping of predefined XMP properties to their types, i.e. from namespace to # property and then type. PREDEFINED_PROPERTIES = { @@ -141,18 +146,22 @@ "http://purl.org/dc/elements/1.1/" => { 'creator' => 'OrderedArray', 'description' => 'LanguageArray', 'title' => 'LanguageArray', }.freeze, + "http://www.aiim.org/pdfa/ns/id/" => { + 'part' => 'Integer', + 'conformance' => 'String', + }.freeze, }.freeze # Creates a new Metadata object for the given PDF document. def initialize(document) @document = document @namespaces = PREDEFINED_NAMESPACES.dup @properties = PREDEFINED_PROPERTIES.transform_values(&:dup) - @default_language = document.catalog[:Lang] || 'en' + @default_language = document.catalog[:Lang] || 'x-default' @metadata = Hash.new {|h, k| h[k] = {} } write_info_dict(true) write_metadata_stream(true) @document.register_listener(:complete_objects, &method(:write_metadata)) parse_metadata @@ -164,11 +173,11 @@ # # Returns the default language in RFC3066 format used for unlocalized strings if no argument # is given. Otherwise sets the default language to the given language. # # The initial default lanuage is taken from the document catalog's /Lang entry. If that is not - # set, the default language is assumed to be English ('en'). + # set, the default language is assumed to be default language ('x-default'). def default_language(value = :UNSET) if value == :UNSET @default_language else @default_language = value @@ -211,12 +220,12 @@ end end # Registers the +property+ for the namespace specified via +prefix+ as the given +type+. # - # The argument +type+ has to be one of the following: 'String', 'Date', 'URI', 'Boolean', - # 'OrderedArray', 'UnorderedArray', or 'LanguageArray'. + # The argument +type+ has to be one of the following: 'String', 'Integer', 'Date', 'URI', + # 'Boolean', 'OrderedArray', 'UnorderedArray', or 'LanguageArray'. def register_property_type(prefix, property, type) (@properties[namespace(prefix)] ||= {})[property] = type end # :call-seq: @@ -238,17 +247,35 @@ ns[property] = value end end # :call-seq: - # metadata.title -> title or nil - # metadata.title(value -> value + # metadata.delete + # metadata.delete(ns_prefix) + # metadata.delete(ns_prefix, name) # + # Deletes either all metadata properties, only the ones from a specific namespace, or a + # specific one. + def delete(ns = nil, property = nil) + if ns.nil? && property.nil? + @metadata.clear + elsif property.nil? + @metadata.delete(namespace(ns)) + else + @metadata[namespace(ns)].delete(property) + end + end + + # :call-seq: + # metadata.title -> title or nil + # metadata.title(value) -> value + # # Returns the document's title if no argument is given. Otherwise sets the document's title to # the given value. # - # The language for the title is specified via #default_language. + # If the +value+ is a LocalizedString, the language for the title is taken from it. Otherwise + # the language specified via #default_language is used. # # The value +nil+ is returned if the property is not set. And by using +nil+ as +value+ the # property is deleted from the metadata. # # This metadata property is represented by the XMP name dc:title. @@ -276,11 +303,12 @@ # metadata.subject(value) -> value # # Returns the subject of the document if no argument is given. Otherwise sets the subject to # the given value. # - # The language for the subject is specified via #default_language. + # If the +value+ is a LocalizedString, the language for the subject is taken from it. + # Otherwise the language specified via #default_language is used. # # The value +nil+ is returned if the property ist not set. And by using +nil+ as +value+ the # property is deleted from the metadata. # # This metadata property is represented by the XMP name dc:description. @@ -404,49 +432,58 @@ def write_metadata ns_dc = namespace('dc') ns_xmp = namespace('xmp') ns_pdf = namespace('pdf') + producer("HexaPDF version #{HexaPDF::VERSION}") + if write_info_dict? info_dict = @document.trailer.info info_dict[:Title] = Array(@metadata[ns_dc]['title']).first - info_dict[:Author] = Array(@metadata[ns_dc]['creator']).join(', ') + if @metadata[ns_dc].key?('creator') + info_dict[:Author] = Array(@metadata[ns_dc]['creator']).join(', ') + end info_dict[:Subject] = Array(@metadata[ns_dc]['description']).first info_dict[:Creator] = @metadata[ns_xmp]['CreatorTool'] info_dict[:CreationDate] = @metadata[ns_xmp]['CreateDate'] info_dict[:ModDate] = @metadata[ns_xmp]['ModifyDate'] info_dict[:Keywords] = @metadata[ns_pdf]['Keywords'] info_dict[:Producer] = @metadata[ns_pdf]['Producer'] - info_dict[:Trapped] = @metadata[ns_pdf]['Trapped'] ? :True : :False + if @metadata[ns_pdf].key?('Trapped') + info_dict[:Trapped] = @metadata[ns_pdf]['Trapped'] ? :True : :False + end end if write_metadata_stream? descriptions = @metadata.map do |namespace, values| + next if values.empty? xmp_description(@namespaces.key(namespace), values) - end.join("\n") + end.compact.join("\n") obj = @document.catalog[:Metadata] ||= @document.add({Type: :Metadata, Subtype: :XML}) obj.stream = xmp_packet(descriptions) end end # Creates an XMP packet with the given payload +data+. def xmp_packet(data) <<~XMP <?xpacket begin="\u{FEFF}" id="#{SecureRandom.uuid.tr('-', '')}"?> + <x:xmpmeta xmlns:x="adobe:ns:meta/"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> #{data} </rdf:RDF> + </x:xmpmeta> <?xpacket end="r"?> XMP end # Creates an 'rdf:Description' element for all metadata +values+ with the given +ns_prefix+. def xmp_description(ns_prefix, values) values = values.map do |name, value| str = +"<#{ns_prefix}:#{name}" case (property_type = @properties[namespace(ns_prefix)][name]) - when 'String' - str << ">#{xmp_escape(value)}</#{ns_prefix}:#{name}>" + when 'String', 'Integer' + str << ">#{xmp_escape(value.to_s)}</#{ns_prefix}:#{name}>" when 'Date' str << ">#{xmp_date(value)}</#{ns_prefix}:#{name}>" when 'URI' str << " rdf:resource=\"#{xmp_escape(value.to_s)}\" />" when 'Boolean'