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'