diff --git a/MANIFEST b/MANIFEST
index 6dfd9301f..ae41ae5f1 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -984,6 +984,9 @@ t/complex/hyperchars.xml
t/complex/hypertest.pdf
t/complex/hypertest.tex
t/complex/hypertest.xml
+t/complex/hyperxmp.pdf
+t/complex/hyperxmp.tex
+t/complex/hyperxmp.xml
t/complex/labelled.pdf
t/complex/labelled.tex
t/complex/labelled.xml
diff --git a/lib/LaTeXML/Engine/Base_Schema.pool.ltxml b/lib/LaTeXML/Engine/Base_Schema.pool.ltxml
index 29d451396..f80d1bee9 100644
--- a/lib/LaTeXML/Engine/Base_Schema.pool.ltxml
+++ b/lib/LaTeXML/Engine/Base_Schema.pool.ltxml
@@ -81,6 +81,7 @@ RequireResource('LaTeXML.css');
"ma" => "http://www.w3.org/ns/ma-ont#",
"og" => "http://ogp.me/ns#",
"owl" => "http://www.w3.org/2002/07/owl#",
+ "prism" => "http://prismstandard.org/namespaces/basic/3.0/",
"rdf" => "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"rdfa" => "http://www.w3.org/ns/rdfa#",
"rdfs" => "http://www.w3.org/2000/01/rdf-schema#",
diff --git a/lib/LaTeXML/Package/hyperref.sty.ltxml b/lib/LaTeXML/Package/hyperref.sty.ltxml
index 820339cec..052647cc3 100644
--- a/lib/LaTeXML/Package/hyperref.sty.ltxml
+++ b/lib/LaTeXML/Package/hyperref.sty.ltxml
@@ -86,21 +86,19 @@ foreach my $option ( # 3.1 General Options
DeclareOption($option, undef); }
# \hypersetup{keyvals} configures various parameters,
-# for each pdf keyword, provide [property,(content|resource),datatype]
-our %pdfkey_property = (
- baseurl => '', # xmp:BaseURL ??
- pdfauthor => ['dcterms:creator', 'content'],
- pdfkeywords => ['dcterms:subject', 'content'], # & pdf:Keywords
- pdflang => ['dcterms:language', 'content'],
- pdfproducer => '', # pdf:Producer & xmp:CreatorTool
- pdfsubject => ['dcterms:subject', 'content'],
- pdftitle => ['dcterms:title', 'content'],
- # Include hyperxmp's keywords, as well.
- pdfauthortitle => '', # photoshop:AuthorsPosition
- pdfcaptionwriter => '', # photoshop:CaptionWriter !?!?!?
- pdfcopyright => ['dcterms:rights', 'content'], # & xmpRights:Marked
- pdflicenseurl => ['cc:licence', 'resource'], # xmpRights:WebStatement
- pdfmetalang => '', # dcterms:language ??
+# for each pdf keyword, provide [property,(content|resource),datatype,langsupport]
+# %pdfkey_property may have already been populated by hyperxmp
+our %pdfkey_property = (%pdfkey_property,
+ baseurl => '', # xmp:BaseURL ??
+ pdfauthor => ['dcterms:creator', 'content'],
+ pdfcreationdate => '', # xmp:CreateDate
+ pdfkeywords => ['dcterms:subject', 'content'], # & pdf:Keywords
+ pdflang => ['dcterms:language', 'content'],
+ pdfmoddate => '', # xmp:ModifyDate
+ pdfproducer => '', # pdf:Producer & xmp:CreatorTool
+ pdfsubject => ['dcterms:description', 'content'],
+ pdftitle => ['dcterms:title', 'content'],
+ pdftrapped => '', # pdf:Trapped
);
# date=>dcterms:date xmp:CreateDate xmp:ModifyDate xmp:MetadataDate ?
# document identifier => xmlMM:DocumentID
@@ -126,7 +124,7 @@ DefPrimitive('\hypersetup RequiredKeyVals:Hyp', sub {
my @pairs = $kv->getPairs;
while (@pairs) {
my ($key, $value) = (shift(@pairs), shift(@pairs));
- hyperref_setoption($key, Digest($value)); }
+ hyperref_setoption($key, Expand($value)); }
return; });
PushValue('@at@end@document', T_CS('\@add@PDF@RDFa@triples'));
@@ -134,13 +132,16 @@ PushValue('@at@end@document', T_CS('\@add@PDF@RDFa@triples'));
DefConstructor('\@add@PDF@RDFa@triples', sub {
my ($document, $xproperty, $content) = @_;
if (my $root = $document->getDocument->documentElement) {
+ my $metalang = LookupMapping('Hyperref_options', 'pdfmetalang') // LookupMapping('Hyperref_options', 'pdflang');
foreach my $key (LookupMappingKeys('Hyperref_options')) {
if (my $entry = ($pdfkey_property{$key})) {
- my ($property, $object, $datatype) = @$entry;
- my $value = LookupMapping('Hyperref_options', $key);
- my $node = $document->openElementAt($root, 'ltx:rdf',
- property => $property, $object => $value,
- ($datatype ? (datatype => $datatype) : ()));
+ my ($property, $object, $datatype, $langsupport) = @$entry;
+ my $value = ToString(LookupMapping('Hyperref_options', $key));
+ my ($lang, $localizedValue) = $value =~ m/^\[([^]]*)\](.*)/;
+ my $node = $document->openElementAt($root, 'ltx:rdf',
+ property => $property, $object => $langsupport ? $localizedValue // $value : $value,
+ ($langsupport && ($lang || $metalang) ? ('xml:lang' => $lang // $metalang) : ()),
+ ($datatype ? (datatype => $datatype) : ()));
# Must do directly; $document->setAttribute omits empty attributes
$node->setAttribute(about => '');
$document->closeElementAt($node); } } } });
diff --git a/lib/LaTeXML/Package/hyperxmp.sty.ltxml b/lib/LaTeXML/Package/hyperxmp.sty.ltxml
index 43700053a..8006a7e61 100644
--- a/lib/LaTeXML/Package/hyperxmp.sty.ltxml
+++ b/lib/LaTeXML/Package/hyperxmp.sty.ltxml
@@ -17,7 +17,63 @@ use LaTeXML::Package;
RequirePackage('ifthen');
-# Basically, the effects of hyperxmp are already built
-# into the LaTeXML binding for hyperref.
+# macro to pass alternate language entries
+# TODO support entries in multiple languages
+# TODO pdfmetalang should affect *following* entries, not all of them
+Let('\XMPLangAlt', '\@gobbletwo');
+
+# macros for including commas in comma-separated lists
+# TODO implement comma-separated lists splitting
+Let('\xmpquote', '\relax');
+DefMacroI('\xmpcomma', undef, ',');
+
+# %pdfkey_property may have already been populated by hyperref
+our %pdfkey_property = (%pdfkey_property,
+ # modify hyperref keywords to support specifying the language
+ pdfsubject => ['dcterms:description', 'content', undef, 1],
+ pdftitle => ['dcterms:title', 'content', undef, 1],
+ pdfaconformance => '', # pdfaid:conformance
+ pdfapart => '', # pdfaid:part
+ pdfauthortitle => '', # photoshop:AuthorsPosition
+ pdfbookedition => ['prism:bookEdition', 'content', undef, 1],
+ pdfbytes => ['prism:byteCount', 'content'],
+ pdfcaptionwriter => '', # photoshop:CaptionWriter
+ pdfcontactaddress => '', # Iptc4xmpCore:CiAdrExtadr
+ pdfcontactcity => '', # Iptc4xmpCore:CiAdrCity
+ pdfcontactcountry => '', # Iptc4xmpCore:CiAdrCtry
+ pdfcontactemail => '', # Iptc4xmpCore:CiEmailWork
+ pdfcontactphone => '', # Iptc4xmpCore:CiTelWork
+ pdfcontactpostcode => '', # Iptc4xmpCore:CiAdrPcode
+ pdfcontactregion => '', # Iptc4xmpCore:CiAdrRegion
+ pdfcontacturl => '', # Iptc4xmpCore:CiUrlWork
+ pdfcopyright => ['dcterms:rights', 'content', undef, 1], # & xmpRights:Marked, xmpRights:WebStatement
+ pdfdate => ['dcterms:date', 'content'], # (if missing, hyperxmp uses \date -- we delegate the default choice to postprocessing)
+ pdfdocumentid => '', # xmpMM:DocumentID
+ pdfdoi => ['prism:doi', 'content'],
+ pdfeissn => ['prism:eIssn', 'content'],
+ pdfidentifier => ['dcterms:identifier', 'content'], # (if missing, hyperxmp uses the first non-empty doi, eissn, issn, isbn with prefix info:doi/ or urn:ISS(B|N): -- we delegate the default choice to postprocessing)
+ pdfinstanceid => '', # xmpMM:InstanceID
+ pdfisbn => ['prism:isbn', 'content'],
+ pdfissn => ['prism:issn', 'content'],
+ pdfissuenum => ['prism:number', 'content'],
+ pdflicenseurl => ['cc:licence', 'resource'], # xmpRights:WebStatement
+ pdfmetadate => '', # xmp:MetadataDate
+ pdfmetalang => '', # the default language of the metadata entries themselves
+ pdfnumpages => ['prism:pageCount', 'content'], # xmpTPg:NPages
+ pdfpagerange => ['prism:pageRange', 'content'],
+ pdfpublication => ['prism:publicationName', 'content', undef, 1],
+ pdfpublisher => ['dcterms:publisher', 'content'],
+ pdfpubstatus => '', # jav:journal_article_version
+ pdfpubtype => ['prism:aggregationType', 'content'],
+ pdfrendition => '', # xmpMM:RenditionClass
+ pdfsource => ['dcterms:source', 'content'],
+ pdfsubtitle => ['prism:subtitle', 'content', undef, 1],
+ pdftype => ['dcterms:type', 'content'],
+ pdfuapart => '', # pdfuaid:part
+ pdfurl => ['prism:url', 'content'],
+ pdfversionid => '', # xmpMM:VersionID
+ pdfvolumenum => ['prism:volume', 'content'],
+ pdfxstandard => '', # {pdfx,pdfxid}:GTS_PDFXVersion
+);
1;
diff --git a/t/80_complex.t b/t/80_complex.t
index e9bd5aea5..4bd80250c 100644
--- a/t/80_complex.t
+++ b/t/80_complex.t
@@ -9,6 +9,7 @@ latexml_tests("t/complex",
cleveref_minimal => 'cleveref.sty',
figure_dual_caption => {packages => 'graphicx.sty', texlive_min => 2021},
figure_mixed_content => {packages => 'graphicx.sty', texlive_min => 2021},
+ hyperxmp => 'babel.sty',
si => {
env=>'CI', # only runs in continuous integration
packages => 'siunitx.sty', texlive_min => 2015 } });
diff --git a/t/complex/hypertest.xml b/t/complex/hypertest.xml
index 8aa242b17..b760145d1 100644
--- a/t/complex/hypertest.xml
+++ b/t/complex/hypertest.xml
@@ -50,6 +50,6 @@
-
+
diff --git a/t/complex/hyperxmp.pdf b/t/complex/hyperxmp.pdf
new file mode 100644
index 000000000..4fd32a816
Binary files /dev/null and b/t/complex/hyperxmp.pdf differ
diff --git a/t/complex/hyperxmp.tex b/t/complex/hyperxmp.tex
new file mode 100644
index 000000000..5049af6cb
--- /dev/null
+++ b/t/complex/hyperxmp.tex
@@ -0,0 +1,64 @@
+% example copied from hyperxmp with minor modifications
+\pdfobjcompresslevel=0 % added to easily inspect the XMP packet
+\documentclass[british]{article} % added british to test pdflang and pdfmetalang
+\usepackage[utf8]{inputenc}
+\usepackage{babel}
+\usepackage[unicode]{hyperref}
+\usepackage{hyperxmp}
+%
+\title{%
+ On a heuristic viewpoint concerning the production and
+ transformation of light}
+\author{Albert Einstein}
+\date{March 17, 1905}
+%
+\hypersetup{%
+ pdfmetalang={en-AU}, % added to test pdfmetalang
+ pdftitle={%
+ On a heuristic viewpoint concerning the production and
+ transformation of light},
+ pdfsubtitle={[en-US]Putting that bum Maxwell in his place},
+ pdfauthor={Albert Einstein},
+ pdfauthortitle={\xmpquote{Technical Assistant\xmpcomma\ Level III}},
+ pdfdate={1905-03-17},
+ pdfcopyright={Copyright (C) 1905, Albert Einstein},
+ pdfsubject={photoelectric effect},
+ pdfkeywords={energy quanta, Hertz effect, quantum physics},
+ pdflicenseurl={http://creativecommons.org/licenses/by-nc-nd/3.0/},
+ pdfcaptionwriter={Scott Pakin},
+ pdfcontactaddress={Kramgasse 49},
+ pdfcontactcity={Bern},
+ pdfcontactpostcode={3011},
+ pdfcontactcountry={Switzerland},
+ pdfcontactphone={031 312 00 91},
+ pdfcontactemail={aeinstein@ipi.ch},
+ pdfcontacturl={%
+ http://einstein.biz/,
+ https://www.facebook.com/AlbertEinstein
+ },
+ pdfdocumentid={uuid:6d1ac9ec-4ff2-515a-954b-648eeb4853b0},
+ pdfversionid={2.998e8},
+ pdfpublication={[de]Annalen der Physik},
+ pdfpublisher={Wiley-VCH},
+ pdfpubtype={journal},
+ pdfvolumenum={322},
+ pdfissuenum={6},
+ pdfpagerange={132-148},
+ pdfissn={0003-3804},
+ pdfeissn={1521-3889},
+ pdfpubstatus={VoR},
+ pdflang={en-GB},
+ pdfurl={http://www.physik.uni-augsburg.de/annalen/history/einstein-papers/1905_17_132-148.pdf},
+ pdfdoi={10.1002/andp.19053220607},
+ pdfidentifier={info:lccn/50013519}
+}
+\XMPLangAlt{de}{pdftitle={Über einen die Erzeugung und Verwandlung des
+ Lichtes betreffenden heuristischen Gesichtspunkt}}
+%
+\begin{document}
+\maketitle
+A profound formal difference exists between the theoretical
+concepts that physicists have formed about gases and other
+ponderable bodies, and Maxwell's theory of electromagnetic
+processes in so-called empty space\dots
+\end{document}
diff --git a/t/complex/hyperxmp.xml b/t/complex/hyperxmp.xml
new file mode 100644
index 000000000..06292859a
--- /dev/null
+++ b/t/complex/hyperxmp.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+ On a heuristic viewpoint concerning the production and
+transformation of light
+
+ Albert Einstein
+
+ March 17, 1905
+
+
A profound formal difference exists between the theoretical
+concepts that physicists have formed about gases and other
+ponderable bodies, and Maxwell’s theory of electromagnetic
+processes in so-called empty space…