From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Mon, 2 Sep 2019 13:46:59 +0900 Subject: Initial commit --- macros/latex/contrib/hyperxmp/hyperxmp.dtx | 5055 ++++++++++++++++++++++++++++ 1 file changed, 5055 insertions(+) create mode 100644 macros/latex/contrib/hyperxmp/hyperxmp.dtx (limited to 'macros/latex/contrib/hyperxmp/hyperxmp.dtx') diff --git a/macros/latex/contrib/hyperxmp/hyperxmp.dtx b/macros/latex/contrib/hyperxmp/hyperxmp.dtx new file mode 100644 index 0000000000..a5aa24a146 --- /dev/null +++ b/macros/latex/contrib/hyperxmp/hyperxmp.dtx @@ -0,0 +1,5055 @@ +% \iffalse meta-comment +% +% Copyright (C) 2019 by Scott Pakin +% ------------------------------------------------------- +% +% This file may be distributed and/or modified under the +% conditions of the LaTeX Project Public License, either version 1.3c +% of this license or (at your option) any later version. +% The latest version of this license is in: +% +% http://www.latex-project.org/lppl.txt +% +% and version 1.3c or later is part of all distributions of LaTeX +% version 2008/05/04 or later. +% +% \fi +% +% \iffalse +%<*driver> +\ProvidesFile{hyperxmp.dtx} +% +%\NeedsTeXFormat{LaTeX2e}[1999/12/01] +%\ProvidesPackage{hyperxmp} +%<*package> + [2019/04/05 v4.1 Store hyperref metadata in XMP format] +% +% +%<*driver> +\bgroup + \catcode`\&=11 + \gdef\levelchar{&} +\egroup +\documentclass{ltxdoc} +\usepackage{graphicx} +\usepackage{color} +\usepackage{tocbibind} +\usepackage{microtype} +\usepackage{needspace} +\usepackage{varioref} +\usepackage{alltt} +\usepackage{multicol} +\usepackage{makeidx} +\usepackage[utf8]{inputenc} +\usepackage{hyperxmp} +\usepackage[unicode,bookmarksopen,bookmarksopenlevel=2,bookmarksnumbered]{hyperref} +\EnableCrossrefs +\CodelineIndex +\RecordChanges + +% Specify this document's metadata. +\GetFileInfo{hyperxmp.dtx} +\title{The \pkgname{hyperxmp} package\thanks{This document + corresponds to \pkgname{hyperxmp}~\fileversion, dated \filedate.}} +\author{Scott Pakin \\ \texttt{scott+hyxmp@pakin.org}} +\makeatletter +\edef\versionnumber{\expandafter\@gobble\fileversion} % Drop the "v" in the file version. +\makeatother +\hypersetup{% + pdfauthor={Scott Pakin}, + pdftitle={The hyperxmp package}, + pdfsubject={LaTeX2e support for XMP metadata}, + pdfkeywords={LaTeX, embedded metadata, XMP, PDF, copyright, license, comments}, + pdfcopyright={Copyright (C) 2019, Scott Pakin}, + pdflicenseurl={http://www.latex-project.org/lppl/}, + pdfcaptionwriter={Scott Pakin}, + pdfcontactemail={scott+hyxmp@pakin.org}, + pdfcontacturl={http://www.pakin.org/\xmptilde scott/}, + pdfversionid={\versionnumber}, + pdflang={en-US}, + pdfurl={http://mirror.ctan.org/macros/latex/contrib/hyperxmp/hyperxmp.pdf}, + baseurl={http://mirror.ctan.org/macros/latex/contrib/hyperxmp/} +} + +\begin{document} + \DocInput{hyperxmp.dtx} + \Needspace{10\baselineskip} + \phantomsection\addcontentsline{toc}{section}{Change History} + \PrintChanges + \makeatletter + \let\orig@index@prologue=\index@prologue + \def\index@prologue{% + \phantomsection\addcontentsline{toc}{section}{Index} + \orig@index@prologue + }% + \makeatother + \Needspace{12\baselineskip} + \PrintIndex +\end{document} +% +% \fi +% +% \CheckSum{2095} +% +% \CharacterTable +% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z +% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z +% Digits \0\1\2\3\4\5\6\7\8\9 +% Exclamation \! Double quote \" Hash (number) \# +% Dollar \$ Percent \% Ampersand \& +% Acute accent \' Left paren \( Right paren \) +% Asterisk \* Plus \+ Comma \, +% Minus \- Point \. Solidus \/ +% Colon \: Semicolon \; Less than \< +% Equals \= Greater than \> Question mark \? +% Commercial at \@ Left bracket \[ Backslash \\ +% Right bracket \] Circumflex \^ Underscore \_ +% Grave accent \` Left brace \{ Vertical bar \| +% Right brace \} Tilde \~} +% +% +% \changes{v1.0}{2006/05/14}{Initial version} +% \changes{v2.0}{2012/08/02}{Heiko Oberdiek's major rewrite of the code +% to better support native-Unicode \string\TeX\ implementations +% (\string\XeTeX\ and \string\LuaTeX)} +% \changes{v2.0}{2012/08/26}{Added support for the \protect\acro{XMP} Basic +% schema and miscellaneous other bits of metadata} +% \changes{v1.2}{2010/06/04}{Added support for the \protect\XeTeX\ backend +% (\texttt{xdvipdfmx})} +% \changes{v1.2}{2010/06/07}{Added support for the Photoshop schema} +% \changes{v2.2}{2010/12/06}{Added support for the \protect\acro{IPTC} Photo Metadata schema} +% \changes{v2.4}{2013/12/21}{Added support for the \protect\acro{PDF/A} +% Identification schema, as requested by Florian Breitwieser} +% \changes{v2.5}{2014/06/19}{Enabled ``\texttt{\string\string\string\_}'' +% to work within email addresses, as requested by Leonid Sinev} +% \changes{v2.6}{2014/09/24}{Added support for a new \protect\optname{pdfdate} +% key to explicitly specify the document date (and optionally time)} +% \changes{v2.9}{2016/04/25}{Force inclusion of \protect\xmpprop{dc:creator}, +% \protect\xmpprop{dc:title}, and \protect\xmpprop{dc:description}---even if +% empty---when \protect\pkgname{hyperref} is loaded with the +% \protect\optname{pdfa} option (suggested by Leonid Sinev)} +% \changes{v2.9}{2016/04/26}{Introduced the \protect\optname{pdftype} +% package option, which enables an author to specify the type of +% document being produced} +% \changes{v3.0}{2016/07/02}{Made the code compatible with \string\LuaTeX~0.85. +% Thanks to Robert Schlicht, Leonid Sinev, and David Carlisle for bug +% reports and to Leonid Sinev for helping test the new +% \protect\pkgname{hyperxmp} code} +% \changes{v3.4}{2017/11/04}{Use \protect\pkgname{ifmtarg} to test for +% empty arguments, including non-empty but all spaces} +% \changes{v4.0}{2019/03/14}{Include all metadata within a single +% \protect\xmpterm{rdf:Description} block} +% \changes{v4.1}{2019/04/05}{Updated the documentation to refer to +% \protect\cs{pdfnumpages} by its correct name. Thanks to Volker RW Schaa +% for catching the discrepancy} +% +% ^^A \GetFileInfo{hyperxmp.dtx} +% +% \DoNotIndex{\,,\&,\ ,\!,\",\#,\(,\),\*,\<,\>,\\,\^,\~,\^,\_} +% \DoNotIndex{\@cons,\@empty,\@firstofone,\@firstoftwo,\@gobble,\@gobbletwo} +% \DoNotIndex{\@ifpackageloaded,\@ifundefined,\@nil,\@secondoftwo} +% \DoNotIndex{\@tempcnta,\@tempcntb,\MessageBreak,\advance,\afterassignment} +% \DoNotIndex{\aftergroup,\begin,\begingroup,\bgroup,\catcode,\csname,\def} +% \DoNotIndex{\divide,\edef,\egroup,\else,\end,\endcsname,\endgroup} +% \DoNotIndex{\expandafter,\fi,\futurelet,\g@addto@macro,\gdef,\global,\if} +% \DoNotIndex{\ifcase,\ifnum,\ifx,\immediate,\lccode,\let,\loop,\lowercase} +% \DoNotIndex{\multiply,\newcommand,\noexpand,\or,\relax,\repeat,\space} +% \DoNotIndex{\string,\the,\toks,\uccode,\uppercase,\usepackage,\xdef} +% +% ^^A Define a few logical styles. +% \DeclareRobustCommand{\term}[1]{#1\SortIndex{#1}{#1}} +% \DeclareRobustCommand{\pkgname}[1]{\mbox{\textsf{#1}}\SortIndex{#1}{\textsf{#1}}} +% \makeatletter +% \DeclareRobustCommand{\xmpprop}[2][]{^^A XMP property +% \def\xmppropopt{#1}^^A +% \ifx\xmppropopt\@empty +% \mbox{\textsf{#2}}^^A +% \else +% \mbox{\textsf{#2}}.\mbox{\textsf{#1}}^^A +% \SortIndex{#1}{\textsf{#1}}^^A +% \fi +% \SortIndex{#2}{\textsf{#2}}^^A +% \index{properties, \acrostyle{XMP}=\textsf{#2}}^^A +% } +% \DeclareRobustCommand{\xmpterm}[1]{^^A All other XMP terms +% \mbox{\textsf{#1}}^^A +% \SortIndex{#1}{\textsf{#1}}^^A +% } +% \makeatother +% \index{XMP=\acrostyle {XMP}&properties|see{properties, \acrostyle{XMP}}} +% \DeclareRobustCommand{\pdfterm}[1]{\mbox{\textsf{#1}}\SortIndex{#1}{\textsf{#1}}} +% \DeclareRobustCommand{\cmdname}[1]{\mbox{\texttt{#1}}\SortIndex{#1}{\texttt{#1}}} +% \DeclareRobustCommand{\optname}[1]{^^A +% \mbox{\textsf{#1}}^^A +% \SortIndex{#1}{\textsf{#1} (option)}^^A +% \index{options=\textsf{#1}}^^A +% } +% ^^A \moptname is the same as \optname but additionally typesets the +% ^^A option name in the margin. +% \DeclareRobustCommand{\moptname}[1]{^^A +% \optname{#1}^^A +% \marginpar{\raggedleft\strut\mbox{\textsf{#1}}}^^A +% } +% \DeclareRobustCommand{\acrostyle}[1]{\textsc{\MakeLowercase{#1}}} +% \DeclareRobustCommand{\acro}[1]{^^A +% \mbox{\acrostyle{#1}}^^A +% \SortIndex{#1}{\acrostyle{#1}}^^A +% } +% \newcounter{note} +% \newcommand{\usagenote}[1]{^^A +% \refstepcounter{note}^^A +% \paragraph{Note~\thenote: #1}% +% } +% +% ^^A Define some other shortcut macros. +% \DeclareRobustCommand{\tex}{^^A +% \texorpdfstring{\TeX\SortIndex{TeX}{\TeX}}{TeX}^^A +% } +% \DeclareRobustCommand{\XeTeXlogo}{^^A +% X\lower0.5ex\hbox{\kern-.15em\reflectbox{E}}\kern-0.1667em\TeX +% } +% \DeclareRobustCommand{\XeTeX}{^^A +% \texorpdfstring{\XeTeXlogo\SortIndex{XeTeX}{\XeTeXlogo}}{XeTeX}^^A +% } +% \DeclareRobustCommand{\XeLaTeXlogo}{^^A +% X\lower0.5ex\hbox{\kern-.15em\reflectbox{E}}\kern-0.1667em\LaTeX +% } +% \DeclareRobustCommand{\XeLaTeX}{^^A +% \texorpdfstring{\XeLaTeXlogo\SortIndex{XeLaTeX}{\XeLaTeXlogo}}{XeLaTeX}^^A +% } +% \DeclareRobustCommand{\LuaTeXlogo}{Lua\TeX} +% \DeclareRobustCommand{\LuaTeX}{^^A +% \texorpdfstring{\LuaTeXlogo\SortIndex{LuaTeX}{\LuaTeXlogo}}{LuaTeX}^^A +% } +% \DeclareRobustCommand{\LuaLaTeXlogo}{Lua\LaTeX} +% \DeclareRobustCommand{\LuaLaTeX}{^^A +% \texorpdfstring{\LuaLaTeXlogo\SortIndex{LuaLaTeX}{\LuaLaTeXlogo}}{LuaLaTeX}^^A +% } +% \DeclareRobustCommand{\pdfTeXlogo}{pdf\TeX} +% \DeclareRobustCommand{\pdfTeX}{^^A +% \texorpdfstring{\pdfTeXlogo\SortIndex{pdfTeX}{\pdfTeXlogo}}{pdfTeX}^^A +% } +% \DeclareRobustCommand{\pdfLaTeXlogo}{pdf\LaTeX} +% \DeclareRobustCommand{\pdfLaTeX}{^^A +% \texorpdfstring{\pdfLaTeXlogo\SortIndex{pdfLaTeX}{\pdfLaTeXlogo}}{pdfLaTeX}^^A +% } +% \DeclareRobustCommand{\Dvips}{^^A +% \texorpdfstring{Dvips\SortIndex{dvips}{\texttt{dvips}}}{Dvips}^^A +% } +% \DeclareRobustCommand{\Lua}{^^A +% Lua\index{Lua}^^A +% } +% +% ^^A Define an environment just like macro but for Lua functions. +% \makeatletter +% \newenvironment{luafunc}{^^A +% \begingroup +% \catcode`\_=11 +% \def\PrintMacroName##1{\strut\MacroFont\string##1\ }^^A +% \def\SpecialIndex@##1##2{^^A +% \@bsphack +% \special@index{\string##1\actualchar +% \string\verb\quotechar*\verbatimchar##1\verbatimchar##2}^^A +% \@esphack +% }^^A +% \begin{macro}^^A +% }{^^A +% \end{macro}^^A +% \endgroup +% } +% \makeatother +% +% ^^A Pack figures a bit tighter onto the page. +% \renewcommand{\floatpagefraction}{0.8} +% +% ^^A Help \pageref refer to arbitrary content. +% \newcounter{pagelabel} +% +% \maketitle +% \sloppy +% +% \begin{abstract} +% \pkgname{hyperxmp} makes it easy for an author to include \acro{XMP} +% metadata in a \acro{PDF} document produced by \LaTeX\@. \pkgname{hyperxmp} +% integrates seamlessly with \pkgname{hyperref} and requires virtually +% no modifications to a document that already specifies document +% metadata through \pkgname{hyperref}'s mechanisms. +% \end{abstract} +% +% +% \section{Introduction} +% +% Adobe Systems, Inc.\ has been promoting +% \acro{XMP}~\cite{Adobe2012:XMP}---eXtensible Metadata Platform---as a +% standard way to include metadata within a document. The idea behind +% \acro{XMP} is that it is an \acro{XML}-based description of various +% document attributes and is embedded as uncompressed, unencoded text +% within the document it describes. By storing the metadata this way it +% is independent of the document's file format. That is, regardless of +% whether a document is in \acro{PDF}, \acrostyle{JPEG}, +% \acrostyle{HTML}, or any other format, it is trivial for a program (or +% human) to locate, extract, and---using any standard \acro{XML} +% parser---process the embedded \acro{XMP} metadata. +% +% As of this writing there are few tools that actually do process +% \acro{XMP}\@. However, it is easy to imagine future support existing +% in file browsers for displaying not only a document's filename but +% also its title, list of authors, description, and other metadata. +% +% \paragraph{This is too abstract! Give me an example.} +% Consider a \LaTeX\ document with three authors: Jack Napier, Edward +% Nigma, and Harvey Dent. The generated \acro{PDF} file will contain, +% among other information, the following stanza of \acro{XMP} code +% embedded within it: +% +% \begin{verbatim} +% +% +% Jack Napier +% Edward Nigma +% Harvey Dent +% +% +% \end{verbatim} +% +% In the preceding code, the |dc| namespace refers to the +% \href{http://purl.org/DC/}{Dublin Core schema}, a collection of +% metadata properties. The \xmpprop{dc:creator} property surrounds the +% list of authors. The \textsf{rdf} namespace is the +% \href{http://www.w3.org/RDF/}{Resource Description Framework}, which +% defines \xmpterm{rdf:Seq} as an ordered list of values. Each author +% is represented by an individual list item (\xmpterm{rdf:li}), making +% it easy for an \acro{XML} parser to separate the authors' names. +% +% Remember that \acro{XMP} code is stored as \emph{metadata}. It does +% not appear when viewing or printing the \acro{PDF} file. Rather, it +% is intended to make it easy for applications to identify and +% categorize the document. +% +% \paragraph{What metadata does \textsf{hyperxmp} process?} +% \pkgname{hyperxmp} knows how to embed all of the following types of +% metadata within a document: +% +% \label{page:begin-metadata-list} +% \begin{itemize} \raggedright +% \item address of primary author +% (\xmpprop[CiAdrExtadr]{Iptc4xmpCore:CreatorContactInfo}, +% \xmpprop[CiAdrCity]{Iptc4xmpCore:CreatorContactInfo}, +% \xmpprop[CiAdrRegion]{Iptc4xmpCore:CreatorContactInfo}, +% \xmpprop[CiAdrPcode]{Iptc4xmpCore:CreatorContactInfo}, and +% \xmpprop[CiAdrCtry]{Iptc4xmpCore:CreatorContactInfo}) +% \item author(s) (\xmpprop{dc:creator}) +% \item base \acro{URL} for relative references (\xmpprop{xmp:BaseURL}) +% \item book edition (\xmpprop{prism:bookEdition}) +% \item copyright (\xmpprop{dc:rights} and \xmpprop{xmpRights:Marked}) +% \item date (\xmpprop{dc:date}, \xmpprop{xmp:CreateDate}, +% \xmpprop{xmp:ModifyDate}, and \xmpprop{xmp:MetadataDate}) +% \item \acro{DOI} (\xmpprop{prism:doi}) +% \item email address(es) of primary author (\xmpprop[CiEmailWork]{Iptc4xmpCore:CreatorContactInfo}) +% \item file format (\xmpprop{dc:format}) +% \item file name of main \LaTeX\ source file (\xmpprop{dc:source}) +% \item file size in bytes (\xmpprop{prism:byteCount}) +% \item \acro{ISBN} (\xmpprop{prism:isbn}) +% \item \acro{ISSN}---both print (\xmpprop{prism:issn}) and electronic (\xmpprop{prism:eIssn}) +% \item issue number of parent publication (\xmpprop{prism:number}) +% \item keywords (\xmpprop{pdf:Keywords} and \xmpprop{dc:subject}) +% \item language used (\xmpprop{dc:language}) +% \item license \acro{URL} (\xmpprop{xmpRights:WebStatement}) +% \item metadata writer (\xmpprop{photoshop:CaptionWriter}) +% \item page count (\xmpprop{prism:pageCount}) +% \item page range(s) (\xmpprop{prism:pageRange}) +% \item \acro{PDF} version (\xmpprop{pdf:PDFVersion}) +% \item \acro{PDF}-generating tool (\xmpprop{pdf:Producer} and \xmpprop{xmp:CreatorTool}) +% \item \acro{PDF/A} compliance level and version (\xmpprop{pdfaid:part} and \xmpprop{pdfaid:conformance}) +% \item position/title of primary author (\xmpprop{photoshop:AuthorsPosition}) +% \item publication name of parent publication (\xmpprop{prism:publicationName}) +% \item publisher of the document (\xmpprop{dc:publisher}) +% \item summary (\xmpprop{dc:description}) +% \item subtitle (\xmpprop{prism:subtitle}) +% \item telephone number(s) of primary author (\xmpprop[CiTelWork]{Iptc4xmpCore:CreatorContactInfo}) +% \item title (\xmpprop{dc:title}) +% \item type of document (\xmpprop{dc:type}) +% \item type of parent publication (\xmpprop{prism:aggregationType}) +% \item \acro{URL} of the document (\xmpprop{prism:url}) +% \item \acro{URL}(s) of the primary author (\xmpprop[CiUrlWork]{Iptc4xmpCore:CreatorContactInfo}) +% \item \acro{UUID} for the document (\xmpprop{xmpMM:DocumentID}) +% \item \acro{UUID} for the document instance (\xmpprop{xmpMM:InstanceID}) +% \item version identifier for the document (\xmpprop{xmpMM:VersionID}) +% \item volume number of parent publication (\xmpprop{prism:volume}) +% \end{itemize} +% \label{page:end-metadata-list} +% +% \noindent +% More types of metadata may be added in a future release. +% +% \paragraph{How does \textsf{hyperxmp} compare to the \textsf{xmpincl} +% package?} +% The short answer is that \pkgname{xmpincl} is more flexible but +% \pkgname{hyperxmp} is easier to use. With \pkgname{xmpincl}, the +% author manually constructs a file of arbitrary \acro{XMP} data and the +% package merely embeds it within the generated \acro{PDF} file. With +% \pkgname{hyperxmp}, the author specifies values for various predefined +% metadata types and the package formats those values as \acro{XMP} and embeds +% the result within the generated \acro{PDF} file. +% +% \pkgname{xmpincl} can embed \acro{XMP} only when running under \pdfLaTeX\ and +% only when in \acro{PDF}-generating mode. \pkgname{hyperxmp} additionally +% works with a few other \acro{PDF}-producing \LaTeX\ backends. +% +% \pkgname{hyperxmp} and \pkgname{xmpincl} can complement each other. +% An author may want to use \pkgname{hyperxmp} to produce a basic set of +% \acro{XMP} code, then extract the \acro{XMP} code from the \acro{PDF} file with a text +% editor, augment the \acro{XMP} code with any metadata not supported by +% \pkgname{hyperxmp}, and use \pkgname{xmpincl} to include the modified +% \acro{XMP} code in the \acro{PDF} file. +% +% \section{Usage} +% +% \pkgname{hyperxmp} works by postprocessing some of the package options +% honored by \pkgname{hyperref}. To use \pkgname{hyperxmp}, merely put +% a |\usepackage{hyperxmp}| in your document's preamble. That line can +% appear anywhere before the \pkgname{hyperref} \acro{PDF} options are +% specified (i.e.,~with either |\usepackage[|\dots|]{hyperref}| or +% |\hypersetup{|\dots|}|). \pkgname{hyperxmp} will construct its +% \acro{XMP} data using the following \pkgname{hyperref} options: +% +% \begin{multicols}{3} +% \raggedcolumns +% \begin{itemize} +% \item \optname{baseurl} +% \item \optname{pdfauthor} +% \item \optname{pdfcreationdate} +% \item \optname{pdfkeywords} +% \item \optname{pdflang} +% \item \optname{pdfmoddate} +% \item \optname{pdfproducer} +% \item \optname{pdfsubject} +% \item \optname{pdftitle} +% \end{itemize} +% \end{multicols} +% +% \noindent +% \mbox{}\label{page:begin-new-options} +% \pkgname{hyperxmp} instructs \pkgname{hyperref} also to accept the +% following options, which have meaning only to \pkgname{hyperxmp}: +% +% \begin{multicols}{3} +% \raggedcolumns +% \begin{itemize} +% \item \optname{pdfaconformance} +% \item \optname{pdfapart} +% \item \optname{pdfauthortitle} +% \item \optname{pdfbookedition} +% \item \optname{pdfbytes} +% \item \optname{pdfcaptionwriter} +% \item \optname{pdfcontactaddress} +% \item \optname{pdfcontactcity} +% \item \optname{pdfcontactcountry} +% \item \optname{pdfcontactemail} +% \item \optname{pdfcontactphone} +% \item \optname{pdfcontactpostcode} +% \item \optname{pdfcontactregion} +% \item \optname{pdfcontacturl} +% \item \optname{pdfcopyright} +% \item \optname{pdfdate} +% \item \optname{pdfdocumentid} +% \item \optname{pdfdoi} +% \item \optname{pdfeissn} +% \item \optname{pdfinstanceid} +% \item \optname{pdfisbn} +% \item \optname{pdfissn} +% \item \optname{pdfissuenum} +% \item \optname{pdflicenseurl} +% \item \optname{pdfmetadate} +% \item \optname{pdfmetalang} +% \item \optname{pdfnumpages} +% \item \optname{pdfpagerange} +% \item \optname{pdfpublication} +% \item \optname{pdfpublisher} +% \item \optname{pdfpubtype} +% \item \optname{pdfsource} +% \item \optname{pdfsubtitle} +% \item \optname{pdftype} +% \item \optname{pdfurl} +% \item \optname{pdfversionid} +% \item \optname{pdfvolumenum} +% \end{itemize} +% \end{multicols} +% \label{page:end-new-options} +% +% +% \subsection{Option descriptions} +% +% The document title is specified as normal for \pkgname{hyperref} with +% \moptname{pdftitle}, but see Note~\ref{note:multilingual} on +% page~\pageref{note:multilingual} for instructions on how to specify a +% title in multiple languages. \pkgname{hyperxmp} introduces a +% complementary \moptname{pdfsubtitle} option: +% +% \begin{verbatim} +% pdftitle={Frankenstein}, +% pdfsubtitle={The Modern Prometheus}, +% \end{verbatim} +% +% Unfortunately, the subtitle can appear in only one language. It +% assumed to be the same language as the document language +% (\optname{pdflang}) but can be overridden by preceding the text with a +% bracketed \acro{ISO} \mbox{639-1} two-letter language code and an +% optional \acro{ISO} \mbox{3166-1} two-letter region code. See the +% example below for \optname{pdfpublication}. +% +% \Needspace{6\baselineskip} +% \moptname{pdfauthortitle} indicates the primary author's position or +% title. \moptname{pdfcaptionwriter} specifies the name of the person +% who added the metadata to the document. The next eight items describe +% how to contact the person or institution responsible for the document +% (the ``contact''). \moptname{pdfcontactaddress} is the contact's +% street address and can include the institution name if the contact is +% an institution; \moptname{pdfcontactcity} is the contact's city; +% \moptname{pdfcontactcountry} is the contact's country; +% \moptname{pdfcontactemail} is the contact's email address (or +% multiple, comma-separated email addresses); \moptname{pdfcontactphone} +% is the contact's telephone number (or multiple, comma-separated +% telephone numbers); \moptname{pdfcontactpostcode} is the contact's +% postal code; \moptname{pdfcontactregion} is the contact's state or +% province; and \moptname{pdfcontacturl} is the contact's \acro{URL} (or +% multiple, comma-separated \acro{URL}s). +% +% \Needspace{2\baselineskip} +% \moptname{pdfcopyright} defines the copyright text, and +% \moptname{pdflicenseurl} identifies a \acro{URL} that points to the +% document's license agreement. +% +% \moptname{pdfmetalang} indicates the natural language in which certain +% metadata---specifically, the document's title, subject, and copyright +% statement---are written. The language should be specified using an +% \acro{IETF} language tag~\cite{IANA2011:lang-tags}, for example, +% ``|en|'' for English, ``|en-US|'' for specifically United States +% English, ``|de|'' for German, and so forth. If \optname{pdfmetalang} +% is not specified, \pkgname{hyperxmp} assumes the metadata language is +% the same as the document language (\pkgname{hyperref}'s +% \moptname{pdflang} option). If neither \optname{pdfmetalang} nor +% \optname{pdflang} is specified, \pkgname{hyperxmp} uses only +% ``|x-default|'' as the metadata language. Note that ``|x-default|'' +% metadata are always included in addition to the specified metadata +% language, as the user reading the document may not have specified a +% language preference. +% +% \acro{XMP} can include a universally unique identifier (\acro{UUID}) +% for each document and for each instance of a given document. By +% default, \pkgname{hyperxmp} assigns a version~4 (i.e.,~pseudorandom) +% \acro{UUID}~\cite{Leach2005:uuid} for each of these. However, a +% document can alternatively specify a particular document identifier +% using \moptname{pdfdocumentid} and (not normally recommended) a +% particular instance identifier using \moptname{pdfinstanceid}. These +% should be of the form +% \texttt{uuid:}\textit{xxxxxxxx}\texttt{-}\textit{xxxx}\texttt{-}\textit{xxxx}\texttt{-}\textit{xxxx}\texttt{-}\textit{xxxxxxxxxxxx}, +% where ``\textit{x}'' is a lowercase hexadecimal number. For example, +% \texttt{uuid:53ab7f19-a48c-5177-8bb2-403ad907f632} is a valid argument +% to \optname{pdfdocumentid} (or \optname{pdfinstanceid}). See Leach, +% Mealling, and Salz's \acro{UUID} specification document for details on +% how to produce the various forms of +% \acro{UUID}s~\cite{Leach2005:uuid}. A more freeform mechanism than +% \optname{pdfinstanceid} for versioning documents is available via +% \moptname{pdfversionid}. The version specified by +% \optname{pdfversionid} can be incremented as~|1|, |2|, |3|,~\dots; +% identified with a hierarchical numbering scheme (e.g.,~this document +% is versioned \texttt{\versionnumber} to match the package version); or +% labeled using any other approach. One possibility is to use a +% revision number or commit hash from the version-control software +% maintaining the document. For example, the |\gitVer| macro from the +% \pkgname{gitver} package is an expandable (see +% Note~\ref{note:expandable} on page~\pageref{note:expandable}) version +% of the current \href{https://git-scm.com/}{Git} hash that can suitably +% be passed to \optname{pdfversionid}. +% +% Already-published documents can be identified in a number of ways. +% \moptname{pdfisbn} specifies the \acro{ISBN}. \moptname{pdfissn} +% refers to the \acro{ISSN} of the \emph{print} version of the document +% while \moptname{pdfeissn} refers to the \acro{ISSN} of the +% \emph{electronic} version of the document. \moptname{pdfdoi} +% specifies the \acro{DOI} and should include only the \acro{DOI} name +% without any \acro{URL} prefix. For example, specify +% |pdfdoi={10.1145/3149526.3149532}|, \emph{not} +% |pdfdoi={https://doi.org/10.1145/3149526.3149532}|. +% \moptname{pdfurl} points to the complete \acro{URL} for the document. +% In contrast, \moptname{baseurl} points one level up and is used to +% resolve relative \acro{URL}s. +% +% Already-published documents can further be identified by the +% publication in which they appear. \moptname{pdfpublication} specifies +% the title of the journal, magazine, or other parent document. The +% title language is assumed to be the same as the document language +% (\optname{pdflang}) but can be overridden by preceding the text with a +% bracketed \acro{ISO} \mbox{639-1} two-letter language code and an +% optional \acro{ISO} \mbox{3166-1} two-letter region code. For +% example, |pdfpublication={[fr]Charlie Hedbo}| indicates a +% French-language title. Were the language or pronunciation differences +% significant, |fr-FR| would indicate specifically the French spoken in +% France, as opposed to that spoken in, say, Canada (|fr-CA|) or Belgium +% (|fr-BE|). The publisher itself can be named using +% \moptname{pdfpublisher}. +% +% \moptname{pdfpubtype} indicates the type of publication in which the +% document was published. This should be one of the \acro{PRISM} +% aggregation types~\cite{PRISM2012:cont-voc} such as |book|, |journal|, +% |magazine|, |manual|, |report|, or |whitepaper|. For publications in +% journals, magazines, and similar periodicals, a document can specify +% the volume number with \moptname{pdfvolumenum} and the issue number +% within the volume with \moptname{pdfissuenum}. +% \moptname{pdfpagerange} indicates the page numbers at which the +% document appears within the publication. The intention is that this +% be a comma-separated list of dash-separated ranges, as in +% |pdfpagerange={1,4-5}|. See Note~\ref{note:page-counting} on +% page~\pageref{note:page-counting} for advice on how to assign +% \optname{pdfpagerange} semi-automatically. For books, +% \moptname{pdfbookedition} names the edition of the book. This is +% specified as text, not a number. As with \optname{pdfpublication} +% (above), \optname{pdfbookedition} accepts a bracketed language code, +% as in |pdfbookedition={[en]Second| |edition}|. +% +% The number of pages in the published, print version of the document +% can be expressed with \moptname{pdfnumpages}. +% Note~\ref{note:page-counting} on page~\pageref{note:page-counting} +% explains how to automatically assign a value to \optname{pdfnumpages}. +% +% \acro{XMP} metadata can include a number of dates (in fact, +% timestamps, as they include both date and time components). +% \moptname{pdfdate} specifies the document date. It is analogous to the +% \LaTeX\ |\date| command, and, like |\date|, defaults to the date the +% document was built. It must be specified in either \acro{XMP} +% format~\cite{Adobe2012:XMP} or \acro{PDF} format~\cite{Adobe2008:PDF}. +% \acro{XMP} dates are written in the form +% \textsc{yyyy}|-|\textsc{mm}|-|\textsc{dd}|T|hh|:|mm|:|ss|+|\textsc{tt}|:|tt.\footnote{Although +% allowed by \acro{XMP}, \pkgname{hyperxmp} does not currently accept +% fractions of a second in timestamps.} A W3C +% recommendation~\cite{Wolf1997:date-time} discusses this format in more +% detail, but as an example, 14~hours, 15~minutes, 9~seconds past +% midnight U.S. Mountain Daylight Time (UTC-6) on the 23rd day of +% September in the year~2014 should be written as +% \texttt{2014-09-23T14:15:09-06:00}. This can be truncated (with loss +% of information) to \texttt{2014-09-23T14:15:09}, +% \texttt{2014-09-23T14:15}, \texttt{2014-09-23}, \texttt{2014-09}, +% or \texttt{2014} but no other subsets. \acro{PDF} dates are written +% in the form |D:|\textsc{yyyymmdd}hhmmss|+|\textsc{tt}|'|tt|'|. The +% same date in the preceding example would be written as +% \texttt{D:20140923141509-06'00'} in \acro{PDF} format. +% +% The document's creation date, modification date, and metadata date are +% normally set automatically, but \moptname{pdfcreationdate}, +% \moptname{pdfmoddate}, and \moptname{pdfmetadate} can be used to +% override the defaults. Like \optname{pdfdate}, \optname{pdfmetadate} +% can be specified in either \acro{XMP} or \acro{PDF} format. However, +% because \pkgname{hyperref} defines \optname{pdfcreationdate} and +% \optname{pdfmoddate} and expects these to be written as \acro{PDF} +% dates, \pkgname{hyperxmp} concomitantly accepts these two dates only +% in \acro{PDF} format as well. Note that it's rare that a document +% would need to specify any of \optname{pdfcreationdate}, +% \optname{pdfmoddate}, or \optname{pdfmetadate}. +% +% \moptname{pdftype} describes the type of document being produced. +% This refers to ``the nature or genre of the +% resource''~\cite{Adobe2012:XMP} such as |poem|, |novel| or |working| +% |paper|, as opposed to the file format (always |application/pdf| when +% generated by \pkgname{hyperxmp}). Although \optname{pdftype} can be +% assigned an arbitrary piece of text, the \acro{XMP} specification +% recommends selecting types from a ``controlled vocabulary'' such as +% the \acro{DCMI} Type Vocabulary~\cite{DCMI2012:meta-terms}. The +% \acro{DCMI} Type Vocabulary currently consists of only |Collection|, +% |Dataset|, |Event|, |Image|, |InteractiveResource|, |MovingImage|, +% |PhysicalObject|, |Service|, |Software|, |Sound|, |StillImage|, and +% |Text|. \optname{pdftype} defaults to |Text|, which refers to +% ``books, letters, dissertations, poems, newspapers, articles, archives +% of mailing lists,''~\cite{DCMI2012:meta-terms} and other forms of +% text---all things \LaTeX\ is commonly used to typeset. +% +% The \moptname{pdfbytes} option expresses the document's file size in +% bytes. The intention is for this to be used to display an estimate of +% download time to a user or to serve as a quick check on whether a file +% was transmitted correctly between systems. This feature is easiest to +% use in conjunction with \pdfTeX's |\pdffilesize| primitive: +% ``|pdfbytes={\pdffilesize{\jobname.pdf}}|''. Note that this requires +% a second run of |pdftex| because it queries the size of the \acro{PDF} +% file from the \emph{previous} run. +% +% A rarely needed option, \moptname{pdfsource}, overrides the name of +% the \LaTeX\ source file. It defaults to |\jobname.tex| but can be +% replaced by any other string. If \optname{pdfsource} is given an +% empty argument, no document source will be specified at all. +% +% Two other rarely needed options, \moptname{pdfaconformance} and +% \moptname{pdfapart}, are used in conjunction with \pkgname{hyperref}'s +% \optname{pdfa} option to claim a particular \acro{PDF/A} standard by +% which the document abides. They default to \optname{pdfapart}=|1| and +% \optname{pdfaconformance}=|B|, indicating the \acro{PDF/A}-\textsc{1b} +% standard. These can be changed (with caution) to assert that the +% document abides by a different standard +% (e.g.,~\acro{PDF/A}-\textsc{2u}). +% +% \bigskip +% +% It is usually more convenient to provide values for those options using +% \pkgname{hyperref}'s |\hypersetup| command than on the |\usepackage| +% command line. See +% \href{http://mirrors.ctan.org/macros/latex/contrib/hyperref/hyperref.pdf}{the +% \pkgname{hyperref} manual} for more information. +% +% +% \subsection{A complete example} +% +% The following is a sample \LaTeX\ document that provides values for +% most of the metadata options that \pkgname{hyperxmp} recognizes: +% +% \Needspace{4\baselineskip} +% \refstepcounter{pagelabel} +% \label{page:begin-sample-doc} +% \begin{verbatim} +% \documentclass{article} +% \usepackage[utf8]{inputenc} +% \usepackage{hyperxmp} +% \usepackage[unicode]{hyperref} +% +% \title{% +% On a heuristic viewpoint concerning the production and +% transformation of light} +% \author{Albert Einstein} +% \date{March 17, 1905} +% +% \hypersetup{% +% 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}, +% pdfnumpages={17}, +% pdfissn={0003-3804}, +% pdfeissn={1521-3889}, +% pdflang={en}, +% pdfmetalang={en}, +% pdfurl={http://www.physik.uni-augsburg.de/annalen/history/einstein-papers/1905_17_132-148.pdf}, +% pdfdoi={10.1002/andp.19053220607}, +% pdfbytes={\pdffilesize{\jobname.pdf}} % Requires pdflatex +% } +% \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} +% \end{verbatim} +% \refstepcounter{pagelabel} +% \label{page:end-sample-doc} +% +% Compile the document to \acro{PDF} using any of the following +% approaches: +% +% \begin{itemize} +% \item \pdfLaTeX +% \item \LuaLaTeX +% \item \LaTeX~$+$ Dvipdfm +% \item \LaTeX~$+$ \Dvips~$+$ Adobe Acrobat Distiller +% \item \XeLaTeX +% \end{itemize} +% +% \noindent +% Unfortunately, the \LaTeX~$+$ \Dvips~$+$ \term{Ghostscript} path +% doesn't work. +% \href{http://bugs.ghostscript.com/show_bug.cgi?id=690066}{Ghostscript +% bug report~\#690066}, closed with ``\textsc{wontfix}'' status on +% 2012-05-28, explains that \term{Ghostscript} doesn't honor the +% \pdfterm{Metadata} tag needed to inject a custom \acro{XMP} packet. +% Instead, \term{Ghostscript} fabricates an \acro{XMP} packet of its own +% based on the metadata it finds in the \acro{PDF} file's \pdfterm{Info} +% dictionary (\pdfterm{Author}, \pdfterm{Title}, \pdfterm{Subject}, and +% \pdfterm{Keywords}). +% +% \bigskip +% +% Once the document is compiled, the resulting \acro{PDF} file will +% contain an \acro{XMP} packet that looks something like that shown in +% Appendix~\ref{sec:sample-packet}. Figure~\ref{fig:xmp-metadata-1} is +% a screenshot of the \acro{XMP} metadata as it appears in Adobe +% Acrobat's ``Advanced'' metadata dialog box. Further clicking on the +% ``Advanced'' item within that dialog box displays all of the +% document's metadata sorted by schema as shown in +% Figure~\ref{fig:xmp-metadata-2}. +% +% \begin{figure}[htbp] +% \includegraphics[width=\linewidth]{einstein1} +% \caption{\acro{XMP} metadata as it appears in Adobe Acrobat} +% \label{fig:xmp-metadata-1} +% \end{figure} +% +% \begin{figure}[htbp] +% \includegraphics[width=\linewidth]{einstein2} +% \caption{Additional \acro{XMP} metadata as it appears in Adobe Acrobat} +% \label{fig:xmp-metadata-2} +% \end{figure} +% +% +% \subsection{Usage notes} +% +% \usagenote{Conflicting metadata in PDF/A documents} +% A \acro{PDF} file includes an \pdfterm{Info} dictionary containing +% \pdfterm{Author}, \pdfterm{Title}, \pdfterm{Subject}, and +% \pdfterm{Keywords} keys. The \pkgname{hyperref} package's +% \optname{pdfauthor}, \optname{pdftitle}, \optname{pdfsubject}, and +% \optname{pdfkeywords} options assign values to those keys. The +% \pkgname{hyperxmp} package additionally uses those options to assign +% values to various \acro{XMP} metadata: \xmpprop{dc:creator}, +% \xmpprop{dc:title}, \xmpprop{dc:description}, and +% \xmpprop{pdf:Keywords}. The \acro{PDF/A} specification indicates that +% values that appear in both the \acro{PDF} \pdfterm{Info} dictionary +% and \acro{XMP} packet must match. The problem is that in \acro{XMP}, +% the author and keywords can be proper lists, as in +% +% \Needspace{7\baselineskip} +% \begin{verbatim} +% +% +% Curly Howard +% Larry Fine +% Moe Howard +% +% +% \end{verbatim} +% +% \noindent +% while in \acro{PDF}, the author and keywords are specified as flat +% strings. Alas, there is no definition of how a list should be +% collapsed to a flat string: ``\texttt{Curly Howard, Larry Fine, Moe +% Howard}'' or ``\texttt{Curly Howard; Larry Fine; Moe Howard}'' or +% something else. I have not yet found a form of flat string that +% passes all \acro{PDF/A} validators. Furthermore, when Adobe +% Acrobat---at least Adobe Acrobat DC (2019) and earlier +% versions---converts a \acro{PDF} file to \acro{PDF/A} format, it does +% so by discarding all but the first author, which is an unsatisfying +% solution. +% +% Starting with version~4.0, \pkgname{hyperxmp}'s solution is to +% suppress writing metadata to the \acro{PDF} \pdfterm{Info} dictionary +% and write it only to the \acro{XMP} packet. This appears to pacify +% \acro{PDF/A} validators yet retains the author and keyword lists in +% their non-truncated form. If desired, the \pdfterm{Info} dictionary +% can be retained by passing the \optname{keeppdfinfo} option to +% \cs{hypersetup}. +% +% \usagenote{Acrobat multiline-field bug} +% The \acro{IPTC} Photo Metadata schema states that ``the [contact] +% address is a multiline field''~\cite{IPTC2010:photo-meta}. +% \pkgname{hyperxmp} converts commas in \optname{pdfcontactaddress}'s +% argument to line breaks in the generated \acro{XML}\@. Unfortunately, +% A bug in Adobe Acrobat---at least in Adobe Acrobat DC (2019) and +% earlier versions---causes that \acro{PDF} reader to discard line +% breaks in the contact address. Interestingly, Adobe Illustrator CS5 +% correctly displays the contact address. If you find Adobe Acrobat's +% behavior bothersome, you can redefine the +% \DescribeMacro{\xmplinesep}|\xmplinesep| macro as a string to use as +% an address-line separator. For example, the following replaces all +% commas appearing in \optname{pdfcontactaddress}'s argument with +% semicolons: +% +% \begin{verbatim} +% \renewcommand*{\xmlinesep}{;} +% \end{verbatim} +% +% \usagenote{Object compression} +% One intention of \acro{XMP} is that metadata embedded in a file be +% readable even without knowledge of the file's format. That is, the +% metadata are expected to appear as plain text. Although +% \pkgname{hyperxmp} does its best to honor that intention, it faces +% a few challenges: +% +% \begin{enumerate} +% \item When run with versions of \LuaLaTeX\ earlier than 0.85, +% \pkgname{hyperxmp} leaves all \acro{PDF} objects uncompressed. This +% is due to \LuaLaTeX\ treating object compression as a global +% parameter, unlike \pdfLaTeX, which treats it as a local parameter. +% Hence, when \pkgname{hyperxmp} requests that the \acro{XMP} packet +% be left uncompressed, \LuaLaTeX\ in fact leaves \emph{all} +% \acro{PDF} streams uncompressed. Beginning with version~3.0, +% \pkgname{hyperxmp} includes a workaround that correctly leaves only +% the \acro{XMP} metadata uncompressed, but this workaround is +% implemented only for \LuaLaTeX\ v0.85 onwards. +% +% \item \XeLaTeX\ (or, more precisely, the \cmdname{xdvipdfmx} back end) +% exhibits the opposite problem. It compresses \emph{all} \acro{PDF} +% objects, including the ones containing \acro{XMP} metadata. While +% Adobe Acrobat can still detect and utilize the \acro{XMP} metadata, +% non-\acro{PDF}-aware applications are unlikely to see the metadata. +% Three options to consider are to (1)~use a different program +% (e.g.,~\LuaLaTeX), (2)~pass the |--output-driver="xdvipdfmx -z0"| +% option to \XeLaTeX\ to instruct \cmdname{xdvipdfmx} to turn off all +% compression (which will of course make the \acro{PDF} file +% substantially larger), or (3)~postprocess the generated \acro{PDF} +% file by loading it into the commercial version of Adobe Acrobat and +% re-saving it with the Save As\dots\ menu option. +% \end{enumerate} +% +% \usagenote{Literal commas} +% \pkgname{hyperxmp} splits the \optname{pdfauthor} and +% \optname{pdfkeywords} lists at commas. Therefore, when specifying +% \optname{pdfauthor} and \optname{pdfkeywords}, you should separate +% items with commas. Also, omit ``|and|'' and other text that does not +% belong to any list item. The following examples should serve as +% clarification: +% +% \begin{description} +% \item[Wrong:] |pdfauthor={Jack Napier, Edward Nigma,| +% \textcolor{red}{\texttt{and}} |Harvey Dent}| +% \item[Wrong:] |pdfauthor={Jack Napier|\textcolor{red}{\texttt{;}} +% |Edward Nigma|\textcolor{red}{\texttt{;}} +% |Harvey Dent}| +% \item[Right:] |pdfauthor={Jack Napier, Edward Nigma, Harvey Dent}| +% \end{description} +% +% \noindent +% \DescribeMacro{\xmpcomma} +% \DescribeMacro{\xmpquote} +% If you need to include a literal comma within an author or keyword +% list (where commas normally separate list items) or a street address +% (where commas normally separate lines), use the |\xmpcomma| macro to +% represent it, and wrap the entire entry containing the comma within +% |\xmpquote{|\dots|}| as shown below: +% +% \begin{verbatim} +% pdfauthor={\xmpquote{Jack Napier\xmpcomma\ Jr.}, +% \xmpquote{Edward Nigma\xmpcomma\ PhD}, +% \xmpquote{Harvey Dent\xmpcomma\ Esq.}} +% +% pdfcontactaddress={Office of the President, +% \xmpquote{Wayne Enterprises\xmpcomma\ Inc.}, +% One Wayne Blvd} +% \end{verbatim} +% +% As of version~2.2 of \pkgname{hyperxmp}, it is acceptable to use +% |\xmpcomma| and |\xmpquote| within any \pkgname{hyperxmp} option, not +% just in those in which a comma normally serves as a separator +% (i.e.,~lists and multiline fields). Outside of cases in which a comma +% serves as a separator, |\xmpcomma| is treated as an ordinary comma, +% and |\xmpquote| returns its argument unmodified. Hence, it is +% legitimate to use |\xmpcomma| and |\xmpquote| in cases like the +% following +% +% \begin{verbatim} +% pdfauthortitle={\xmpquote{Psychiatrist\xmpcomma\ Arkham Asylum}} +% \end{verbatim} +% +% \noindent +% (Like most \pkgname{hyperxmp} options, \optname{pdfauthortitle} +% inserts its argument unmodified in an \acro{XMP} tag.) When in +% doubt, use |\xmpcomma| and |\xmpquote|; it should always be safe +% to do so. +% +% \DescribeMacro{\xmptilde} +% Version~2.4 of \pkgname{hyperxmp} introduces a convenience macro +% called |\xmptilde|. |\xmptilde| expands to a literal tilde character +% instead of the nonbreaking space that ``|~|'' normally represents. +% Use it to represent \acro{URL}s such as +% \url{http://www.pakin.org/~scott/} (``|http://www.pakin.org/|\linebreak[0]|\xmptilde| +% |scott/|'') in options such as \optname{baseurl}, +% \optname{pdfcontacturl} and \optname{pdflicenseurl}. +% +% \usagenote{Unicode support} +% \term{Unicode} support is provided via the \pkgname{hyperref} package. +% If you specify \optname{unicode}|=true| either as a \pkgname{hyperref} +% option or as an argument to the |\hypersetup| command, the document +% can include \term{Unicode} characters in its \acro{XMP} fields. +% +% \usagenote{Automatically specified metadata} +% \optname{pdftitle} defaults to the document's title as specified by +% |\title{|\dots|}|. \optname{pdfauthor} defaults to the document's +% author(s) as specified by |\author{|\dots|}|. \optname{pdfdate} +% defaults to the current date and time. \optname{pdfmetalang} +% defaults to the same value as \optname{pdflang} if non-empty, +% ``|x-default|'' otherwise. An implication of automatic +% metadata specification is that an author can simply include +% |\usepackage{hyperxmp}| in a document's preamble and benefit from +% a modicum of \acro{XMP} metadata with no additional effort. +% +% \usagenote{Multilingual metadata} +% \label{note:multilingual} +% The \optname{pdfmetalang} option specifies the language in which the +% document's metadata is written. It defaults to the value of +% \optname{pdflang}, which specifies the document language. +% \DescribeMacro{\XMPLangAlt} As of version~3.3 of \pkgname{hyperxmp}, +% it is possible to include certain metadata---specifically, the +% document's title, subject, and copyright statement---in more than one +% language. The |\XMPLangAlt| macro provides this functionality. Usage +% is as follows: +% +% \bigskip +% \indent +% |\XMPLangAlt| \marg{language} |{| \meta{option}|=|\meta{text},~\dots\ |}| +% \bigskip +% +% \noindent +% where \meta{language} is an \acro{ISO} \mbox{639-1} two-letter country +% code with an optional \acro{ISO} \mbox{3166-1} two-letter region code +% (e.g.,~``|en|'' for English or ``|en-US|'' for specifically US +% English); \meta{option} is one of ``|pdftitle|'', ``|pdfsubject|'', or +% ``|pdfcopyright|''; and \meta{text} is the text as expressed in the +% specified language. By way, of example, the following code provides +% the document title in English then specifies an alternative title to +% use in four other languages: +% +% \begin{verbatim} +% \hypersetup{% +% pdfmetalang={en}, +% pdftitle={English title} +% } +% \XMPLangAlt{de}{pdftitle={Deutscher Titel}} +% \XMPLangAlt{fr}{pdftitle={Titre fran\c{c}ais}} +% \XMPLangAlt{it}{pdftitle={Titolo italiano}} +% \XMPLangAlt{rm}{pdftitle={Titel rumantsch}} +% \end{verbatim} +% +% \usagenote{Expandable arguments} +% \label{note:expandable} +% All arguments passed to \pkgname{hyperxmp} options must be expandable, +% in \tex\ terminology. This implies that any macros that are used in +% arguments are limited to a relatively small set of operations (such as +% conditionals and macro expansion) and must produce a string of text. +% Code (such as macro definitions and arithmetic operations) will be +% written to \acro{XMP} as code, not as the result of executing the +% code. +% +% By way of example, the macros provided by the \pkgname{texdate} +% package for typesetting dates are not expandable (at least at the time +% of this writing). Hence, the |\printfdate{Y}| in the following code +% snippet is not replaced by the current year, as one might expect: +% +% \begin{verbatim} +% \usepackage{texdate} +% \initcurrdate +% \hypersetup{% +% pdfcopyright={Copyright \textcopyright\ \printfdate{Y}, Scott Pakin} +% } +% \end{verbatim} +% +% \noindent +% Rather, it generates a \xmpprop{dc:rights} tag of the form +% ``\texttt{Copyright \textcopyright\ =2=0=by-1by=0\the\year, Scott +% Pakin}''. The garbage in that line corresponds to the remnants of +% the |\printfdate| code after expanding all of the \tex\ primitives and +% certain other control sequences it uses to the empty string. For +% example, ``|\global\advance\texd@yr| |by-1|'' expands to ``|by-1|''. +% +% It is not possible to determine a~priori whether or not a macro is +% expandable. The best advice is to carefully inspect the \acro{XMP} +% package in the output file to ensure that any macros used in arguments +% to \pkgname{hyperxmp} options produced the expected output. +% +% \usagenote{Automatic page counting} +% \label{note:page-counting} +% Although \optname{pdfnumpages} and \optname{pdfpagerange} are intended +% to refer to pages in the final, published version of a document, it +% would be convenient for them to be generated automatically when +% producing a standalone \acro{PDF} file that is not intended to be +% incorporated into a book, journal, or other publication (or if it is +% known that the pages will not be renumbered for publication). One +% approach is to use the \pkgname{totpages} package to keep track of the +% number of pages. +% +% \begin{verbatim} +% \hypersetup{% +% pdfnumpages={\ref*{TotPages}} +% } +% \end{verbatim} +% +% \pkgname{totpages} can likewise help generate \optname{pdfpagerange}. +% For documents numbered from~1 to~$n$, a simple +% +% \begin{verbatim} +% \hypersetup{% +% pdfpagerange={1-\ref*{TotPages}} +% } +% \end{verbatim} +% +% \noindent +% should suffice. A bit more effort is needed for documents that change +% numbering schemes, such as using lowercase Roman numerals for the +% front matter and Arabic numerals for the main matter and back matter. +% One approach is to use \cs{label} to mark the first and last page of +% each numbering scheme and specify \optname{pdfpagerange} as in the +% following: +% +% \Needspace{6\baselineskip} +% \begin{verbatim} +% \hypersetup{% +% pdfpagerange={% +% \pageref*{page:begin-front}-\pageref*{page:end-front},% +% 1-\pageref*{TotPages}% +% } +% } +% \end{verbatim} +% +% I don't know how unnumbered pages (e.g.,~blank pages and the title +% page) are supposed to be handled. I suppose blank pages can be +% omitted from \optname{pdfpagerange}, and title page can be either +% omitted or listed as |title|, for example. +% +% It appears that at least with version~2.00 of \pkgname{totpages}, the +% |TotPages| label is not defined until after the |\begin{document}|. +% Consequently, using |TotPages| within a \cs{hypersetup} invocation in +% the document's preamble will produce ``|??|'' as the page count in the +% \acro{XMP} packet. The solution is either to assign +% \optname{pdfnumpages} and \optname{pdfpagerange} after the +% |\begin{document}| or to ask \LaTeX\ to do that on your behalf: +% +% \begin{verbatim} +% \AtBeginDocument{% +% \hypersetup{% +% pdfnumpages={\ref*{TotPages}}, +% pdfpagerange={1-\ref*{TotPages}} +% }% +% } +% \end{verbatim} +% +% +% \StopEventually{^^A +% \section{Help Wanted} +% +% \paragraph{Comma handling} +% Ideally, \texttt{\string\xmpquote} should automatically replace all +% commas with \texttt{\string\xmpcomma}. Unfortunately, my \tex\ skills +% are insufficient to pull that off. If you know a way to make +% \texttt{\string\xmpquote\string{Hello, world\string}} work with both +% \term{Unicode} and non-\term{Unicode} encodings and with all +% \tex\ engines (\pdfTeX, \LuaTeX, \XeTeX, etc.), please send me a code +% patch. +% +% \appendix +% +% \section{Sample XMP Packet} +% \label{sec:sample-packet} +% +% The following is an example of a complete \acro{XMP} packet as may be +% produced by \pkgname{hyperxmp}. This packet corresponds to the +% metadata included in the sample \LaTeX\ document presented +% \vpagerefrange{page:begin-sample-doc}{page:end-sample-doc}. For +% clarity, metadata values, either specified explicitly by the document +% or introduced automatically by \pkgname{hyperxmp}, are colored blue. +% +% \input{einstein-xmp} +% +% \begin{thebibliography}{1} +% \bibitem{Adobe2010:pdfmark} +% Adobe Systems, Inc., San Jose, California. +% \newblock \emph{{A}dobe {A}crobat~{X} {SDK} Help, pdfmark Reference}. +% \newblock Available from +% \url{http://www.adobe.com/devnet/acrobat/documentation.html}. +% +% \bibitem{Adobe1996:postscript} +% {Adobe Systems, Inc.} +% \newblock \emph{{P}ost{S}cript Language Reference Manual}. +% \newblock Addison-Wesley, 2nd edition, January 1996, ISBN: \mbox{0-201-18127-4}. +% +% \bibitem{Adobe2008:PDF} +% Adobe Systems, Inc., San Jose, California. +% \newblock \emph{Document Management---Portable Document Format---Part 1: +% PDF~1.7}, July 2008. +% \newblock ISO \mbox{32000-1} standard document. Available from +% \url{http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf}. +% +% \bibitem{Adobe2012:XMP} +% Adobe Systems, Inc., San Jose, California. +% \newblock \emph{{XMP} Specification Part~1: Data model, Serialization, and Core +% Properties}, April 2012. +% \newblock Available from +% \url{http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/cc-201306/XMPSpecificationPart1.pdf}. +% +% \bibitem{DCMI2012:meta-terms} +% DCMI Usage Board +% \newblock \emph{DCMI Metadata Terms}, June~14, 2012. +% \newblock Available from \url{http://dublincore.org/documents/dcmi-terms/}. +% +% \bibitem{Downes1994:ATB15} +% Michael Downes. +% \newblock Around the bend~\#15, answers, 4th (last) installment. +% \newblock \href{news:comp.text.tex}{\texttt{comp.text.tex}} newsgroup posting, +% January~3, 1994. +% \newblock Archived by Google at +% \url{http://groups.google.com/group/comp.text.tex/msg/7da7643b9e8f3b48}. +% +% \bibitem{PRISM2012:basic-meta} +% International Digital Enterprise Alliance, Inc. +% \newblock \emph{Publishing Requirements for Industry Standard Metadata, +% Version~3.0: {PRISM} Basic Metadata Specification}, October~12, 2012. +% \newblock Available from +% \url{http://www.prismstandard.org/specifications/3.0/PRISM_Basic_Metadata_3.0.htm}. +% +% \bibitem{PRISM2012:cont-voc} +% International Digital Enterprise Alliance, Inc. +% \newblock \emph{Publishing Requirements for Industry Standard Metadata, +% Version~3.0: {PRISM} Controlled Vocabularies Specification}, October~4, 2012. +% \newblock Available from +% \url{http://www.prismstandard.org/specifications/3.0/PRISM_CV_Spec_3.0.pdf}. +% +% \bibitem{IPTC2010:photo-meta} +% International Press Telecommunications Council. +% \newblock \emph{{IPTC} Photo Metadata: Core 1.1/Extension 1.1}, July 2010. +% \newblock Revision~1. Available from +% \url{http://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata-201007_1.pdf}. +% +% \bibitem{IANA2011:lang-tags} +% {Internet Assigned Numbers Authority}. +% \newblock Language subtag registry, January~11, 2011. +% \newblock Available from +% \url{http://www.iana.org/assignments/language-subtag-registry}. +% +% \bibitem{Leach2005:uuid} +% Paul~J. Leach, Michael Mealling, and Rich Salz. +% \newblock A {U}niversally {U}nique {ID}entifier ({UUID}) {URN} namespace. +% \newblock Request for Comments 4122, Internet Engineering Task Force, Network +% Working Group, July 2005. +% \newblock Category: Standards Track. Available from +% \url{http://www.ietf.org/rfc/rfc4122.txt}. +% +% \bibitem{PDFA2008:xmp-props} +% PDF/A Competence Center, Berlin, Germany. +% \newblock \emph{{T}ech{N}ote~0008: Predefined {XMP} Properties in {PDF/A-1}}, +% March~20, 2008. +% \newblock Available from +% \url{http://www.pdfa.org/wp-content/uploads/2011/08/tn0008_predefined_xmp_properties_in_pdfa-1_2008-03-20.pdf}. +% +% \bibitem{PDFA2008:ext-schemas} +% PDF/A Competence Center, Berlin, Germany. +% \newblock \emph{{T}ech{N}ote~0009: {XMP} Extension Schemas in {PDF/A-1}}, +% March~20, 2008. +% \newblock Available from +% \url{http://www.pdfa.org/wp-content/uploads/2011/08/tn0009_xmp_extension_schemas_in_pdfa-1_2008-03-20.pdf}. +% +% \bibitem{Wolf1997:date-time} +% Misha Wolf and Charles Wicksteed. +% \newblock Date and time formats. +% \newblock Note NOTE-datetime, World Wide Web Consortium (W3C), September~15, +% 1997. +% \newblock Available from \url{http://www.w3.org/TR/NOTE-datetime}. +% \end{thebibliography} +% } +% +% +% \section{Implementation} +% +% This section presents the commented \LaTeX\ source code for +% \pkgname{hyperxmp}. Read this section only if you want to learn how +% \pkgname{hyperxmp} is implemented. +% +% +% \subsection{Initial preparation} +% \label{sec:initial-prep} +% +% \changes{v1.2}{2011/04/17}{Made the package compatible with \pkgname{ngerman}. +% Thanks to Tobias Mueller for the bug report.} +% +% \begin{macro}{\hyxmp@dq@code} +% The \pkgname{ngerman} package redefines +% ``\,{\fontencoding{T1}\selectfont\textquotedbl}\,'' as an active +% character, which causes problems for \pkgname{hyperxmp} when it tries +% to use that character. We therefore save the double-quote character's +% current category code in |\hyxmp@dq@code| and mark the character as +% category code~12 (``other''). The original category code is restored +% at the end of the package code (Section~\ref{sec:clean-up}). +% \begin{macrocode} +\edef\hyxmp@dq@code{\the\catcode`\"} +\catcode`\"=12 +% \end{macrocode} +% \end{macro} +% +% \changes{v1.5}{2012/03/10}{Made the \protect\acro{XMP} inclusion more robust. +% Thanks to Heiko Oberdiek for the bug report and suggested modifications.} +% +% \begin{macro}{\hyxmp@at@end} +% \begin{macro}{\hyxmp@driver} +% The |\hyxmp@at@end| macro includes code at the end of the document. +% For \pdfTeX, the standard |\AtEndDocument| works well enough. For all +% the other backends we use |\AtEndDvi| from the \pkgname{atenddvi} +% package, which is more robust but requires an addition \LaTeX\ run. +% \begin{macrocode} +\def\hyxmp@driver{hpdftex} +\ifx\hyxmp@driver\Hy@driver + \let\hyxmp@at@end=\AtEndDocument +\else + \RequirePackage{atenddvi} + \let\hyxmp@at@end=\AtEndDvi +\fi +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% +% \subsection{Integration with \textsf{hyperref}} +% +% An important design decision underlying \pkgname{hyperxmp} is that the +% package should integrate seamlessly with \pkgname{hyperref}. To that +% end, \pkgname{hyperxmp} takes its \acro{XMP} metadata from +% \pkgname{hyperref}'s \optname{baseurl}, \optname{pdfauthor}, +% \optname{pdfkeywords}, \optname{pdflang}, \optname{pdfproducer}, +% \optname{pdfsubject}, and \optname{pdftitle} options. It also +% introduces a number of new options, which are listed +% \vpagerefrange[above]{page:begin-new-options}{page:end-new-options}. +% For consistency with \pkgname{hyperref}'s document-metadata naming +% conventions (which are in turn based on \LaTeX's document-metadata +% naming conventions), we do not prefix metadata-related macro names +% with our package-specific |\hyxmp@| prefix. That is, we use names +% like |\@pdfcopyright| instead of |\hyxmp@pdfcopyright|. +% +% \bigskip +% +% We load a bunch of helper packages: \pkgname{kvoptions} for +% package-option processing, \pkgname{pdfescape} and \pkgname{stringenc} +% for re-encoding \term{Unicode} strings, \pkgname{intcalc} for +% performing integer calculations (division and modulo), +% \pkgname{ifxetex} for detecting \XeTeX, and \pkgname{ifmtarg} for +% testing if a macro argument is empty or all spaces. +% \begin{macrocode} +\RequirePackage{kvoptions} +\RequirePackage{pdfescape} +\RequirePackage{stringenc} +\RequirePackage{intcalc} +\RequirePackage{ifxetex} +\RequirePackage{ifmtarg} +% \end{macrocode} +% +% \begin{macro}{\@ifmtargexp} +% \begin{macro}{\@ifnotmtargexp} +% |\@ifmtarg| and |\@ifnotmtarg| do not expand their first argument. +% Define |\@ifmtargexp| and |\@ifnotmtargexp| as expanding versions of +% those macros. +% \begin{macrocode} +\def\@ifmtargexp#1{\expandafter\@ifmtarg\expandafter{#1}} +\def\@ifnotmtargexp#1{\expandafter\@ifnotmtarg\expandafter{#1}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@pdfstringdef} +% \begin{macro}{\hyxmp@textunderscore} +% Because \pkgname{hyperxmp} uses underscores to represent hard spaces, +% we need ``|\_|'' to map initially to something other than an +% underscore, in particular the \acro{ASCII} \acro{NAK}~(|^^U|) +% character. To accomplish this, we wrap \pkgname{hyperref}'s +% |\pdfstringdef| macro with our own version that temporarily does the +% proper substitution. Later in the execution, after underscores have +% been replaced with spaces, we replace \acro{NAK} characters with +% underscores. +% \changes{v2.5}{2014/06/19}{Added this macro} +% \begin{macrocode} +\newcommand{\hyxmp@pdfstringdef}[2]{% + \let\hyxmp@textunderscore=\textunderscore + \let\textunderscore=\hyxmp@uscore + \pdfstringdef{#1}{#2}% + \let\textunderscore=\hyxmp@textunderscore +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@pdfdatetime} +% Prepare to store the document's date and (optionally) time. Whether +% specified by the author in \acro{XMP} format or \acro{PDF} format +% (see Section~\ref{sec:date-manip}) we always store |\@pdfdatetime| as +% an \acro{XMP}-format string. +% \begin{macrocode} +\def\@pdfdatetime{} +\define@key{Hyp}{pdfdate}{% + \begingroup + \Hy@unicodefalse +% \end{macrocode} +% \begin{macro}{\next} +% Expand \optname{pdfdate}'s argument and convert it to \acro{XMP} format. +% \begin{macrocode} + \edef\next{% + \noexpand\hyxmp@pdfstringdef\noexpand\@pdfdatetime{% + \noexpand\hyxmp@as@xmp@date{#1}}% + }% + \next + \endgroup +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@pdfmetadatetime} +% Prepare to store the document's metadata date and (optionally) time. +% Whether specified by the author in \acro{XMP} format or \acro{PDF} +% format (see Section~\ref{sec:date-manip}) we always store +% |\@pdfmetadatetime| as an \acro{XMP}-format string. +% \begin{macrocode} +\def\@pdfmetadatetime{} +\define@key{Hyp}{pdfmetadate}{% + \begingroup + \Hy@unicodefalse +% \end{macrocode} +% \begin{macro}{\next} +% Expand \optname{pdfmetadate}'s argument and convert it to \acro{XMP} format. +% \begin{macrocode} + \edef\next{% + \noexpand\hyxmp@pdfstringdef\noexpand\@pdfmetadatetime{% + \noexpand\hyxmp@as@xmp@date{#1}}% + }% + \next + \endgroup +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@pdfcopyright} +% Prepare to store the document's copyright statement. +% \begin{macrocode} +\def\@pdfcopyright{} +\define@key{Hyp}{pdfcopyright}{\hyxmp@pdfstringdef\@pdfcopyright{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdftype} +% Prepare to store the document's logical type, which defaults to ``|Text|''. +% \begin{macrocode} +\def\@pdftype{Text} +\define@key{Hyp}{pdftype}{\hyxmp@pdfstringdef\@pdftype{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdflicenseurl} +% Prepare to store the \acro{URL} containing the document's license +% agreement. +% \begin{macrocode} +\def\@pdflicenseurl{} +\define@key{Hyp}{pdflicenseurl}{\hyxmp@pdfstringdef\@pdflicenseurl{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfauthortitle} +% Prepare to store the author's position/title (e.g.,~Staff Writer). +% \begin{macrocode} +\def\@pdfauthortitle{} +\define@key{Hyp}{pdfauthortitle}{\hyxmp@pdfstringdef\@pdfauthortitle{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfcaptionwriter} +% Prepare to store the name of the person who inserted the +% \pkgname{hyperxmp} metadata. +% \begin{macrocode} +\def\@pdfcaptionwriter{} +\define@key{Hyp}{pdfcaptionwriter}{\hyxmp@pdfstringdef\@pdfcaptionwriter{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfmetalang} +% Prepare to store the natural language of the document's metadata, +% typically as an \acro{ISO}~\mbox{639-1} two-letter abbreviation. +% \begin{macrocode} +\def\@pdfmetalang{} +\define@key{Hyp}{pdfmetalang}{\hyxmp@pdfstringdef\@pdfmetalang{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfapart} +% Prepare to store the \acro{PDF/A} part ID, which defaults to~``1''. +% \begin{macrocode} +\def\@pdfapart{1} +\define@key{Hyp}{pdfapart}{\hyxmp@pdfstringdef\@pdfapart{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfaconformance} +% Prepare to store the \acro{PDF/A} conformance ID, which defaults to~``B''. +% \begin{macrocode} +\def\@pdfaconformance{B} +\define@key{Hyp}{pdfaconformance}{\hyxmp@pdfstringdef\@pdfaconformance{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfsource} +% Prepare to store the document's source, which defaults to the value of +% |\jobname|. +% \changes{v3.3}{2017/07/16}{Added this macro and the corresponding +% \protect\optname{pdfsource} option, at Niklas Beisert's request} +% \begin{macrocode} +\edef\@pdfsource{\jobname.tex} +\define@key{Hyp}{pdfsource}{\hyxmp@pdfstringdef\@pdfsource{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@DocumentID} +% Prepare to store a \acro{UUID} that represents the document. +% \changes{v3.5}{2018/11/27}{Added the \protect\optname{pdfdocumentid} +% option, at Michael Osipov's request} +% \begin{macrocode} +\def\hyxmp@DocumentID{} +\define@key{Hyp}{pdfdocumentid}{\hyxmp@pdfstringdef\hyxmp@DocumentID{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@InstanceID} +% Prepare to store a \acro{UUID} that represents the current instance of +% the document. +% \changes{v3.5}{2018/11/27}{Added the \protect\optname{pdfinstanceid} +% option, at Michael Osipov's request} +% \begin{macrocode} +\def\hyxmp@InstanceID{} +\define@key{Hyp}{pdfinstanceid}{\hyxmp@pdfstringdef\hyxmp@InstanceID{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfversionid} +% Prepare to store a \acro{UUID} that represents the current version of +% the document. +% \begin{macrocode} +\def\@pdfversionid{} +\define@key{Hyp}{pdfversionid}{\hyxmp@pdfstringdef\@pdfversionid{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfpublication} +% Prepare to store the name of the publication in which the +% document was published. +% \begin{macrocode} +\def\@pdfpublication{} +\define@key{Hyp}{pdfpublication}{\hyxmp@pdfstringdef\@pdfpublication{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfpubtype} +% Prepare to store the type of the publication in which the +% document was published. +% \begin{macrocode} +\def\@pdfpubtype{} +\define@key{Hyp}{pdfpubtype}{\hyxmp@pdfstringdef\@pdfpubtype{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfbytes} +% Prepare to store the size of the file in bytes. +% \begin{macrocode} +\def\@pdfbytes{} +\define@key{Hyp}{pdfbytes}{\hyxmp@pdfstringdef\@pdfbytes{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfnumpages} +% Prepare to store the number of pages in the file. +% \begin{macrocode} +\def\@pdfnumpages{} +\define@key{Hyp}{pdfnumpages}{\hyxmp@pdfstringdef\@pdfnumpages{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfissn} +% Prepare to store the \acro{ISSN} of the publication in which the +% document was published. +% \begin{macrocode} +\def\@pdfissn{} +\define@key{Hyp}{pdfissn}{\hyxmp@pdfstringdef\@pdfissn{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfeissn} +% Prepare to store the \acro{ISSN} of the electronic version of the +% publication in which the document was published. +% \begin{macrocode} +\def\@pdfeissn{} +\define@key{Hyp}{pdfeissn}{\hyxmp@pdfstringdef\@pdfeissn{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfisbn} +% Prepare to store the \acro{ISBN} of the publication in which the +% document was published. +% \begin{macrocode} +\def\@pdfisbn{} +\define@key{Hyp}{pdfisbn}{\hyxmp@pdfstringdef\@pdfisbn{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfbookedition} +% Prepare to store the edition of the book in which the document +% was published. +% \begin{macrocode} +\def\@pdfbookedition{} +\define@key{Hyp}{pdfbookedition}{\hyxmp@pdfstringdef\@pdfbookedition{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfpublisher} +% Prepare to store the name of the document's publisher. +% \begin{macrocode} +\def\@pdfpublisher{} +\define@key{Hyp}{pdfpublisher}{\hyxmp@pdfstringdef\@pdfpublisher{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfvolumenum} +% Prepare to store the volume identifier of the publication in which the +% document was published. +% \begin{macrocode} +\def\@pdfvolumenum{} +\define@key{Hyp}{pdfvolumenum}{\hyxmp@pdfstringdef\@pdfvolumenum{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfissuenum} +% Prepare to store the identifier of the issue within a volume of the +% publication in which the document was published. +% \begin{macrocode} +\def\@pdfissuenum{} +\define@key{Hyp}{pdfissuenum}{\hyxmp@pdfstringdef\@pdfissuenum{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfpagerange} +% Prepare to store the document's range of pages within the publication +% in which the document was published. +% \begin{macrocode} +\def\@pdfpagerange{} +\define@key{Hyp}{pdfpagerange}{\hyxmp@pdfstringdef\@pdfpagerange{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfdoi} +% Prepare to store a \acro{DOI} that represents the current instance of +% the document. +% \begin{macrocode} +\def\@pdfdoi{} +\define@key{Hyp}{pdfdoi}{\hyxmp@pdfstringdef\@pdfdoi{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfurl} +% Prepare to store a \acro{URL} that represents where the document +% can be found. Note that we do not prepend \optname{baseurl} to +% the value provided. +% \begin{macrocode} +\def\@pdfurl{} +\define@key{Hyp}{pdfurl}{\hyxmp@pdfstringdef\@pdfurl{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfsubtitle} +% Prepare to store the document's subtitle. +% \begin{macrocode} +\def\@pdfsubtitle{} +\define@key{Hyp}{pdfsubtitle}{\hyxmp@pdfstringdef\@pdfsubtitle{#1}} +% \end{macrocode} +% \end{macro} +% +% The following eight macros---|\@pdfcontactaddress|, +% |\@pdfcontactcity|, |\@pdfcontactregion|, |\@pdfcontactpostcode|, +% |\@pdfcontactcountry|, |\@pdfcontactphone|, |\@pdfcontactemail|, and +% |\@pdfcontacturl|---together specify how to contact the person or +% institution responsible for the document. +% +% \begin{macro}{\@pdfcontactaddress} +% Prepare to store a street address for the document's contact +% person\slash institution. The \acro{IPTC} standard defines this as +% follows: +% +% \begin{quote} +% The contact information address part. Comprises an optional company +% name and all required information to locate the building or postbox +% to which mail should be sent. To that end, the address is a +% multiline field. +% \end{quote} +% +% For consistency with the rest of \pkgname{hyperxmp}, we use commas to +% separate terms, in this case, lines of the address. The author can +% use |\xmpquote| and |\xmpcomma| to include literal commas. +% \begin{macrocode} +\def\@pdfcontactaddress{} +\define@key{Hyp}{pdfcontactaddress}{% + \let\xmpcomma=\hyxmp@comma + \def\xmpquote##1{##1}% + \hyxmp@pdfstringdef\@pdfcontactaddress{#1}% + \def\xmpcomma{,}% + \let\xmpquote=\relax +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfcontactcity} +% Prepare to store the city of the document's contact person\slash +% institution. +% \begin{macrocode} +\def\@pdfcontactcity{} +\define@key{Hyp}{pdfcontactcity}{\hyxmp@pdfstringdef\@pdfcontactcity{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfcontactregion} +% Prepare to store the state or province of the document's contact +% person\slash institution. +% \begin{macrocode} +\def\@pdfcontactregion{} +\define@key{Hyp}{pdfcontactregion}{\hyxmp@pdfstringdef\@pdfcontactregion{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfcontactpostcode} +% Prepare to store the postal code of the document's contact +% person\slash institution. +% \begin{macrocode} +\def\@pdfcontactpostcode{} +\define@key{Hyp}{pdfcontactpostcode}{\hyxmp@pdfstringdef\@pdfcontactpostcode{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfcontactcountry} +% Prepare to store the country of the document's contact person\slash +% institution. +% \begin{macrocode} +\def\@pdfcontactcountry{} +\define@key{Hyp}{pdfcontactcountry}{\hyxmp@pdfstringdef\@pdfcontactcountry{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfcontactphone} +% Prepare to store the telephone number of the document's contact +% person\slash institution. +% \begin{macrocode} +\def\@pdfcontactphone{} +\define@key{Hyp}{pdfcontactphone}{\hyxmp@pdfstringdef\@pdfcontactphone{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfcontactemail} +% Prepare to store the email address of the document's contact +% person\slash institution. +% \begin{macrocode} +\def\@pdfcontactemail{} +\define@key{Hyp}{pdfcontactemail}{\hyxmp@pdfstringdef\@pdfcontactemail{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@pdfcontacturl} +% Prepare to store the \acro{URL} of the document's contact person\slash +% institution. +% \begin{macrocode} +\def\@pdfcontacturl{} +\define@key{Hyp}{pdfcontacturl}{\hyxmp@pdfstringdef\@pdfcontacturl{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@suppress@pdf@metadata} +% Suppress \pkgname{hyperref} from writing \pdfterm{Author}, +% \pdfterm{Title}, \pdfterm{Subject}, and other \acro{PDF} metadata into +% the \pdfterm{Info} dictionary. This prevents conflicts between the +% \acro{PDF} metadata and the \acro{XMP} metadata, which cause +% \acro{PDF/A} validation to fail. The \acro{PDF} metadata can be +% restored by passing the \optname{keeppdfinfo} option to +% |\hypersetup|. +% \changes{v4.0}{2019/03/31}{Added this macro} +% \begin{macrocode} +\def\hyxmp@suppress@pdf@metadata{% + \global\let\PDF@FinishDoc=\@empty +} +\define@key{Hyp}{keeppdfinfo}[true]{% + \gdef\hyxmp@suppress@pdf@metadata{}% +} +% \end{macrocode} +% \end{macro} +% +% \changes{v2.1}{2012/09/16}{Enabled \protect\pkgname{hyperxmp} and +% \protect\pkgname{hyperref} to be loaded in either order. This addresses +% a bug report by Yury Donskoy} +% +% We need to capture list arguments (viz.~\optname{pdfauthor} and +% \optname{pdfkeywords}) before \pkgname{hyperref} converts them to +% \term{PDFDocEncoding}. Otherwise, |\xmpcomma| is permanently replaced +% with a comma, and we lose our ability to change it to a +% |\hyxmp@comma|. We therefore need to augment \pkgname{hyperref}'s +% option processing with our own. Because \pkgname{hyperref} has not +% yet been loaded we need to ensure that our augmentation gets loaded in +% the future: after the |\usepackage{hyperref}| but before options are +% passed to that package. +% +% For lack of a better approach, \pkgname{hyperxmp} redefines +% |\ProcessKeyvalOptions| to alter the way \pkgname{hyperref} processes +% \optname{pdfauthor} and \optname{pdfkeywords}. This is somewhat +% heavy-handed as it gets executed for \emph{every} subsequently loaded +% package that uses |\ProcessKeyvalOptions|, but at least it does what +% we need. \pkgname{hyperxmp} also redefines |\hypersetup| to do the +% same thing. This is required in case \pkgname{hyperref} is loaded +% before \pkgname{hyperxmp}. +% +% \begin{macro}{\hyxmp@pdfauthor} +% \begin{macro}{\hyxmp@pdfkeywords} +% Prepare to store the name of the author and a list of keywords. +% \begin{macrocode} +\def\hyxmp@pdfauthor{} +\def\hyxmp@pdfkeywords{} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@redefine@Hyp} +% If not already redefined, redefine \pkgname{hyperref}'s +% \optname{pdfauthor} and \optname{pdfkeywords} options to properly +% handle |\xmpcomma| and |\xmpquote|. +% \changes{v2.1}{2012/09/16}{Added this macro} +% \begin{macrocode} +\newcommand*{\hyxmp@redefine@Hyp}{% +% \end{macrocode} +% \begin{macro}{\hyxmp@Hyp@pdfauthor} +% Store the old definition of |\KV@Hyp@pdfauthor| in +% |\hyxmp@Hyp@pdfauthor|, but only if we see that |\KV@Hyp@pdfauthor| is +% defined and |\hyxmp@Hyp@pdfauthor| isn't. Otherwise, we'd be defining +% |\hyxmp@Hyp@pdfauthor| in terms of itself and creating an infinite +% loop. +% \begin{macrocode} + \@ifundefined{KV@Hyp@pdfauthor}{}{% + \@ifundefined{hyxmp@Hyp@pdfauthor}{% + \expandafter\let\expandafter\hyxmp@Hyp@pdfauthor + \csname KV@Hyp@pdfauthor\endcsname + }{}% + }% +% \end{macrocode} +% \begin{macro}{\KV@Hyp@pdfauthor} +% \begin{macro}{\xmpcomma} +% \begin{macro}{\xmpquote} +% \begin{macro}{\hyxmp@and} +% \begin{macro}{\and} +% \begin{macro}{\hyxmp@pdfauthor} +% \begin{macro}{\@pdfauthor} +% Redefine |\KV@Hyp@pdfauthor| to process its argument twice. The first +% time, |\xmpcomma| is defined as a placeholder character +% (|\hyxmp@comma|) and |\xmpquote| as the identity function. The result +% is stored in |\hyxmp@pdfauthor| for use in structured lists (those +% surrounding each entry with ||). The second time, |\xmpcomma| +% is defined as an ordinary comma, and |\xmpquote| is defined as a macro +% that puts its argument within double quotes. The result is stored in +% |\@pdfauthor| for use in unstructured lists (those in which the entire +% list appears within a single pair of tags). In case +% \optname{pdfauthor} is left unspecified and we copy |\author|'s +% argument to \optname{pdfauthor}, we temporarily redefine |\and| as the +% list separator when producing a structured list and as ``|and|'' when +% producing an unstructured list. +% \begin{macrocode} + \define@key{Hyp}{pdfauthor}{% + \let\xmpcomma=\hyxmp@comma + \def\xmpquote####1{####1}% + \let\hyxmp@and=\and + \def\and{,}% + \hyxmp@Hyp@pdfauthor{##1}% + \global\let\hyxmp@pdfauthor=\@pdfauthor + \def\and{and\space}% + \def\xmpcomma{,}% + \def\xmpquote####1{"####1"}% + \hyxmp@Hyp@pdfauthor{##1}% + \def\xmpcomma{,}% + \let\xmpquote=\relax + \let\and=\hyxmp@and + }% +% \end{macrocode} +% \begin{macro}{\hyxmp@Hyp@pdfkeywords} +% The previous block of code now repeats for the keyword list, starting +% by storing the old definition of |\KV@Hyp@pdfkeywords| in +% |\hyxmp@Hyp@pdfkeywords|. +% \begin{macrocode} + \@ifundefined{KV@Hyp@pdfkeywords}{}{% + \@ifundefined{hyxmp@Hyp@pdfkeywords}{% + \expandafter\let\expandafter\hyxmp@Hyp@pdfkeywords + \csname KV@Hyp@pdfkeywords\endcsname + }{}% + }% +% \end{macrocode} +% \begin{macro}{\KV@Hyp@pdfkeywords} +% \begin{macro}{\xmpcomma} +% \begin{macro}{\xmpquote} +% \begin{macro}{\hyxmp@pdfkeywords} +% \begin{macro}{\@pdfkeywords} +% Redefine |\KV@Hyp@pdfkeywords| to process its argument twice. The +% first time, |\xmpcomma| is defined as a placeholder character +% (|\hyxmp@comma|) and |\xmpquote| as the identity function. The +% result is stored in |\hyxmp@pdfkeywords| for use in structured lists +% (those surrounding each entry with ||). The second time, +% |\xmpcomma| is defined as an ordinary comma, and |\xmpquote| is +% defined as a macro that puts its argument within double quotes. The +% result is stored in |\@pdfkeywords| for use in unstructured lists +% (those in which the entire list appears within a single pair of tags). +% \begin{macrocode} + \define@key{Hyp}{pdfkeywords}{% + \let\xmpcomma=\hyxmp@comma + \def\xmpquote####1{####1}% + \hyxmp@Hyp@pdfkeywords{##1}% + \global\let\hyxmp@pdfkeywords=\@pdfkeywords + \def\xmpcomma{,}% + \def\xmpquote####1{"####1"}% + \hyxmp@Hyp@pdfkeywords{##1}% + \def\xmpcomma{,}% + \let\xmpquote=\relax + }% +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@ProcessKeyvalOptions} +% \changes{v2.0}{2012/09/05}{Added this macro} +% \begin{macro}{\ProcessKeyvalOptions} +% \changes{v2.0}{2012/09/05}{Added this macro} +% Redefine \pkgname{kvoptions}'s |\ProcessOptions| command to invoke +% |\hyxmp@redefine@Hyp| before performing its normal option processing. +% \begin{macrocode} +\let\hyxmp@ProcessKeyvalOptions=\ProcessKeyvalOptions +\renewcommand*{\ProcessKeyvalOptions}{% + \hyxmp@redefine@Hyp + \hyxmp@ProcessKeyvalOptions +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@hypersetup} +% \changes{v2.1}{2012/09/16}{Added this macro} +% \begin{macro}{\hypersetup} +% \changes{v2.1}{2012/09/16}{Added this macro} +% Redefine \pkgname{hyperref}'s |\hypersetup| command to invoke +% |\hyxmp@redefine@Hyp| before performing its normal option processing. +% \begin{macrocode} +\let\hyxmp@hypersetup=\hypersetup +\def\hypersetup{% + \hyxmp@redefine@Hyp + \hyxmp@hypersetup +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@find@metadata} +% \begin{macro}{\hyxmp@concated@metadata} +% Issue a warning message if the author failed to specify any metadata +% at all. This excludes metadata that is included automatically such as +% the current timestamp. Note that we don't consider |\@pdfmetalang| as +% metadata as that value is meaningful only when used in conjunction +% with other information. We also don't examine |\@pdfapart| or +% |\@pdfaconformance| because those have nonempty default values. +% \begin{macrocode} +\newcommand*{\hyxmp@find@metadata}{% + \edef\hyxmp@concated@metadata{% + \@baseurl + \@pdfauthor + \@pdfauthortitle + \@pdfbookedition + \@pdfbytes + \@pdfcaptionwriter + \@pdfcontactaddress + \@pdfcontactcity + \@pdfcontactcountry + \@pdfcontactemail + \@pdfcontactphone + \@pdfcontactpostcode + \@pdfcontactregion + \@pdfcontacturl + \@pdfcopyright + \@pdfcreationdate + \@pdfdatetime + \@pdfdoi + \@pdfeissn + \@pdfisbn + \@pdfissn + \@pdfissuenum + \@pdfkeywords + \@pdflang + \@pdflicenseurl + \@pdfmetadatetime + \@pdfmoddate + \@pdfnumpages + \@pdfpagerange + \@pdfpublication + \@pdfpubtype + \@pdfsubject + \@pdfsubtitle + \@pdftitle + \@pdftype + \@pdfurl + \@pdfversionid + \@pdfvolumenum + }% + \ifx\hyxmp@concated@metadata\@empty + \PackageWarningNoLine{hyperxmp}{% + \jobname.tex did not specify any metadata to\MessageBreak + include in the XMP packet.\space\space Please see the\MessageBreak + hyperxmp documentation for instructions on how to\MessageBreak + provide metadata values to hyperxmp}% + \fi +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% Rather than load \pkgname{hyperref} ourself we let the author do it +% then verify he actually did. This approach gives the author the +% flexibility to load \pkgname{hyperxmp} and \pkgname{hyperref} in +% either order and to call |\hypersetup| anywhere in the document's +% preamble, not just before \pkgname{hyperxmp} is loaded. +% \begin{macrocode} +\AtBeginDocument{% + \@ifpackageloaded{hyperref}{% +% \end{macrocode} +% In older versions of \pkgname{hyperref}, |\@pdflang| is set to +% |\@empty| if \optname{pdflang} is not specified. In newer versions of +% \pkgname{hyperref}, |\@pdflang| is set to |\relax| if +% \optname{pdflang} is not specified. The latter is a bit problematic +% for \pkgname{hyperxmp} because it makes |\@pdflang| non-expandable, +% which causes a literal ``|\@pdflang|'' to be written as \acro{XMP} +% metadata. To avoid that situation we redefine |\@pdflang| as +% |\@empty| if we see it set to |\relax|. +% \changes{v2.3a}{2013/04/16}{Bug fix: Redefine \cs{@pdflang} as +% \cs{@empty} when \protect\pkgname{hyperref} has set +% it to \cs{relax}} +% \begin{macrocode} + \ifx\@pdflang\relax + \let\@pdflang=\@empty + \fi +% \end{macrocode} +% If the author explicitly specified the language to use for the +% document's metadata, we use that. If not, we use the document +% language, specified to \pkgname{hyperref} with the \protect\optname{pdflang} +% option. If the author did not specify a language, we use |x-default| +% as the metadata language. +% \changes{v1.3}{2011/04/25}{Introduced the \protect\optname{pdfmetalang} +% package option, which enables an author to specify the language in which he +% wrote the document's metadata} +% \changes{v2.0}{2012/08/02}{New \cs{AtBeginDocument} code from Heiko +% Oberdiek to properly encode \cs{@pdfmetalang}} +% \changes{v3.3}{2017/07/21}{Don't overwrite an existing +% \protect\optname{pdfmetalang} with \protect\optname{pdflang} or +% \protect\texttt{x-default}. This addresses a bug report by Niklas +% Beisert} +% \begin{macrocode} + \ifx\@pdfmetalang\@empty + \ifx\@pdflang\@empty + \let\@pdfmetalang=\hyxmp@x@default + \else + \edef\@pdfmetalang{\@pdflang}% + \fi + \fi + \hyxmp@xmlify\@pdfmetalang +% \end{macrocode} +% If the author explicitly specified the document date, override the +% compilation timestamp with the specified date. +% \begin{macrocode} + \ifx\@pdfdatetime\@empty + \else + \edef\hyxmp@today{\@pdfdatetime}% + \fi +% \end{macrocode} +% If the author left \optname{pdftitle} blank but specified |\title|, +% use the title for \optname{pdftitle}. Likewise, if the author left +% \optname{pdfauthor} blank but specified |\author|, use the author for +% \optname{pdfauthor}. +% \changes{v2.7}{2016/02/01}{Automatically use \cs{title} and \cs{author} +% if \protect\optname{pdftitle} and \protect\optname{pdfauthor} are left +% unspecified. Thanks to Maciej Radziejewski for the suggestion} +% \begin{macrocode} + \@ifmtargexp{\@pdftitle}{% + \@ifnotmtargexp{\@title}{% + \hypersetup{pdftitle={\@title}}% + }% + }% + {}% + \@ifmtargexp{\@pdfauthor}{% + \@ifnotmtargexp{\@author}{% + \hypersetup{pdfauthor={\@author}}% + }% + }% + {}% +% \end{macrocode} +% Older versions of \pkgname{hyperref} write the \pdfterm{Info} +% dictionary to the \acro{PDF} file at the end of the document. New +% versions of \pkgname{hyperref} write the \pdfterm{Info} dictionary to +% the \acro{PDF} file at the \emph{beginning} of the document. For +% compatibility with both old and new \pkgname{hyperref} implementations +% we suppress writing the \pdfterm{Info} dictionary here, at the +% beginning of the document. +% \changes{v4.1}{2019/04/02}{Invoke +% \protect\cs{hyxmp@suppress@pdf@metadata} at the beginning of the +% document, for compatibility with both newer and older versions of +% \protect\pkgname{hyperref}} +% \begin{macrocode} + \hyxmp@suppress@pdf@metadata +% \end{macrocode} +% We wait until the end of the document to construct the \acro{XMP} +% packet and write it to the \acro{PDF} document catalog. This gives +% the author ample opportunity to provide metadata to \pkgname{hyperref} +% and thereby \pkgname{hyperxmp}. +% \begin{macrocode} + \hyxmp@at@end{% + \hyxmp@find@metadata + \hyxmp@embed@packet + }% + }{% + \PackageWarningNoLine{hyperxmp}{% + \jobname.tex failed to include a\MessageBreak + \string\usepackage\string{hyperref\string} + in the preamble.\MessageBreak + Consequently, all hyperxmp functionality will be\MessageBreak + disabled}% + }% +} +% \end{macrocode} +% +% +% \subsection{Manipulating author-supplied data} +% +% The author provides metadata information to \pkgname{hyperxmp} via +% package options to \pkgname{hyperref} or via \pkgname{hyperref}'s +% |\hypersetup| command. The functions in this section convert +% author-supplied lists (e.g.,~|pdfkeywords={foo, bar, baz}|) into +% \LaTeX\ lists (e.g.,~|\@elt {foo}| |\@elt {bar}| |\@elt {baz}|) that +% can be more easily manipulated (Section~\ref{sec:list-manip}); trim +% spaces off the ends of strings (Section~\ref{sec:trim-spaces}); and, +% in Section~\ref{sec:text-xml}, convert text to \acro{XML} (e.g.,~from +% || to |<scott+hyxmp@pakin.org>|). +% +% \subsubsection{List manipulation} +% \label{sec:list-manip} +% +% We define a macro for converting a list of comma-separated elements +% (e.g.,~the list of \acro{PDF} keywords) to a list of \LaTeX\ +% |\@elt|-separated elements. +% +% \begin{macro}{\hyxmp@commas@to@list} +% Given a macro name~(|#1|) and a comma-separated list~(|#2|), define +% the macro name as the elements of the list, each preceded by |\@elt|. +% (Executing the macro therefore applies |\@elt| to each element in +% turn.) +% \begin{macrocode} +\newcommand*{\hyxmp@commas@to@list}[2]{% + \gdef#1{}% + \expandafter\hyxmp@commas@to@list@i\expandafter#1#2,,% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@commas@to@list@i} +% \begin{macro}{\next} +% Recursively construct macro~|#1| from comma-separated list~|#2|. Stop +% if |#2| is empty. +% \begin{macrocode} +\def\hyxmp@commas@to@list@i#1#2,{% + \gdef\hyxmp@sublist{#2}% + \ifx\hyxmp@sublist\@empty + \let\next=\relax + \else + \hyxmp@trimspaces\hyxmp@sublist + \@cons{#1}{{\hyxmp@sublist}}% + \def\next{\hyxmp@commas@to@list@i{#1}}% + \fi + \next +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\xmpcomma} +% Because \pkgname{hyperxmp} splits lists at commas, a comma cannot +% normally be used within a list. We there provide an |\xmpcomma| macro +% that can expand to either a true comma or a placeholder character +% depending on the situation. Here, we bind it to a comma so it can be +% used in \emph{any} \pkgname{hyperxmp} option, not just those that treat +% commas specially. +% \changes{v2.0}{2012/08/25}{Added this macro} +% \changes{v2.2}{2012/12/07}{Changed the default from +% \texttt{\string\string\string\relax} to an ordinary comma} +% \begin{macrocode} +\def\xmpcomma{,}% +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@comma} +% This is what |\xmpcomma| maps to during list construction. We assume +% that documents will never otherwise use an \acro{ETX} (|^^C|) +% character in their \acro{XMP} metadata. +% \changes{v2.0}{2012/08/25}{Added this macro} +% \begin{macrocode} +\bgroup + \catcode`\^^C=11 + \gdef\hyxmp@comma{^^C} +\egroup +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@uscore} +% This is what |\_| temporarily maps to during packet construction. +% Because underscores are replaced by spaces, we need a mechanism to +% preserve user-specified underscores (e.g.,~in email addresses). We +% assume that documents will never otherwise use an \acro{NAK} (|^^U|) +% character in their \acro{XMP} metadata. +% \changes{v2.5}{2014/06/19}{Added this macro} +% \begin{macrocode} +\bgroup + \catcode`\^^U=11 + \gdef\hyxmp@uscore{^^U} +\egroup +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\xmpquote} +% Adobe Acrobat likes to see double quotes around list elements that +% contain commas when the entire list appears within a single \acro{XMP} +% tag (e.g.,~||). However, it doesn't like to see double +% quotes around list elements that contain commas when the list is +% broken up into individual components (i.e.,~using || tags). +% We therefore introduce an |\xmpquote| macro that quotes or doesn't +% quote its argument based on context. Here, we bind |\xmpquote| to +% |\relax| to prevent it from prematurely quoting or not quoting. +% \changes{v2.0}{2012/09/10}{Added this macro} +% \begin{macrocode} +\let\xmpquote=\relax +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\xmptilde} +% As a convenience for the user, we define |\xmptilde| as a category~12 +% (other) ``|~|'' character. +% \changes{v2.4}{2014/01/01}{Added this macro} +% \begin{macrocode} +\bgroup + \catcode`\~=12% + \gdef\xmptilde{~}% +\egroup +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\XMPTruncateList} +% \changes{v2.0}{2012/09/07}{Added this macro} +% \changes{v2.3b}{2013/07/18}{Made all definitions local to avoid +% spurious \texttt{Too many unprocessed floats} errors when running +% with \string\pkgname{memoir}} +% \changes{v4.0}{2019/03/31}{Deprecated this macro} +% \begin{macro}{\hyxmp@temp@str} +% \begin{macro}{\hyxmp@temp@list} +% \begin{macro}{\@elt} +% As a workaround for the inability of older Adobe Acrobat versions to +% display author lists correctly we introduce a hack that replaces a +% list with its first element. One can then write +% ``|\XMPTruncateList{pdfauthor}|'' and have Adobe Acrobat display the +% author list correctly. +% \begin{macrocode} +\newcommand{\XMPTruncateList}[1]{{% + \PackageWarning{hyperxmp}{% + \noexpand\XMPTruncateList has been deprecated since\MessageBreak + hyperxmp 4.0 and may be removed in future\MessageBreak + versions of the package. \noexpand\XMPTruncateList\MessageBreak + was found}% + \edef\hyxmp@temp@str{\csname hyxmp@#1\endcsname}% + \hyxmp@commas@to@list{\hyxmp@temp@list}{\hyxmp@temp@str}% + \def\@elt##1{% + \expandafter\gdef\csname @#1\endcsname{##1}% + \let\@elt=\@gobble + } + \hyxmp@temp@list +}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% +% \subsubsection{Date manipulation} +% \label{sec:date-manip} +% +% \pkgname{hyperxmp} needs to manipulate two types of date (really, +% timestamp) formats: \acro{PDF} format and \acro{XMP} format. +% \acro{PDF} timestamps are of the form +% ``|D:|\textsc{yyyymmdd}hhmmss|+|\textsc{tt}|'|tt|'|'' +% \makeatletter +% \@ifundefined{pdffeedback}{^^A +% \@ifundefined{pdfcreationdate}{^^A +% }{^^A +% (e.g.,~\texttt{\pdfcreationdate})^^A +% }^^A +% }{^^A +% (e.g.,~\texttt{\pdffeedback creationdate})^^A +% } +% \makeatother +% ~\cite{Adobe2008:PDF}, while \acro{XMP} timestamps are of the form +% ``\textsc{yyyy}|-|\textsc{mm}|-|\textsc{dd}|T|hh|:|mm|:|ss|+|\textsc{tt}|:|tt'' +% \makeatletter +% \@ifundefined{pdffeedback}{^^A +% \@ifundefined{pdfcreationdate}{^^A +% \unskip +% }{^^A +% (e.g.,~\texttt{\expandafter\hyxmp@pdf@to@xmp@date\pdfcreationdate})^^A +% }^^A +% }{^^A +% (e.g.,~\texttt{\expandafter\hyxmp@pdf@to@xmp@date\pdffeedback creationdate})^^A +% }^^A +% \makeatother +% ~\cite{Adobe2012:XMP}. The |\hyxmp@as@pdf@date| and +% |\hyxmp@as@xmp@date| macros defined in this section facilitate +% timestamp conversions to \acro{PDF} and \acro{XMP} formats, +% respectively. +% +% \begin{macro}{\hyxmp@first@char} +% \begin{macro}{\hyxmp@first@char@i} +% Return the first character of a string. This macro is fully expandable. +% \begin{macrocode} +\def\hyxmp@first@char#1{\hyxmp@first@char@i#1\relax} +\def\hyxmp@first@char@i#1#2\relax{#1} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@as@xmp@date} +% If necessary, convert a timestamp to \acro{XMP} format. That is, if +% the timestamp is in \acro{PDF} format, convert it; otherwise, leave it +% unmodified. This macro is fully expandable. +% \changes{v3.2}{2017/02/20}{Added this macro} +% \begin{macrocode} +\def\hyxmp@as@xmp@date#1{% + \expandafter\ifx\hyxmp@first@char@i#1\relax D% + \hyxmp@pdf@to@xmp@date{#1}% + \else + #1% + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@pdf@to@xmp@date} +% Convert a timestamp from \acro{PDF} format to \acro{XMP} format. This +% macro is fully expandable. +% \changes{v2.4}{2013/12/24}{Added this macro} +% \begin{macrocode} +\def\hyxmp@pdf@to@xmp@date#1:#2#3#4#5#6#7#8#9{% + #2#3#4#5-#6#7-#8#9% + \hyxmp@parse@time +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@parse@time} +% This is a helper function for |\hyxmp@pdf@to@xmp@date|. +% |\hyxmp@pdf@to@xmp@date| proper parses only the year, month, and day +% then calls |\hyxmp@parse@time|. |\hyxmp@parse@time| parses the +% hours, minutes, and seconds then calls |\hyxmp@parse@tz@char|. +% \changes{v2.4}{2013/12/24}{Added this macro} +% \begin{macrocode} +\def\hyxmp@parse@time#1#2#3#4#5#6{% + T#1#2:#3#4:#5#6% + \hyxmp@parse@tz@char +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@parse@tz@char} +% This is another helper function for |\hyxmp@pdf@to@xmp@date|. So far, +% the date and time have been parsed. |\hyxmp@parse@tz@char| parses the +% first character of the timezone descriptor. This can be one of +% ``|+|'' for eastern timezones (\mbox{UTC$+x$}, including Asia, +% Oceania, and most of Europe), ``|-|'' for western timezones +% (\mbox{UTC$-x$}, primarily the Americas), or ``|Z|'' for Zulu time +% (\mbox{UTC$+0$}). Timezones beginning with ``|+|'' or ``|-|'' are +% followed by an offset in hours and minutes (parsed by +% |\hyxmp@parse@tz|; timezones beginning with ``|Z|'' are not. +% \changes{v2.4}{2013/12/24}{Added this macro} +% \begin{macrocode} +\def\hyxmp@parse@tz@char#1{% + #1% + \ifx#1-% + \expandafter\hyxmp@parse@tz + \else + \ifx#1+% + \expandafter\hyxmp@parse@tz + \fi + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@parse@tz} +% This is the final helper function for |\hyxmp@pdf@to@xmp@date|. It +% parses the piece of the timezone comprising the offset from +% Coordinated Universal Time, measured in hours and minutes. +% \changes{v2.4}{2013/12/24}{Added this macro} +% \begin{macrocode} +\def\hyxmp@parse@tz#1'#2'{% + #1:#2% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@as@pdf@date} +% If necessary, convert a timestamp to \acro{PDF} format. That is, if +% the timestamp is in \acro{XMP} format, convert it; otherwise, leave it +% unmodified. This macro is fully expandable. +% \changes{v3.2}{2017/02/20}{Added this macro} +% \begin{macrocode} +\def\hyxmp@as@pdf@date#1{% + \expandafter\ifx\hyxmp@first@char@i#1\relax D% + #1% + \else + \hyxmp@xmp@to@pdf@date{#1}% + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@xmp@to@pdf@date} +% Convert a timestamp from \acro{XMP} format to \acro{PDF} format. This +% macro is fully expandable. +% \changes{v2.4}{2013/12/24}{Added this macro} +% \begin{macrocode} +\def\hyxmp@xmp@to@pdf@date#1{% + D:\hyxmp@xmp@to@pdf@date@i#1\relax\relax +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@xmp@to@pdf@date@i} +% Parse the year for |\hyxmp@xmp@to@pdf@date|. +% \begin{macrocode} +\def\hyxmp@xmp@to@pdf@date@i#1#2#3#4#5#6{% + #1#2#3#4% + \ifx#5-% + \expandafter\hyxmp@xmp@to@pdf@date@ii\expandafter#6% + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@xmp@to@pdf@date@ii} +% Parse the month for |\hyxmp@xmp@to@pdf@date|. +% \begin{macrocode} +\def\hyxmp@xmp@to@pdf@date@ii#1#2#3#4{% + #1#2% + \ifx#3-% + \expandafter\hyxmp@xmp@to@pdf@date@iii\expandafter#4% + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@xmp@to@pdf@date@iii} +% Parse the day for |\hyxmp@xmp@to@pdf@date|. +% \begin{macrocode} +\def\hyxmp@xmp@to@pdf@date@iii#1#2#3#4{% + #1#2% + \ifx#3T% + \expandafter\hyxmp@xmp@to@pdf@date@iv\expandafter#4% + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@xmp@to@pdf@date@iv} +% Parse the hour for |\hyxmp@xmp@to@pdf@date|. +% \begin{macrocode} +\def\hyxmp@xmp@to@pdf@date@iv#1#2#3#4{% + #1#2% + \ifx#3:% + \expandafter\hyxmp@xmp@to@pdf@date@v\expandafter#4% + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@xmp@to@pdf@date@v} +% Parse the minute for |\hyxmp@xmp@to@pdf@date|. +% \begin{macrocode} +\def\hyxmp@xmp@to@pdf@date@v#1#2#3#4{% + #1#2% + \ifx#3:% + \expandafter\hyxmp@xmp@to@pdf@date@vi\expandafter#4% + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@gobbletwo} +% This is exactly the same as \LaTeXe's |\@gobbletwo| but needs to be a +% different literal for |\hyxmp@xmp@to@pdf@date@vii|'s pattern-matching +% to work. +% \begin{macrocode} +\let\hyxmp@gobbletwo=\@gobbletwo +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@xmp@to@pdf@date@vi} +% Parse the second for |\hyxmp@xmp@to@pdf@date|. The challenge here is +% that we need to handle four cases for the character following the +% seconds---``|+|'', ``|-|'', ``|Z|'', and no character---without sacrificing +% expandability. Our tricky solution is to insert a |\@gobbletwo| as a +% sentinel and let |\hyxmp@xmp@to@pdf@date@vi| discard everything up to +% that sentinel (i.e.,~all the other conditionals). +% \begin{macrocode} +\def\hyxmp@xmp@to@pdf@date@vi#1#2#3#4{% + #1#2% + \ifx#3+% + +\expandafter\hyxmp@xmp@to@pdf@date@vii + \fi + \ifx#3-% + -\expandafter\hyxmp@xmp@to@pdf@date@vii + \fi + \ifx#3Z% + Z% + \fi + \ifx#3\relax + \expandafter\hyxmp@gobbletwo + \fi + \@gobbletwo #4% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@xmp@to@pdf@date@vii} +% Parse the time-zone hours for |\hyxmp@xmp@to@pdf@date|. +% \begin{macrocode} +\def\hyxmp@xmp@to@pdf@date@vii#1\@gobbletwo#2#3#4#5{% + #2#3% + \ifx#4:% + \expandafter\hyxmp@xmp@to@pdf@date@viii\expandafter#5% + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@xmp@to@pdf@date@viii} +% Parse the time-zone minutes for |\hyxmp@xmp@to@pdf@date|. +% \begin{macrocode} +\def\hyxmp@xmp@to@pdf@date@viii#1#2#3#4{% + '#1#2'% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@today@define} +% Use \tex\ primitives to define a given macro as today's date in +% \textsc{yyyy}-\textsc{mm}-\textsc{dd}|T|hh|:|mm format. +% \changes{v2.4}{2013/12/21}{Added this macro} +% \changes{v3.0}{2016/07/03}{Modified to accept the name of a macro +% to define} +% \changes{v3.2}{2017/02/21}{Modified to include hours and minutes} +% \begin{macrocode} +\def\hyxmp@today@define#1{% +% \end{macrocode} +% The date is a straightforward representation of \tex's |\year|, +% |\month|, and |\day| primitives, with the latter two zero-padded to +% two digits apiece. +% \begin{macrocode} + \xdef#1{\the\year}% + \ifnum\month<10 + \xdef#1{#1-0\the\month}% + \else + \xdef#1{#1-\the\month}% + \fi + \ifnum\day<10 + \xdef#1{#1-0\the\day}% + \else + \xdef#1{#1-\the\day}% + \fi +% \end{macrocode} +% \tex\ does not provide the time in terms of separate hours and minutes +% but rather as the total number of minutes since midnight (|\time|). +% There's no mechanism in \tex\ to query the number of seconds since +% midnight or the timezone so we omit those fields when defining +% macro~|#1|. +% \begin{macrocode} + \@tempcnta=\time + \divide\@tempcnta by 60% + \ifnum\@tempcnta<10% + \xdef#1{#1T0\the\@tempcnta}% + \else + \xdef#1{#1T\the\@tempcnta}% + \fi + \multiply\@tempcnta by -60% + \advance\@tempcnta by \time + \ifnum\@tempcnta<10% + \xdef#1{#1:0\the\@tempcnta}% + \else + \xdef#1{#1:\the\@tempcnta}% + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@today} +% Define |\hyxmp@today| as the current date and (if available) time and +% timezone in \acro{XMP} \xmpterm{Date} format~\cite{Adobe2012:XMP}. +% \changes{v2.4}{2013/12/24}{Modified the code to parse the time and +% timezone from \texttt{\string\string\string\pdfcreationdate}, as +% proposed by Florian Breitwieser} +% \begin{macrocode} +\@ifundefined{pdffeedback}{% + \@ifundefined{pdfcreationdate}{% +% \end{macrocode} +% Case 1: Neither |\pdffeedback| nor |\pdfcreationdate| is defined +% (\XeLaTeX\ and regular \LaTeX). +% \begin{macrocode} + \hyxmp@today@define\hyxmp@today + }{% +% \end{macrocode} +% Case 2: |\pdfcreationdate| is defined (\pdfLaTeX\ and pre-0.85 \LuaLaTeX). +% \begin{macrocode} + \edef\hyxmp@today{\expandafter\hyxmp@pdf@to@xmp@date\pdfcreationdate}% + }% +}{% +% \end{macrocode} +% Case 3: |\pdffeedback| is defined (\LuaLaTeX~0.85+). +% \begin{macrocode} + \edef\hyxmp@today{\expandafter\hyxmp@pdf@to@xmp@date\pdffeedback creationdate}% +} +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{Trimming leading and trailing spaces} +% \label{sec:trim-spaces} +% +% To make it easier for \acro{XMP} processors to manipulate our output we +% define a |\hyxmp@trimspaces| macro to strip leading and trailing +% spaces from various data fields. +% +% \begin{macro}{\hyxmp@trimspaces} +% Redefine a macro as its previous value but without leading or trailing +% spaces. This code---as well as that for its helper macros, +% |\hyxmp@trimb| and |\hyxmp@trimc|---was taken almost verbatim from a +% solution to an \emph{Around the Bend} puzzle~\cite{Downes1994:ATB15}. +% Inline comments are also taken from the solution text. +% \begin{macrocode} +\catcode`\Q=3 +% \end{macrocode} +% |\hyxmp@trimspaces\x| redefines |\x| to have the same replacement text +% sans leading and trailing space tokens. +% \begin{macrocode} +\newcommand{\hyxmp@trimspaces}[1]{% +% \end{macrocode} +% Use grouping to emulate a multi-token |afterassignment| queue. +% \begin{macrocode} + \begingroup +% \end{macrocode} +% Put ``|\toks 0 {|'' into the |afterassignment| queue. +% \begin{macrocode} + \aftergroup\toks\aftergroup0\aftergroup{% +% \end{macrocode} +% Apply |\hyxmp@trimb| to the replacement text of |#1|, adding a leading +% |\noexpand| to prevent brace stripping and to serve another purpose +% later. +% \begin{macrocode} + \expandafter\hyxmp@trimb\expandafter\noexpand#1Q Q}% +% \end{macrocode} +% Transfer the trimmed text back into |#1|. +% \begin{macrocode} + \edef#1{\the\toks0}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@trimb} +% |\hyxmp@trimb| removes a trailing space if present, then calls +% |\hyxmp@trimc| to clean up any leftover bizarre |Q|s, and trim a +% leading space. In order for |\hyxmp@trimc| to work properly we need to +% put back a |Q| first. +% \begin{macrocode} +\def\hyxmp@trimb#1 Q{\hyxmp@trimc#1Q} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@trimc} +% Execute |\vfuzz| assignment to remove leading space; the |\noexpand| +% will now prevent unwanted expansion of a macro or other expandable +% token at the beginning of the trimmed text. The |\endgroup| will feed +% in the |\aftergroup| tokens after the |\vfuzz| assignment is +% completed. +% \begin{macrocode} +\def\hyxmp@trimc#1Q#2{\afterassignment\endgroup \vfuzz\the\vfuzz#1} +\catcode`\Q=11 +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{Converting text to XML} +% \label{sec:text-xml} +% +% The ``|<|'', ``|>|'', and ``|&|'' characters are significant to \acro{XML}. +% We therefore need to escape them in any author-supplied text. +% +% \begin{macro}{\ifhyxmp@unicodetex} +% \changes{v2.0}{2012/08/02}{Added by Heiko Oberdiek} +% \begin{macro}{\hyxmp@unicodetextrue} +% \begin{macro}{\hyxmp@unicodetexfalse} +% \XeTeX\ and \LuaTeX\ natively support \term{Unicode}. We define the +% conditional |\ifhyxmp@unicodetex| to check for these so we can +% properly handle encoding conversions. The trick here is that +% \term{Unicode} \tex\ implementations compare decimal~64 to +% hexadecimal~40 (decimal~64), specified with four carets, and take the +% \textsc{true} branch; non-\term{Unicode} \tex\ implementations compare +% decimal~64 to character~``|^|'' (decimal~94), ignore the ``|^^0040|'' +% and the rest of the \textsc{true} branch, and take the \textsc{false} +% branch. +% \begin{macrocode} +\newif\ifhyxmp@unicodetex +\ifnum64=`\^^^^0040\relax + \hyxmp@unicodetextrue +\else + \hyxmp@unicodetexfalse +\fi +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@reencode} +% This is now a placeholder macro needed only for |\@pdfmetalang| in +% the |\begin{document}|. +% \changes{v1.3}{2011/04/25}{Introduced this macro to re-encode Unicode +% strings as 8-bit strings before manipulating them into \acro{XMP} +% schema. This change addresses a bug reported by Martin M\"unch} +% \changes{v2.0}{2012/08/02}{Replaced with an empty macro by Heiko Oberdiek} +% \begin{macrocode} +\newcommand*{\hyxmp@reencode}[1]{} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\SE->pdfdoc@03} +% Preserve \acro{ETX} (|^^C|), which is normally an invalid character in +% \term{PDFDocEncoding}. We use it in \pkgname{hyperxmp} (and +% specifically in |\hyxmp@xmlify| below) as a list-element separator. +% \begin{macrocode} +\expandafter\def\csname SE->pdfdoc@03\endcsname{0003} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\SE->pdfdoc@15} +% Preserve \acro{NAK} (|^^U|), which is normally an invalid character in +% \term{PDFDocEncoding}. We use it in \pkgname{hyperxmp} (and +% specifically in |\hyxmp@xmlify| below) as a placeholder for an +% underscore character. +% \begin{macrocode} +\expandafter\def\csname SE->pdfdoc@15\endcsname{0015} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@xmlify} +% \changes{v2.0}{2012/08/02}{Completely rewritten by Heiko Oberdiek to +% better support Unicode-enabled \TeX\ programs} +% \begin{macro}{\hyxmp@xmlified} +% \begin{macro}{\hyxmp@text} +% Given a piece of text defined using |\pdfstringdef| (i.e.,~with many +% special characters redefined to have category code~11), set +% |\hyxmp@xmlified| to the same text but with all occurrences of~``|<|'' +% replaced with~|<|, all occurrences of~``|>|'' replaced with~|>|, +% and all occurrences of~``|&|'' replaced with~|&|. +% \begin{macrocode} +\newcommand*{\hyxmp@xmlify}[1]{% + \gdef\hyxmp@xmlified{}% +% \end{macrocode} +% Escaped \acro{PDF} string~$\rightarrow$ \term{PDFDocEncoding}/\term{Unicode} +% \begin{macrocode} + \EdefUnescapeString\hyxmp@text{#1}% + \ifhyxmp@unicodetex +% \end{macrocode} +% \term{PDFDocEncoding}/\term{Unicode}~$\rightarrow$ \acro{UTF-32BE} +% \begin{macrocode} + \hyxmp@is@unicode\hyxmp@text{% + \StringEncodingConvert + \hyxmp@text\hyxmp@text{utf16be}{utf32be}% + }{% + \ifxetex + \hyxmp@xetex@crap + \else + \StringEncodingConvert + \hyxmp@text\hyxmp@text{pdfdoc}{utf32be}% + \fi + }% +% \end{macrocode} +% \acro{UTF-32BE}~$\rightarrow$ \acro{UTF-32BE} as hex string +% \begin{macrocode} + \EdefEscapeHex\hyxmp@text{\hyxmp@text}% +% \end{macrocode} +% \acro{UTF-32BE}~$\rightarrow$ \acro{XML} in \acro{ASCII} +% \begin{macrocode} + \edef\hyxmp@text{% + \expandafter + }\expandafter\hyxmp@toxml@unicodetex\hyxmp@text + \relax\relax\relax\relax\relax\relax\relax\relax + \else +% \end{macrocode} +% \term{PDFDocEncoding}/\term{Unicode}~$\rightarrow$ \acro{UTF-8} +% \begin{macrocode} + \hyxmp@is@unicode\hyxmp@text{% + \StringEncodingConvert + \hyxmp@text\hyxmp@text{utf16be}{utf8}% + }{% + \StringEncodingConvert + \hyxmp@text\hyxmp@text{pdfdoc}{utf8}% + }% +% \end{macrocode} +% \acro{UTF-8}~$\rightarrow$ \acro{UTF-8} as hex string +% \begin{macrocode} + \EdefEscapeHex\hyxmp@text{\hyxmp@text}% +% \end{macrocode} +% \acro{UTF-8} as hex string~$\rightarrow$ \acro{XML} in \acro{UTF-8} +% as hex string +% \begin{macrocode} + \edef\hyxmp@text{% + \expandafter\hyxmp@toxml\hyxmp@text\@empty\@empty + }% +% \end{macrocode} +% \acro{XML} in \acro{UTF-8} as hex string~$\rightarrow$ \acro{XML} +% in \acro{UTF-8} +% \begin{macrocode} + \EdefUnescapeHex\hyxmp@text{\hyxmp@text}% + \fi + \global\let\hyxmp@xmlified\hyxmp@text +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@is@unicode} +% \changes{v2.0}{2012/08/02}{Added by Heiko Oberdiek} +% \begin{macro}{\hyxmp@@is@unicode} +% Given a string and two expressions, evaluate the first expression if +% the string is \acro{UTF-16BE}-encoded and the second expression if not. +% \begin{macrocode} +\begingroup + \lccode`\<=254 % + \lccode`\>=255 % + \catcode254=12 % + \catcode255=12 % +\lowercase{\endgroup + \def\hyxmp@is@unicode#1{% + \expandafter\hyxmp@@is@unicode#1<>\@nil + }% + \def\hyxmp@@is@unicode#1<>#2\@nil{% + \ifx\\#1\\% + \expandafter\@firstoftwo + \else + \expandafter\@secondoftwo + \fi + }% +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@toxml} +% \changes{v2.0}{2012/08/02}{Added by Heiko Oberdiek} +% Replace the characters ``\textless'', ``\&'', and ``\textgreater'' with +% \acro{XML} entities when using a non-native-\term{Unicode} +% \tex\ (\tex\ or \pdfTeX). +% \begin{macrocode} +\def\hyxmp@toxml#1#2{% + \ifx#1\@empty + \else + \ifnum"#1#2=`\& % + 26616D703B% & + \else\ifnum"#1#2=`\< % + 266C743B% < + \else\ifnum"#1#2=`\> % + 2667743B% > + \else +% \end{macrocode} +% \cmdname{dvips} wraps text when generating most PostScript code but +% preserves line breaks within strings. Unfortunately, \cmdname{dvips} +% fails to observe the special case in the PostScript specification that +% ``[b]alanced pairs of parentheses in the string require no special +% treatment''~\cite{Adobe1996:postscript}. Consequently, \acro{XMP} +% data containing parentheses (e.g.,~``\texttt{Copyright (C) 1605 Miguel +% de Cervantes}'') confuse \cmdname{dvips} into thinking that the +% string has ended after the closing parenthesis and that line breaks +% can subsequently be injected safely into the document at arbitrary +% points for formatting purposes. This leads to erroneous display by +% \acro{PDF} viewers, which honor line breaks within \acro{XMP} tags. +% The solution is to insert a backslash before all parentheses when in +% \texttt{pdfmark}-generating mode to convince \cmdname{dvips} that the +% entire \acro{XMP} packet must be treated as a single, +% not-to-be-modified string. +% \changes{v2.0}{2012/08/22}{Escaped parentheses written with +% \texttt{pdfmark}s to prevent \cmdname{dvips} from line-wrapping the +% \protect\acro{XMP} packet} +% \begin{macrocode} + \@ifundefined{pdfmark}{% + #1#2% + }{% + \ifnum"#1#2=`\( % + 5C28% \( + \else\ifnum"#1#2=`\) % + 5C29% \) + \else + #1#2% + \fi\fi + }% + \fi\fi\fi + \expandafter\hyxmp@toxml + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@toxml@unicodetex} +% \changes{v2.0}{2012/08/02}{Added by Heiko Oberdiek} +% \begin{macro}{\hyxmp@text} +% Replace the characters ``\textless'', ``\&'', and ``\textgreater'' with +% \acro{XML} entities when using a native-\term{Unicode} +% \tex\ (\XeTeX\ or \LuaTeX). +% \begin{macrocode} +\def\hyxmp@toxml@unicodetex#1#2#3#4#5#6#7#8{% + \ifx#1\relax + \else + \ifnum"#1#2#3#4#5#6#7#8>127 % + \uccode`\*="#1#2#3#4#5#6#7#8\relax + \uppercase{% + \edef\hyxmp@text{\hyxmp@text *}% + }% + \else\ifnum"#7#8=`\< % + \edef\hyxmp@text{\hyxmp@text <}% + \else\ifnum"#7#8=`\& % + \edef\hyxmp@text{\hyxmp@text &}% + \else\ifnum"#7#8=`\> % + \edef\hyxmp@text{\hyxmp@text >}% + \else\ifnum"#7#8=`\ % + \edef\hyxmp@text{\hyxmp@text\space}% + \else + \uccode`\*="#7#8\relax + \uppercase{% + \edef\hyxmp@text{\hyxmp@text *}% + }% + \fi\fi\fi\fi\fi + \expandafter\hyxmp@toxml@unicodetex + \fi +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@skipzeros} +% \changes{v2.0}{2012/08/02}{Added by Heiko Oberdiek} +% Skip over leading zeroes in the input argument. +% \begin{macrocode} +\def\hyxmp@skipzeros#1{% + \ifx#10% + \expandafter\hyxmp@skipzeros + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\x} +% \begin{macro}{\hyxmp@xetex@crap} +% \changes{v2.0}{2012/08/02}{Added by Heiko Oberdiek} +% \begin{macro}{\hyxmp@try} +% \begin{macro}{\hyxmp@crap@result} +% \begin{macro}{\hyxmp@text} +% In the case of \XeTeX, the strings defined by |\pdfstringdef| can +% contain big characters. In this case, the string is treated as +% \term{Unicode}. +% \begin{macrocode} +\begingroup +\def\x#1{\endgroup + \def\hyxmp@xetex@crap{% + \edef\hyxmp@try{% + \expandafter\hyxmp@SpaceOther\hyxmp@text#1\@nil + }% + \let\hyxmp@crap@result=N% + \expandafter\hyxmp@crap@test\hyxmp@try\relax + \ifx\hyxmp@crap@result Y% + \let\hyxmp@text\@empty + \expandafter\hyxmp@crap@convert\hyxmp@try\relax + \else + \StringEncodingConvert\hyxmp@text\hyxmp@text{pdfdoc}{utf32be}% + \fi + }% +} +\x{ } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@SpaceOther} +% Re-encode all spaces in a string with category code~12 (``other''). +% \changes{v2.0}{2012/08/02}{Added by Heiko Oberdiek} +% \begin{macrocode} +\begingroup + \catcode`\~=12 % + \lccode`\~=`\ % +\lowercase{\endgroup + \def\hyxmp@SpaceOther#1 #2\@nil{% + #1% + \ifx\relax#2\relax + \expandafter\@gobble + \else + ~% + \expandafter\@firstofone + \fi + {\hyxmp@SpaceOther#2\@nil}% + }% +} +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\hyxmp@crap@test} +% Determine if we need to treat a string as \term{Unicode}. +% \changes{v2.0}{2012/08/02}{Added by Heiko Oberdiek} +% \begin{macrocode} +\def\hyxmp@crap@test#1{% + \ifx#1\relax + \else + \ifnum`#1>127 % + \let\hyxmp@crap@result=Y% + \expandafter\expandafter\expandafter\hyxmp@skiptorelax + \else + \expandafter\expandafter\expandafter\hyxmp@crap@test + \fi + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@skiptorelax} +% Discard all tokens up to and including the first |\relax|. +% \changes{v2.0}{2012/08/02}{Added by Heiko Oberdiek} +% \begin{macrocode} +\def\hyxmp@skiptorelax#1\relax{} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@crap@convert} +% \changes{v2.0}{2012/08/02}{Added by Heiko Oberdiek} +% \begin{macro}{\hyxmp@num} +% \begin{macro}{\hyxmp@text} +% Convert a hexadecimal string to a number. +% \begin{macrocode} +\def\hyxmp@crap@convert#1{% + \ifx#1\relax + \else + \edef\hyxmp@num{\number`#1}% + \ifnum\hyxmp@num>"FFFFFF % + \lccode`\!=\intcalcDiv{\hyxmp@num}{\number"1000000}\relax + \lowercase{\edef\hyxmp@text{\hyxmp@text!}}% + \edef\hyxmp@num{\intcalcMod{\hyxmp@num}{\number"1000000}}% + \else + \edef\hyxmp@text{\hyxmp@text\hyxmp@zero}% + \fi + \ifnum\hyxmp@num>"FFFF % + \lccode`\!=\intcalcDiv{\hyxmp@num}{\number"10000}\relax + \lowercase{\edef\hyxmp@text{\hyxmp@text!}}% + \edef\hyxmp@num{\intcalcMod{\hyxmp@num}{\number"10000}}% + \else + \edef\hyxmp@text{\hyxmp@text\hyxmp@zero}% + \fi + \ifnum\hyxmp@num>"FF % + \lccode`\!=\intcalcDiv{\hyxmp@num}{\number"100}\relax + \lowercase{\edef\hyxmp@text{\hyxmp@text!}}% + \edef\hyxmp@num{\intcalcMod{\hyxmp@num}{\number"100}}% + \else + \edef\hyxmp@text{\hyxmp@text\hyxmp@zero}% + \fi + \ifnum\hyxmp@num>0 % + \lccode`\!=\hyxmp@num\relax + \lowercase{\edef\hyxmp@text{\hyxmp@text!}}% + \else + \edef\hyxmp@text{\hyxmp@text\hyxmp@zero}% + \fi + \expandafter\hyxmp@crap@convert + \fi +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@zero} +% Define a null character with category code~12 (``other''). +% \changes{v2.0}{2012/08/02}{Added by Heiko Oberdiek} +% \begin{macrocode} +\begingroup + \catcode0=12 % + \gdef\hyxmp@zero{^^00}% +\endgroup +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{Providing metadata in multiple languages} +% \label{sec:lang-alt} +% +% Certain \acro{XMP} tags---\xmpprop{dc:title}, +% \xmpprop{dc:description}, and \xmpprop{dc:rights} (and others? Let me +% know.)---can be expressed in multiple languages. The same text is +% used for both language \optname{pdfmetalang} (default: +% \optname{pdflang}) and language ``|x-default|''. To express the same +% metadata in multiple languages, we provide an |\XMPLangAlt| macro to +% construct a list of alternative forms for a piece of metadata. +% +% \begin{macro}{\hyxmp@alt@title} +% \begin{macro}{\hyxmp@alt@description} +% \begin{macro}{\hyxmp@alt@rights} +% Each of these macros is a list in which each element is of the form +% ``|\do| \meta{language} \meta{text}'' in which \meta{language} is an +% \acro{ISO} \mbox{639-1} two-letter country code with an optional +% \acro{ISO} \mbox{3166-1} two-letter region code. For example, +% |\hyxmp@alt@title| may contain an element, ``|\do| |{es-MX}| +% |{Este| |es| |mi| |documento}|''. +% \begin{macrocode} +\def\hyxmp@alt@title{} +\def\hyxmp@alt@description{} +\def\hyxmp@alt@rights{} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@LA@accept} +% This macro wraps |\define@key| to make the option ``|#1|=\meta{value}'' +% append \meta{value} to list~|#2|. +% \begin{macrocode} +\newcommand{\hyxmp@LA@accept}[2]{% + \define@key{hyxmp@LA}{#1}{% +% \end{macrocode} +% \begin{macro}{\hyxmp@value} +% As Niklas Beisert observed, if the option passed to the current key +% contains \LaTeX\ code, this code will be included in the \acro{XMP} +% packet, which is undesirable. Hence, we first clean up the string +% using |\hyxmp@pdfstringdef|. +% \begin{macrocode} + \hyxmp@pdfstringdef\hyxmp@value{##1}% + \xdef#2{#2\noexpand\do {\hyxmp@cur@lang} {\hyxmp@value}}% + } +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% Define \meta{key}=\meta{value} options for appending to each of the +% |\hyxmp@alt|\meta{tag} lists. +% \begin{macrocode} +\hyxmp@LA@accept{pdftitle}{\hyxmp@alt@title} +\hyxmp@LA@accept{pdfsubject}{\hyxmp@alt@description} +\hyxmp@LA@accept{pdfcopyright}{\hyxmp@alt@rights} +% \end{macrocode} +% +% \begin{macro}{\XMPLangAlt} +% Argument~|#1| is a language expressed as a two-letter country code and +% optional two-letter region code. Argument~|#2| is a list of +% \meta{key}=\meta{value} pairs. Keys correspond to |\hypersetup| +% options such as ``|pdftitle|'', ``|pdfsubject|'', and +% ``|pdfcopyright|''. Values are the alternative-language form of the +% text provided for the corresponding option. +% \changes{v3.3}{2017/07/22}{Added this macro based on a request---and +% some code---by Niklas Beisert to support metadata expressed in multiple +% languages} +% \begin{macrocode} +\newcommand{\XMPLangAlt}[2]{% + \let\do=\relax +% \end{macrocode} +% \begin{macro}{\hyxmp@cur@lang} +% Store the provided language, which will be used during option processing. +% \begin{macrocode} + \edef\hyxmp@cur@lang{#1}% + \setkeys{hyxmp@LA}{#2}% +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% +% \subsection{UUID generation} +% \label{sec:uuid-gen} +% +% We use a linear congruential generator to produce pseudorandom +% version~4 \acro{UUID}s~\cite{Leach2005:uuid}. True, this method has +% its flaws but it's simple to implement in \tex\ and is good enough for +% producing the \acro{XMP} \xmpprop{xmpMM:DocumentID} and +% \xmpprop{xmpMM:InstanceID} fields. +% +% \begin{macro}{\hyxmp@modulo@a} +% Replace the contents of |\@tempcnta| with the contents modulo~|#1|. +% Note that |\@tempcntb| is overwritten in the process. +% \begin{macrocode} +\def\hyxmp@modulo@a#1{% + \@tempcntb=\@tempcnta + \divide\@tempcntb by #1 + \multiply\@tempcntb by #1 + \advance\@tempcnta by -\@tempcntb +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@big@prime} +% \begin{macro}{\hyxmp@big@prime@ii} +% Define a couple of large prime numbers that can still be stored in a +% \tex\ counter. +% \begin{macrocode} +\def\hyxmp@big@prime{536870923} +\def\hyxmp@big@prime@ii{536870027} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@seed@rng} +% \begin{macro}{\hyxmp@one@token} +% Seed \pkgname{hyperxmp}'s random-number generator from a given piece +% of text. +% \begin{macrocode} +\def\hyxmp@seed@rng#1{% + \@tempcnta=\hyxmp@big@prime + \futurelet\hyxmp@one@token\hyxmp@seed@rng@i#1\@empty +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@seed@rng@i} +% \begin{macro}{\hyxmp@one@token} +% \begin{macro}{\next} +% Do all of the work for |\hyxmp@seed@rng|. For each character code $c$ +% of the input text, assign $\mathtt{\string\@tempcnta} \leftarrow 3 +% \cdot \mathtt{\string\@tempcnta} + c +% \pmod{\mathtt{\string\hyxmp@big@prime}}$. +% \begin{macrocode} +\def\hyxmp@seed@rng@i{% + \ifx\hyxmp@one@token\@empty + \let\next=\relax + \else + \def\next##1{% + \multiply\@tempcnta by 3 + \advance\@tempcnta by `##1 + \hyxmp@modulo@a{\hyxmp@big@prime}% + \futurelet\hyxmp@one@token\hyxmp@seed@rng@i + }% + \fi + \next +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@set@rand@num} +% \begin{macro}{\hyxmp@rand@num} +% Advance |\hyxmp@rand@num| to the next pseudorandom number in the +% sequence. Specifically, we assign $\mathtt{\string\hyxmp@rand@num} +% \leftarrow 3 \cdot \mathtt{\string\hyxmp@rand@num} + +% \mathtt{\string\hyxmp@big@prime@ii} +% \pmod{\mathtt{\string\hyxmp@big@prime}}$. Note that both |\@tempcnta| +% and |\@tempcntb| are overwritten in the process. +% \begin{macrocode} +\def\hyxmp@set@rand@num{% + \@tempcnta=\hyxmp@rand@num + \multiply\@tempcnta by 3 + \advance\@tempcnta by \hyxmp@big@prime@ii + \hyxmp@modulo@a{\hyxmp@big@prime}% + \xdef\hyxmp@rand@num{\the\@tempcnta}% +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@append@hex} +% Append a randomly selected hexadecimal digit to macro~|#1|. Note that +% both |\@tempcnta| and |\@tempcntb| are overwritten in the process. +% \begin{macrocode} +\def\hyxmp@append@hex#1{% + \hyxmp@set@rand@num + \@tempcnta=\hyxmp@rand@num + \hyxmp@modulo@a{16}% + \ifnum\@tempcnta<10 + \xdef#1{#1\the\@tempcnta}% + \else +% \end{macrocode} +% There \emph{must} be a better way to handle the numbers~10--15 than +% with |\ifcase|. +% \begin{macrocode} + \advance\@tempcnta by -10 + \ifcase\@tempcnta + \xdef#1{#1a}% + \or\xdef#1{#1b}% + \or\xdef#1{#1c}% + \or\xdef#1{#1d}% + \or\xdef#1{#1e}% + \or\xdef#1{#1f}% + \fi + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@append@hex@iii} +% Invoke |\hyxmp@append@hex| three times. +% \begin{macrocode} +\def\hyxmp@append@hex@iii#1{% + \hyxmp@append@hex#1% + \hyxmp@append@hex#1% + \hyxmp@append@hex#1% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@append@hex@iv} +% Invoke |\hyxmp@append@hex| four times. +% \begin{macrocode} +\def\hyxmp@append@hex@iv#1{% + \hyxmp@append@hex@iii#1% + \hyxmp@append@hex#1% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@create@uuid} +% As per the definition of a version~4 +% \acro{UUID}~\cite{Leach2005:uuid}, define macro~|#1| as a \acro{UUID} +% of the form +% ``|uuid:|\textit{xxxxxxxx}|-|\linebreak[0]\textit{xxxx}|-|\linebreak[0]\textit{4xxx}|-|\linebreak[0]\textit{yxxx}|-|\linebreak[0]\textit{xxxxxxxxxxxx}'' +% in which each ``\textit{x}'' is a lowercase hexadecimal digit and +% ``\textit{y}'' is one of ``|8|'', ``|9|'', ``|a|'', or~``|b|''. We +% assume that the random-number generator is already seeded. Note that +% |\hyxmp@create@uuid| overwrites both |\@tempcnta| and |\@tempcntb|. +% \changes{v2.4}{2014/01/02}{Modified this macro to produce a proper +% version~4 (random or pseudorandom) \protect\acro{UUID}} +% \begin{macrocode} +\def\hyxmp@create@uuid#1{% + \def#1{uuid:}% + \hyxmp@append@hex@iv#1% + \hyxmp@append@hex@iv#1% + \g@addto@macro#1{-}% + \hyxmp@append@hex@iv#1% + \g@addto@macro#1{-4}% + \hyxmp@append@hex@iii#1% + \g@addto@macro#1{-}% +% \end{macrocode} +% Randomly select one of ``|8|'', ``|9|'', ``|a|'', or~``|b|''. +% \begin{macrocode} + \hyxmp@set@rand@num + \@tempcnta=\hyxmp@rand@num + \hyxmp@modulo@a{4}% + \ifcase\@tempcnta + \g@addto@macro#1{8}% + \or\g@addto@macro#1{9}% + \or\g@addto@macro#1{a}% + \or\g@addto@macro#1{b}% + \fi + \hyxmp@append@hex@iii#1% + \g@addto@macro#1{-}% + \hyxmp@append@hex@iv#1% + \hyxmp@append@hex@iv#1% + \hyxmp@append@hex@iv#1% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@def@DocumentID} +% \begin{macro}{\hyxmp@DocumentID} +% \begin{macro}{\hyxmp@seed@string} +% Seed the random-number generator with a function of the current +% filename, \acro{PDF} document title, and \acro{PDF} author, then invoke +% |\hyxmp@create@uuid| to define |\hyxmp@DocumentID| as a random \acro{UUID}. +% \changes{v3.4}{2017/11/04}{Correctly handle an author field of all spaces. +% Bug reported by Ga\"etan Leurent} +% \begin{macrocode} +\newcommand*{\hyxmp@def@DocumentID}{% + \edef\hyxmp@seed@string{\jobname:\@pdftitle:\@pdfauthor:}% + \expandafter\hyxmp@seed@rng\expandafter{\hyxmp@seed@string}% + \edef\hyxmp@rand@num{\the\@tempcnta}% + \hyxmp@create@uuid\hyxmp@DocumentID +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@def@InstanceID} +% \begin{macro}{\hyxmp@InstanceID} +% \begin{macro}{\hyxmp@seed@string} +% Seed the random-number generator with a function of the current +% filename, \acro{PDF} document title, \acro{PDF} author, and the +% current timestamp, then invoke |\hyxmp@create@uuid| to define +% |\hyxmp@InstanceID| as a random \acro{UUID}. For the current +% timestamp, we use both the document-specified timestamp from +% \optname{pdfdate} and the \tex\ time. The former can be more precise +% (to sub-seconds) but may be less random (as it depends on manual +% document modifications) while the latter is typically less precise (to +% minutes) but may be more random (as it is updated automatically). +% \changes{v3.5}{2018/11/27}{Seed with the \protect\TeX\ timestamp in +% addition to the document-specified timestamp} +% \begin{macrocode} +\newcommand*{\hyxmp@def@InstanceID}{% + \hyxmp@today@define{\hyxmp@seed@string}% + \edef\hyxmp@seed@string{% + \jobname:\@pdftitle:\@pdfauthor:\hyxmp@today:\hyxmp@seed@string + }% + \expandafter\hyxmp@seed@rng\expandafter{\hyxmp@seed@string}% + \edef\hyxmp@rand@num{\the\@tempcnta}% + \hyxmp@create@uuid\hyxmp@InstanceID +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% +% \subsection{Constructing the XMP packet} +% +% An \acro{XMP} packet ``shall consist of the following, in order: a +% header \acro{PI}, the serialized \acro{XMP} data model (the \acro{XMP} +% packet) with optional white-space padding, and a trailer +% \acro{PI}''~\cite{Adobe2012:XMP}. (``\acro{PI}'' is an abbreviation +% for ``processing instructions''). The serialized \acro{XMP} includes +% blocks of \acro{XML} for various \acro{XMP} schemata: Adobe \acro{PDF} +% (Section~\ref{sec:adobe-pdf}), Dublin Core +% (Section~\ref{sec:dublin-core}), \acro{XMP} Rights Management +% (Section~\ref{sec:xmp-rights}), \acro{XMP} Media Management +% (Section~\ref{sec:xmp-media}), \acro{XMP} Basic +% (Section~\ref{sec:xmp-basic}), Photoshop +% (Section~\ref{sec:photoshop}), \acro{IPTC} Photo Metadata +% (Section~\ref{sec:photo-meta}), and \acro{PDF/A} Identification +% (Section~\ref{sec:pdfa-id}). The |\hyxmp@construct@packet| macro +% (Section~\ref{sec:combining-schemata}) constructs the \acro{XMP} +% packet into |\hyxmp@xml|. It first writes the appropriate \acro{XML} +% header, then calls the various schema-writing macros, then injects +% |\hyxmp@padding| as padding, and finally writes the appropriate +% \acro{XML} trailer. +% +% \subsubsection{XMP utility functions} +% +% \begin{macro}{\hyxmp@add@to@xml} +% Given a piece of text, replace all underscores with category-code~11 +% (``other'') spaces and all |^C| characters with commas, then append +% the result to the |\hyxmp@xml| macro. +% \changes{v2.0}{2012/08/24}{Updated also to replace commas} +% \changes{v2.5}{2014/06/19}{Updated also to replace underscores and to +% modify only the text being added, not the already-modified text} +% \changes{v2.8}{2014/04/05}{Corrected inadvertent lowercasing of non-Latin +% characters when run under \XeLaTeX\ or \LuaLaTeX\ (bug reported by +% Leonid Sinev)} +% \begin{macrocode} +\newcommand*{\hyxmp@add@to@xml}[1]{% + \bgroup + \@tempcnta=0 + \ifhyxmp@unicodetex + \@tempcntb=65536% + \else + \@tempcntb=256% + \fi + \loop + \lccode\@tempcnta=\@tempcnta + \advance\@tempcnta by 1 + \ifnum\@tempcnta<\@tempcntb + \repeat + \lccode`\_=`\ \relax + \lccode`\^^C=`\,\relax + \lccode`\^^U=`\_\relax + \lowercase{\xdef\hyxmp@new@xml{#1}}% + \xdef\hyxmp@xml{\hyxmp@xml\hyxmp@new@xml}% + \egroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@hash} +% Define a category-code~11 (``other'') version of the ``|#|'' character. +% \begin{macrocode} +\bgroup +\catcode`\#=11 +\gdef\hyxmp@hash{#} +\egroup +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@padding} +% \begin{macro}{\hyxmp@xml} +% The \acro{XMP} specification recommends leaving approximately +% 2000~bytes of whitespace at the end of each \acro{XMP} packet to +% facilitate editing the packet in place~\cite{Adobe2012:XMP}. +% |\hyxmp@padding| is defined to contain 32~lines of 63~spaces and a +% newline apiece for a total of 2048 characters of whitespace. +% \begin{macrocode} +\bgroup + \xdef\hyxmp@xml{}% + \hyxmp@add@to@xml{% +_______________________________________________________________^^J% + } + \xdef\hyxmp@padding{\hyxmp@xml}% +\egroup +\xdef\hyxmp@padding{\hyxmp@padding\hyxmp@padding} +\xdef\hyxmp@padding{\hyxmp@padding\hyxmp@padding} +\xdef\hyxmp@padding{\hyxmp@padding\hyxmp@padding} +\xdef\hyxmp@padding{\hyxmp@padding\hyxmp@padding} +\xdef\hyxmp@padding{\hyxmp@padding\hyxmp@padding} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@x@default} +% Define an |x-default| string that we can use in comparisons with +% |\@pdfmetalang|. +% \begin{macrocode} +\newcommand*{\hyxmp@x@default}{x-default} +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{The Adobe PDF schema} +% \label{sec:adobe-pdf} +% +% \begin{macro}{\hyxmp@pdf@schema} +% Add properties defined by the Adobe \acro{PDF} schema to the |\hyxmp@xml| +% macro. +% \begin{macrocode} +\newcommand*{\hyxmp@pdf@schema}{% +% \end{macrocode} +% Add a block of \acro{XML} to |\hyxmp@xml| that lists the document's +% keywords (the \xmpprop{pdf:Keywords} property), the tools used to +% produce the \acro{PDF} file (the \xmpprop{pdf:Producer} property), and +% the version of the \acro{PDF} standard adhered to (the +% \xmpprop{pdf:PDFVersion} property). Unlike most of the other schemata +% that \pkgname{hyperxmp} supports, the Adobe \acro{PDF} schema is +% \emph{always} included in the document, even if all of its keys are +% empty. This is because \acro{PDF/A}-1b requires the keywords and +% producer to be the same in the \acro{XMP} metadata and the \acro{PDF} +% metadata. Because \pkgname{hyperref} always specifies the +% \pdfterm{Keywords} and \pdfterm{Producer} fields, even when they're +% empty, \pkgname{hyperxmp} has to follow suit and define +% \xmpprop{pdf:Keywords} and \xmpprop{pdf:Producer} in the \acro{XMP} +% packet. +% \changes{v2.4}{2013/12/21}{Made +% \texttt{\string\string\string\hyxmp@pdf@schema} +% \protect\emph{always} include the Adobe \protect\acro{PDF} schema, +% even when empty. Florian Breitwieser noted that this is necessary +% for \protect\acro{PDF/A}-1b compliance} +% \begin{macrocode} + \hyxmp@add@simple@var{pdf:Keywords}{@pdfkeywords}% + \hyxmp@add@simple@var{pdf:Producer}{@pdfproducer}% + \@ifundefined{pdfvariable}{% + \@ifundefined{pdfminorversion}{% +% \end{macrocode} +% Case 1: Neither |\pdfvariable| nor |\pdfminorversion| is defined +% (\XeLaTeX\ and regular \LaTeX). +% \begin{macrocode} + }{% +% \end{macrocode} +% Case 2: |\pdfminorversion| is defined (\pdfLaTeX\ and pre-0.85 \LuaLaTeX). +% \begin{macrocode} + \hyxmp@add@simple{pdf:PDFVersion}{1.\the\pdfminorversion}% + }% + }{% +% \end{macrocode} +% Case 3: |\pdfvariable| is defined (\LuaLaTeX~0.85+). +% \begin{macrocode} + \hyxmp@add@simple{pdf:PDFVersion}{1.\the\pdfvariable minorversion}% + }% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@extra@indent} +% This macro is used internally to increase the amount of indentation +% when writing certain \acro{XML} data. It is normally defined as empty +% but can temporarily be redefined to a sequence of |\space| characters. +% \begin{macrocode} +\newcommand*{\hyxmp@extra@indent}{} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@add@simple} +% Given an \acro{XMP} tag~(|#1|) and a string~(|#2|), if the string is +% nonempty, add a begin tag, the string, and an end tag to the packet. +% The ``|simple|'' in the macro name indicates that the string is output +% without variations for different languages. +% \changes{v2.0}{2012/08/25}{Added this macro} +% \begin{macrocode} +\newcommand*{\hyxmp@add@simple}[2]{% + \@ifnotmtargexp{#2}{% + \hyxmp@xmlify{#2}% + \hyxmp@add@to@xml{% + \hyxmp@extra@indent______<#1>\hyxmp@xmlified^^J% + }% + }% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@add@simple@var} +% Given an \acro{XMP} tag~(|#1|) and a variable name~(|#2|), if the +% string is defined, add a begin tag, the string, and an end tag to the +% packet. The ``|simple|'' in the macro name indicates that the string +% is output without variations for different languages. +% |\hyxmp@add@simple@var| differs from |\hyxmp@add@simple| in that the +% former includes defined but empty values in the \acro{XMP} packet +% while the latter excludes both undefined and defined but empty values. +% \changes{v2.4}{2013/12/21}{Added this macro} +% \begin{macrocode} +\newcommand*{\hyxmp@add@simple@var}[2]{% + \expandafter\ifx\csname#2\endcsname\relax + \else + \hyxmp@xmlify{\csname#2\endcsname}% + \hyxmp@add@to@xml{% + \hyxmp@extra@indent______<#1>\hyxmp@xmlified^^J% + }% + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@add@simple@lang} +% Given an \acro{XMP} tag~(|#1|) and a string~(|#2|), if the string is +% nonempty, add a begin tag, the string, and an end tag to the packet. +% The ``|simple|'' in the macro name indicates that the string is output +% without variations for different languages. However, if the string +% begins with a language code in square brackets, specify that as the +% (sole) language for the tag. +% \changes{v4.0}{2019/03/12}{Added this macro} +% \begin{macrocode} +\newcommand*{\hyxmp@add@simple@lang}[2]{% + \@ifnotmtarg{#2}{% + \hyxmp@xmlify{#2}% + \expandafter\hyxmp@add@simple@lang@i\hyxmp@xmlified\relax{#1}% + }% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@add@simple@lang@i} +% This is a helper macro for |\hyxmp@add@simple@lang|. It takes an +% optional language code (in brackets), text up to |\relax|, and a tag, +% and typesets the text within the \acro{XML} tag. +% \begin{macrocode} +\newcommand*{\hyxmp@add@simple@lang@i}{% + \@ifnextchar[\hyxmp@add@simple@lang@ii{\hyxmp@add@simple@lang@ii[]}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@add@simple@lang@ii} +% This is another helper macro for |\hyxmp@add@simple@lang|. It takes +% an mandatory language code (in brackets; can be empty), text up to +% |\relax|, and a tag, and typesets the text within the \acro{XML} tag. +% \begin{macrocode} +\def\hyxmp@add@simple@lang@ii[#1]#2\relax#3{% + \@ifnotmtarg{#2}{% + \hyxmp@xmlify{#2}% + \@ifmtarg{#1}{% + \hyxmp@add@to@xml{% +______<#3>\hyxmp@xmlified^^J% + }% + }{% + \hyxmp@add@to@xml{% +______<#3 xml:lang="#1">\hyxmp@xmlified^^J% + }% + }% + }% +} +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{The Dublin Core schema} +% \label{sec:dublin-core} +% +% \begin{macro}{\hyxmp@rdf@dc} +% Given an optional |\if|\meta{something} statement (|#1|), a Dublin Core +% property~(|#2|) and a macro containing some |\pdfstringdef|-defined +% text~(|#3|), append the appropriate block of \acro{XML} to the |\hyxmp@xml| +% macro. +% \changes{v1.4}{2011/06/12}{Included metadata in the \texttt{x-default} +% language regardless of the specified metadata language} +% \changes{v3.3}{2017/07/22}{Bug fix: Output the metadata language as +% correct \noexpand\acro{XML} even when \noexpand\pkgname{hyperref} is +% loaded with the \noexpand\optname{unicode} option} +% \begin{macrocode} +\newcommand*{\hyxmp@rdf@dc}[3][\iffalse]{% +% \end{macrocode} +% Set |\@tempswatrue| only if the given text is nonempty or the provided +% conditional evaluates to \textsc{true}. +% \begin{macrocode} + \@ifmtargexp{#3}{\@tempswafalse}{\@tempswatrue}% + #1 + \@tempswatrue + \fi +% \end{macrocode} +% Append the corresponding \acro{XML} only if |\@tempswatrue|. +% \begin{macrocode} + \if@tempswa + \hyxmp@xmlify{#3}% +% \end{macrocode} +% \begin{macro}{\hyxmp@value} +% Store the \acro{XML}-ified version of |#3| in |\hyxmp@value| so we can +% reuse |\hyxmp@xmlifiied| if necessary. +% \begin{macrocode} + \let\hyxmp@value=\hyxmp@xmlified + \hyxmp@add@to@xml{% +______^^J% +________^^J% + }% + \ifx\@pdfmetalang\hyxmp@x@default + \else + \hyxmp@xmlify{\@pdfmetalang}% + \hyxmp@add@to@xml{% +__________\hyxmp@value^^J% + }% + \fi + \hyxmp@add@to@xml{% +__________\hyxmp@value^^J% + }% +% \end{macrocode} +% Include variants of the text expressed in other languages, as specified by +% the author using |\XMPLangAlt| (Section~\ref{sec:lang-alt}). +% \begin{macrocode} + \def\do##1##2{ + \hyxmp@xmlify{##2}% + \hyxmp@add@to@xml{% +__________\hyxmp@xmlified^^J% + }% + }% + \csname hyxmp@alt@#2\endcsname +% \end{macrocode} +% Complete this \acro{XMP} element. +% \begin{macrocode} + \hyxmp@add@to@xml{% +________^^J% +______^^J% + }% + \fi +}% +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@list@to@xml} +% Given an optional |\if|\meta{something} statement (|#1|), a Dublin Core +% property~(|#2|), an \acro{RDF} array~(|#3|), and a macro containing a +% comma-separated list~(|#4|), append the appropriate block of \acro{XML} to +% the |\hyxmp@xml| macro. +% \changes{v2.0}{2012/08/02}{Modified by Heiko Oberdiek to use the new +% \term{Unicode}-processing macros} +% \begin{macrocode} +\newcommand*{\hyxmp@list@to@xml}[4][\iffalse]{% +% \end{macrocode} +% Set |\@tempswatrue| only if the given list is nonempty or the provided +% conditional evaluates to \textsc{true}. +% \begin{macrocode} + \@ifmtargexp{#4}{\@tempswafalse}{\@tempswatrue}% + #1 + \@tempswatrue + \fi +% \end{macrocode} +% Append the corresponding \acro{XML} only if |\@tempswatrue|. +% \begin{macrocode} + \if@tempswa + \hyxmp@add@to@xml{% +______^^J% +________^^J% + }% + \bgroup +% \end{macrocode} +% \begin{macro}{\@elt} +% Re-encode the text from \term{Unicode} if necessary. Then redefine +% |\@elt| to \acro{XML}-ify each element of the list and append it to +% |\hyxmp@xmlified|. +% \begin{macrocode} + \hyxmp@xmlify{#4}% + \hyxmp@commas@to@list\hyxmp@list{\hyxmp@xmlified}% + \def\@elt##1{% + \hyxmp@add@to@xml{% +__________##1^^J% + }% + }% + \hyxmp@list + \egroup + \hyxmp@add@to@xml{% +________^^J% +______^^J% + }% + \fi +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@singleton@dc} +% Given an optional list type (|Seq| or |Bag|), a Dublin Core property, +% and a string, append a block of \acro{XML} representing a one-element +% list consisting of the given string. +% \changes{v4.1}{2019/04/05}{Added this macro} +% \begin{macrocode} +\newcommand{\hyxmp@singleton@dc}[3][Bag]{% + \@ifnotmtarg{#3}{% + \hyxmp@xmlify{#3}% + \hyxmp@add@to@xml{% +______^^J% +________^^J% +__________\hyxmp@xmlified^^J% +________^^J% +______^^J% + }% + } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@dc@schema} +% Add properties defined by the Dublin Core schema to the |\hyxmp@xml| +% macro. Specifically, we add entries for the \xmpprop{dc:title} +% property if the author specified a \optname{pdftitle}, the +% \xmpprop{dc:description} property if the author specified a +% \optname{pdfsubject}, the \xmpprop{dc:rights} property if the author +% specified a \optname{pdfcopyright}, the \xmpprop{dc:creator} property +% if the author specified a \optname{pdfauthor}, the +% \xmpprop{dc:subject} property if the author specified +% \optname{pdfkeywords}, and the \xmpprop{dc:language} property if the +% author specified \optname{pdflang}. We also specify the +% \xmpprop{dc:date} property using the date the document was run through +% \LaTeX\ and the \xmpprop{dc:source} property using the base name of +% the source file with |.tex| appended. +% \changes{v2.0}{2012/08/26}{Added support for \xmpprop{dc:language} +% and \xmpprop{dc:source}} +% \changes{v2.4}{2013/12/21}{Made \xmpprop{dc:language} a \xmpterm{Bag} +% instead of an individual item so as to conform to the latest +% \acro{XMP} specifications, a detail identified by Florian Breitwieser} +% \begin{macrocode} +\newcommand*{\hyxmp@dc@schema}{% + \hyxmp@add@simple{dc:format}{application/pdf}% + \hyxmp@rdf@dc[\ifHy@pdfa]{title}{\@pdftitle}% + \hyxmp@rdf@dc[\ifHy@pdfa]{description}{\@pdfsubject}% + \hyxmp@rdf@dc{rights}{\@pdfcopyright}% + \hyxmp@singleton@dc{publisher}{\@pdfpublisher}% + \hyxmp@singleton@dc[Seq]{date}{\hyxmp@today}% + \hyxmp@singleton@dc{language}{\@pdflang}% + \hyxmp@singleton@dc{type}{\@pdftype}% + \hyxmp@list@to@xml[\ifHy@pdfa]{creator}{Seq}{\hyxmp@pdfauthor}% + \hyxmp@list@to@xml{subject}{Bag}{\hyxmp@pdfkeywords}% + \ifx\@pdfsource\@empty + \else + \hyxmp@add@simple{dc:source}{\@pdfsource}% + \fi +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{The XMP Rights Management schema} +% \label{sec:xmp-rights} +% +% \begin{macro}{\hyxmp@xmpRights@schema} +% Add properties defined by the \acro{XMP} Rights Management schema to the +% |\hyxmp@xml| macro. Currently, these are only the +% \xmpprop{xmpRights:Marked} property and the +% \xmpprop{xmpRights:WebStatement} property. If the author specified a +% copyright statement we mark the document as copyrighted. If the +% author specified a license statement we include the \acro{URL} in the +% metadata. +% \changes{v1.4}{2011/05/29}{Renamed the \texttt{xapRights} namespace +% prefix to \texttt{xmpRights}} +% \changes{v2.0}{2012/08/25}{Modified to include +% \xmpprop{xmpRights:Marked} only when \optname{pdfcopyright} is +% specified and \xmpprop{xmpRights:WebStatement} only when +% \optname{pdflicenseurl} is specified} +% \begin{macrocode} +\newcommand*{\hyxmp@xmpRights@schema}{% +% \end{macrocode} +% \begin{macro}{\hyxmp@legal} +% Set |\hyxmp@rights| to |YES| if either \optname{pdfcopyright} or +% \optname{pdflicenseurl} was specified. +% \begin{macrocode} + \let\hyxmp@rights=\@empty + \ifx\@pdflicenseurl\@empty + \else + \def\hyxmp@rights{YES}% + \fi + \ifx\@pdfcopyright\@empty + \else + \def\hyxmp@rights{YES}% + \fi +% \end{macrocode} +% Include the license-statement \acro{URL} and/or the copyright +% indication. The copyright statement itself is included by +% |\hyxmp@dc@schema| in Section~\ref{sec:dublin-core}. +% \begin{macrocode} + \ifx\hyxmp@rights\@empty + \else + \ifx\@pdfcopyright\@empty + \else + \hyxmp@add@simple{xmpRights:Marked}{True}% + \fi + \hyxmp@add@simple{xmpRights:WebStatement}{\@pdflicenseurl}% + \fi +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% +% \subsubsection{The XMP Media Management schema} +% \label{sec:xmp-media} +% +% \begin{macro}{\hyxmp@mm@schema} +% Add properties defined by the \acro{XMP} Media Management schema to +% the |\hyxmp@xml| macro. According to the \acro{XMP} specification, +% the \xmpprop{xmpMM:DocumentID} property is supposed to uniquely +% identify a document, and the \xmpprop{xmpMM:InstanceID} property is +% supposed to change with each save operation~\cite{Adobe2012:XMP}. As +% seen in Section~\ref{sec:uuid-gen}, we do what we can to honor this +% intention from within a \tex-based workflow. We additionally support +% the \xmpprop{xmpMM:VersionID} property, whose value is supplied by +% the author using \optname{pdfversionid}. +% \changes{v1.4}{2011/05/29}{Renamed the \texttt{xapMM} namespace +% prefix to \texttt{xmpMM}} +% \changes{v3.5}{2018/11/27}{Generate \protect\cs{hyxmp@DocumentID} and +% \protect\cs{hyxmp@InstanceID} only if the document does not already +% define these using the \protect\optname{pdfdocumentid} and +% \protect\optname{pdfinstanceid} options} +% \changes{v4.0}{2019/03/09}{Include \protect\xmpprop{xmpMM:VersionID} in +% the \protect\acro{XMP} packet} +% \begin{macrocode} +\gdef\hyxmp@mm@schema{% + \@ifmtargexp{\hyxmp@DocumentID}{\hyxmp@def@DocumentID}{}% + \@ifmtargexp{\hyxmp@InstanceID}{\hyxmp@def@InstanceID}{}% + \hyxmp@add@simple{xmpMM:DocumentID}{\hyxmp@DocumentID}% + \hyxmp@add@simple{xmpMM:InstanceID}{\hyxmp@InstanceID}% + \hyxmp@add@simple{xmpMM:VersionID}{\@pdfversionid}% +} +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{The XMP Basic schema} +% \label{sec:xmp-basic} +% +% \begin{macro}{\hyxmp@define@createdate} +% \begin{macro}{\hyxmp@createdate} +% Define |\hyxmp@createdate| as the document's creation date but in +% \acro{XMP} date format, not \acro{PDF} date format. We use +% |\hyxmp@createdate| for the \xmpprop{xmp:CreateDate}, +% \xmpprop{xmp:ModifyDate}, and \xmpprop{xmp:MetadataDate} fields. +% \begin{macrocode} +\newcommand*{\hyxmp@define@createdate}{% + \@ifundefined{pdffeedback}{% + \@ifundefined{pdfcreationdate}{% +% \end{macrocode} +% Case 1: Neither |\pdffeedback| nor |\pdfcreationdate| is defined +% (\XeLaTeX\ and regular \LaTeX). +% \begin{macrocode} + \hyxmp@today@define\hyxmp@createdate + }{% +% \end{macrocode} +% Case 2: |\pdfcreationdate| is defined (\pdfLaTeX\ and pre-0.85 \LuaLaTeX). +% \begin{macrocode} + \edef\hyxmp@createdate{\expandafter\hyxmp@pdf@to@xmp@date\pdfcreationdate}% + }% + }{% +% \end{macrocode} +% Case 3: |\pdffeedback| is defined (\LuaLaTeX~0.85+). +% \begin{macrocode} + \edef\hyxmp@createdate{\expandafter\hyxmp@pdf@to@xmp@date\pdffeedback creationdate}% + }% +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@xmp@basic@schema} +% Add properties defined by the \acro{XMP} Basic schema to the +% |\hyxmp@xml| macro. These include a bunch of dates (all set to the +% same value) and the base \acro{URL} for the document if specified with +% \optname{baseurl}. +% \changes{v2.0}{2012/08/26}{Added this macro} +% \changes{v3.0}{2016/07/04}{Made the \protect\acro{XMP} +% \protect\xmpprop{xmp:CreateDate}, \protect\xmpprop{xmp:ModifyDate}, +% and \protect\xmpprop{xmp:MetadataDate} match the \protect\acro{PDF} +% \protect\pdfterm{CreationDate}} +% \changes{v3.2}{2017/01/22}{Honor \string\pkgname{hyperref}'s +% \string\optname{pdfcreationdate} and \string\optname{pdfmoddate} +% options plus a new \string\optname{pdfmetadate} option. Leonid +% Sinev requested this additional control and helped test the resulting +% \protect\pkgname{hyperxmp} code} +% \begin{macrocode} +\newcommand*{\hyxmp@xmp@basic@schema}{% + \hyxmp@define@createdate +% \end{macrocode} +% For the document's creation date, use the user-specified +% |\@pdfcreationdate| if defined and non-empty. Otherwise use our +% fabricated |\hyxmp@createdate|. +% \begin{macrocode} + \@ifundefined{@pdfcreationdate}{% + \hyxmp@add@simple{xmp:CreateDate}{\hyxmp@createdate}% + }{% + \ifx\@pdfcreationdate\@empty + \hyxmp@add@simple{xmp:CreateDate}{\hyxmp@createdate}% + \else + \hyxmp@add@simple{xmp:CreateDate}{% + \expandafter\hyxmp@as@xmp@date\expandafter{\@pdfcreationdate}}% + \fi + }% +% \end{macrocode} +% For the document's modification date, use the user-specified +% |\@pdfmoddate| if defined and non-empty. Otherwise use our +% fabricated |\hyxmp@createdate|. +% \begin{macrocode} + \@ifundefined{@pdfmoddate}{% + \hyxmp@add@simple{xmp:ModifyDate}{\hyxmp@createdate}% + }{% + \ifx\@pdfmoddate\@empty + \hyxmp@add@simple{xmp:ModifyDate}{\hyxmp@createdate}% + \else + \hyxmp@add@simple{xmp:ModifyDate}{% + \expandafter\hyxmp@as@xmp@date\expandafter{\@pdfmoddate}}% + \fi + }% +% \end{macrocode} +% For the document's metadata date, use the user-specified +% |\@pdfmetadatetime| if defined and non-empty. Otherwise use our +% fabricated |\hyxmp@createdate|. +% \begin{macrocode} + \ifx\@pdfmetadatetime\@empty + \hyxmp@add@simple{xmp:MetadataDate}{\hyxmp@createdate}% + \else + \hyxmp@add@simple{xmp:MetadataDate}{\@pdfmetadatetime}% + \fi +% \end{macrocode} +% Define the creation tool and the base \acro{URL}. +% \begin{macrocode} + \hyxmp@add@simple{xmp:CreatorTool}{\@pdfcreator}% + \hyxmp@add@simple{xmp:BaseURL}{\@baseurl}% +} +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{The Photoshop schema} +% \label{sec:photoshop} +% +% \begin{macro}{\hyxmp@photoshop@schema} +% \changes{v2.0}{2012/08/26}{Simplified using +% \texttt{\string\string\string\hyxmp@add@simple}} +% \begin{macro}{\hyxmp@photoshop@data} +% Add properties defined by the Photoshop schema to the |\hyxmp@xml| +% macro. We currently support only the +% \xmpprop{photoshop:AuthorsPosition} and +% \xmpprop{photoshop:CaptionWriter} properties. +% \begin{macrocode} +\gdef\hyxmp@photoshop@schema{% + \edef\hyxmp@photoshop@data{\@pdfauthortitle\@pdfcaptionwriter}% + \hyxmp@add@simple{photoshop:AuthorsPosition}{\@pdfauthortitle}% + \hyxmp@add@simple{photoshop:CaptionWriter}{\@pdfcaptionwriter}% +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% +% \subsubsection{The PDF/A Identification schema} +% \label{sec:pdfa-id} +% +% \begin{macro}{\hyxmp@pdfa@id@schema} +% Add properties defined by the \acro{PDF/A} Identification +% schema~\cite{PDFA2008:xmp-props} to the |\hyxmp@xml| macro. These +% properties identify a document as conforming to a particular +% \acro{PDF/A} standard. We default to \acro{PDF/A}-1b if any \acro{PDF/A} +% compliance is detected but let the author override the ``1'' with +% \optname{pdfapart} and the ``B'' with \optname{pdfaconformance}. +% \changes{v2.4}{2013/12/21}{Added this macro} +% \changes{v2.9}{2016/04/12}{Let the author specify the \protect\acro{PDF/A} +% part and conformance IDs, as requested by Leonid Sinev} +% \begin{macrocode} +\newcommand*{\hyxmp@pdfa@id@schema}{% + \ifHy@pdfa + \hyxmp@add@simple{pdfaid:part}{\@pdfapart}% + \hyxmp@add@simple{pdfaid:conformance}{\@pdfaconformance}% + \fi +} +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{The IPTC Photo Metadata schema} +% \label{sec:photo-meta} +% +% \begin{macro}{\xmplinesep} +% Lines in multiline fields are separated by |\xmplinesep| in the +% generated \acro{XML}. This defaults to an \acro{LF} (|^^J|) character +% but written as an \acro{XML} character entity for consistency across +% operating systems. +% \changes{v2.2}{2012/12/07}{Added this macro} +% \begin{macrocode} +\begingroup + \catcode`\&=12 + \catcode`\#=12 + \gdef\xmplinesep{ } +\endgroup +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@list@to@lines} +% Given a property~(|#1|) and a macro containing a comma-separated +% list~(|#2|), replace commas with |\xmplinesep|. Do nothing it the +% list is empty. +% \changes{v2.2}{2012/12/07}{Added this macro} +% \begin{macrocode} +\newcommand*{\hyxmp@list@to@lines}[2]{% + \@ifnotmtargexp{#2}{% + \bgroup + \hyxmp@add@to@xml{% + \hyxmp@extra@indent______<#1>% + }% +% \end{macrocode} +% \begin{macro}{\@elt@first} +% The first element of the list is output as is. +% \begin{macrocode} + \def\@elt@first##1{% + \hyxmp@add@to@xml{##1}% + \let\@elt=\@elt@rest + }% +% \end{macrocode} +% \begin{macro}{\@elt@rest} +% The remaining elements of the list are output with a preceding line +% separator (|\xmplinesep|). +% \begin{macrocode} + \def\@elt@rest##1{% + \hyxmp@add@to@xml{\xmplinesep##1}% + }% +% \end{macrocode} +% \begin{macro}{\@elt} +% Re-encode the text from \term{Unicode} if necessary. Then redefine +% |\@elt| to insert a line separator between terms. +% \begin{macrocode} + \let\@elt=\@elt@first + \hyxmp@xmlify{#2}% + \hyxmp@commas@to@list\hyxmp@list{\hyxmp@xmlified}% + \hyxmp@list + \hyxmp@add@to@xml{^^J}% + \egroup + }% +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@iptc@schema} +% Add properties defined by the \acro{IPTC} Photo Metadata +% schema~\cite{IPTC2010:photo-meta} to the |\hyxmp@xml| macro. We +% currently support only the \xmpprop{Iptc4xmpCore:CreatorContactInfo} +% property, although this is a structure containing multiple fields. +% \changes{v2.2}{2012/12/07}{Added this macro} +% \changes{v2.9}{2016/04/23}{Use \textsf{Iptc4xmpCore} instead of +% \textsf{Iptc4ContInfo} as the contact-information metadata prefix. +% Leonid Sinev reports that Acrobat's \acro{PDF/A} validator seems to +% prefer \textsf{Iptc4xmpCore}} +% \changes{v4.0}{2019/03/09}{Moved the definition of +% \protect\cs{hyxmp@iptc@data} from here into +% \protect\cs{hyxmp@check@iptc@data}} +% \changes{v4.0}{2019/03/16}{Renamed this macro to +% \protect\cs{hyxmp@iptc@schema} from +% \protect\cs{hyxmp@photometa@schema}} +% \changes{v4.0}{2019/03/16}{Rewrote this macro entirely to correct +% the use of fields within a structure} +% \begin{macrocode} +\gdef\hyxmp@iptc@schema{% +% \end{macrocode} +% Because we currently support only +% \xmpprop{Iptc4xmpCore:CreatorContactInfo} it suffices to check if we +% have any relevant data. If so, we instantiate a +% \xmpprop{Iptc4xmpCore:ContactInfo} structure with all available +% fields. +% \begin{macrocode} + \ifx\hyxmp@iptc@data\@empty + \else + \hyxmp@add@to@xml{% +______^^J% + }% +% \end{macrocode} +% We locally redefine |\hyxmp@extra@indent| to increase the indentation +% of the assignments to \xmpprop{Iptc4xmpCore:CreatorContactInfo}'s +% fields. +% \begin{macrocode} + \bgroup + \edef\hyxmp@extra@indent{\hyxmp@extra@indent\space\space}% + \hyxmp@list@to@lines{Iptc4xmpCore:CiAdrExtadr}{\@pdfcontactaddress}% + \hyxmp@add@simple{Iptc4xmpCore:CiAdrCity}{\@pdfcontactcity}% + \hyxmp@add@simple{Iptc4xmpCore:CiAdrRegion}{\@pdfcontactregion}% + \hyxmp@add@simple{Iptc4xmpCore:CiAdrPcode}{\@pdfcontactpostcode}% + \hyxmp@add@simple{Iptc4xmpCore:CiAdrCtry}{\@pdfcontactcountry}% +% \end{macrocode} +% \begin{macro}{\xmplinesep} +% The \acro{IPTC} standard states that sets of telephone numbers, email +% addresses, and \acro{URL}s for the contact person or institution, +% ``[m]ay have to be separated by a comma in the user +% interface''~\cite{IPTC2010:photo-meta}. This is rather ambiguous: +% Does the comma appear \emph{only} in the user interface or also in the +% generated \acro{XML}\@? Here we assume the latter interpretation and +% temporarily redefine |\xmplinesep| as a comma and use +% |\hyxmp@list@to@lines| to insert the data. Unlike +% |\hyxmp@add@simple|, this approach trims all spaces surrounding commas. +% \begin{macrocode} + \def\xmplinesep{,}% + \hyxmp@list@to@lines{Iptc4xmpCore:CiTelWork}{\@pdfcontactphone}% + \hyxmp@list@to@lines{Iptc4xmpCore:CiEmailWork}{\@pdfcontactemail}% + \hyxmp@list@to@lines{Iptc4xmpCore:CiUrlWork}{\@pdfcontacturl}% + \egroup + \hyxmp@add@to@xml{% +______^^J% + }% + \fi +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsubsection{The PRISM Basic Metadata schema} +% \label{sec:prism-meta} +% +% \begin{macro}{\hyxmp@prism@schema} +% \changes{v4.0}{2019/03/10}{Added this macro} +% Add properties defined by the \acro{PRISM} Basic Metadata +% schema~\cite{PRISM2012:basic-meta}. +% \begin{macrocode} +\newcommand*{\hyxmp@prism@schema}{% + \ifx\hyxmp@prism@data\@empty + \else + \hyxmp@add@simple{prism:complianceProfile}{three}% + \fi + \hyxmp@add@simple@lang{prism:subtitle}{\@pdfsubtitle}% + \hyxmp@add@simple@lang{prism:publicationName}{\@pdfpublication}% + \hyxmp@add@simple{prism:aggregationType}{\@pdfpubtype}% + \hyxmp@add@simple@lang{prism:bookEdition}{\@pdfbookedition}% + \hyxmp@add@simple{prism:volume}{\@pdfvolumenum}% + \hyxmp@add@simple{prism:number}{\@pdfissuenum}% + \hyxmp@add@simple{prism:pageRange}{\@pdfpagerange}% + \hyxmp@add@simple{prism:isbn}{\@pdfisbn}% + \hyxmp@add@simple{prism:issn}{\@pdfissn}% + \hyxmp@add@simple{prism:eIssn}{\@pdfeissn}% + \hyxmp@add@simple{prism:doi}{\@pdfdoi}% + \hyxmp@add@simple{prism:url}{\@pdfurl}% + \hyxmp@add@simple{prism:byteCount}{\@pdfbytes}% + \hyxmp@add@simple{prism:pageCount}{\@pdfnumpages}% +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{XMP extension schemata} +% \label{sec:extension-schemata} +% +% Not all of the schemata supported by \pkgname{hyperxmp} are predefined +% by \acro{XMP}. \acro{PDF/A} conversion would normally fail for +% documents that employ ``custom'' schemata. However, this problem can +% be circumvented by declaring non-standard schemata in the \acro{XMP} +% packet itself, following a technique described in a PDF Association +% technical note~\cite{PDFA2008:ext-schemas}. In this section, we +% declare only those schemata we actually use. +% +% \begin{macro}{\hyxmp@check@iptc@data} +% Define |\hyxmp@iptc@data| as the concatenation of all \acro{IPTC} +% photo metadata supplied by the document. +% \begin{macrocode} +\newcommand*{\hyxmp@check@iptc@data}{% +% \end{macrocode} +% \begin{macro}{\hyxmp@iptc@data} +% \begin{macrocode} + \edef\hyxmp@iptc@data{% + \@pdfcontactaddress + \@pdfcontactcity + \@pdfcontactregion + \@pdfcontactpostcode + \@pdfcontactcountry + \@pdfcontactphone + \@pdfcontactemail + \@pdfcontacturl + }% +}% +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@check@prism@data} +% Define |\hyxmp@prism@data| as the concatenation of all \acro{PRISM} +% metadata supplied by the document. +% \begin{macrocode} +\newcommand*{\hyxmp@check@prism@data}{% +% \end{macrocode} +% \begin{macro}{\hyxmp@prism@data} +% \begin{macrocode} + \edef\hyxmp@prism@data{% + \@pdfbookedition + \@pdfbytes + \@pdfdoi + \@pdfeissn + \@pdfisbn + \@pdfissn + \@pdfissuenum + \@pdfnumpages + \@pdfpagerange + \@pdfpublication + \@pdfpubtype + \@pdfsubtitle + \@pdfurl + \@pdfvolumenum + }% +}% +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hyxmp@begin@extension@decls} +% Begin a block of \acro{XML} tags that indicates we're declaring one or +% more extension schemata. +% \begin{macrocode} +\newcommand*{\hyxmp@begin@extension@decls}{% + \hyxmp@add@to@xml{% +______^^J% +________^^J% + }% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@end@extension@decls} +% End the block of \acro{XML} tags begun by |\hyxmp@begin@extension@decls|. +% \begin{macrocode} +\newcommand*{\hyxmp@end@extension@decls}{% + \hyxmp@add@to@xml{% +________^^J% +______^^J% + }% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@begin@ext@decl} +% Begin the declaration of a single extension schema. +% |\hyxmp@begin@ext@decl| accepts the schema's name, prefix, and +% namespace URI. +% \changes{v4.0}{2019/03/15}{Added this macro} +% \begin{macrocode} +\newcommand*{\hyxmp@begin@ext@decl}[3]{% + \hyxmp@add@to@xml{% +__________^^J% +____________#1^^J% +____________#2^^J% +____________#3^^J% +____________^^J% +______________^^J% + }% +}% +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@end@ext@decl} +% End the declaration of a single extension schema. +% \changes{v4.0}{2019/03/15}{Added this macro} +% \begin{macrocode} +\newcommand*{\hyxmp@end@ext@decl}{% + \hyxmp@add@to@xml{% +______________^^J% +____________^^J% +__________^^J% + }% +}% +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@declare@property} +% Declare a single extension-schema property. |\hyxmp@declare@property| +% takes as input an optional type (defaults to \xmpterm{Text}) and a +% mandatory name, category, and description. +% \changes{v4.0}{2019/03/15}{Added this macro} +% \begin{macrocode} +\newcommand{\hyxmp@declare@property}[4][Text]{% + \hyxmp@add@to@xml{% +________________^^J% +__________________#2^^J% +__________________#1^^J% +__________________#3^^J% +__________________#4^^J% +________________^^J% + }% +}% +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@declare@field} +% Declare a single field in a custom datatype required by an extension +% schema. |\hyxmp@declare@field| takes as input an optional type +% (defaults to \xmpterm{Text}) and a mandatory name and description. +% \changes{v4.0}{2019/03/16}{Replaced +% \protect\cs{hyxmp@declare@resource} with this macro} +% \begin{macrocode} +\newcommand{\hyxmp@declare@field}[3][Text]{% + \hyxmp@add@to@xml{% +______________________^^J% +________________________#2^^J% +________________________#1^^J% +________________________#3^^J% +______________________^^J% + }% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@mm@extensions} +% Declare the \acro{XMP} Media Management schema. +% \changes{v4.0}{2019/03/15}{Added this macro} +% \begin{macrocode} +\newcommand*{\hyxmp@mm@extensions}{% + \hyxmp@begin@ext@decl + {XMP Media Management Schema}% + {xmpMM}% + {http://ns.adobe.com/xap/1.0/mm/}% + \hyxmp@declare@property + [URI] + {DocumentID}% + {internal}% + {UUID based identifier for all versions and renditions of a document}% + \hyxmp@declare@property + [URI] + {InstanceID}% + {internal}% + {UUID based identifier for specific incarnation of a document}% + \hyxmp@declare@property + {VersionID}% + {internal}% + {Document version identifier}% + \hyxmp@end@ext@decl +}% +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@pdfa@id@extensions} +% Declare the \acro{PDF/A} Identification schema~\cite{PDFA2008:xmp-props}. +% \changes{v4.0}{2019/03/15}{Added this macro} +% \begin{macrocode} +\newcommand*{\hyxmp@pdfa@id@extensions}{% + \hyxmp@begin@ext@decl + {PDF/A Identification Schema}% + {pdfaid}% + {http://www.aiim.org/pdfa/ns/id/}% + \hyxmp@declare@property + [Integer]% + {part}% + {internal}% + {Part of PDF/A standard}% + \hyxmp@declare@property + {conformance}% + {internal}% + {Conformance level of PDF/A standard}% + \hyxmp@end@ext@decl +}% +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@iptc@extensions} +% \changes{v2.2}{2012/12/13}{Added this macro to support +% \acro{PDF/A} generation} +% \changes{v2.3}{2013/01/08}{Gave the +% \xmpprop{Iptc4xmpCore:CreatorContactInfo} fields a unique +% \xmpprop{pdfaType:prefix} to better support conversion of the +% document to \acro{PDF/A}} +% \changes{v4.0}{2019/03/09}{Moved the header code from here into +% \protect\cs{hyxmp@begin@extension@decls} and the trailer code +% from here into \protect\cs{hyxmp@end@extension@decls}} +% \changes{v4.0}{2019/03/16}{Rewrote to more closely honor the +% \acro{XMP} specification} +% Because \acro{IPTC} metadata are not recognized by the \acro{PDF/A} +% standard, \acro{PDF/A} conversion would normally fail for documents +% that utilize \acro{IPTC} metadata. Declaring the \acro{IPTC} +% metadata we support enables the document to be converted to +% \acro{PDF/A} format. +% \begin{macrocode} +\newcommand*{\hyxmp@iptc@extensions}{% + \hyxmp@begin@ext@decl + {IPTC Core Schema}% + {Iptc4xmpCore}% + {http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/}% + \hyxmp@declare@property + [ContactInfo] + {CreatorContactInfo} + {external} + {Document creator's contact information} +% \end{macrocode} +% We can't call |\hyxmp@end@ext@decl| because we need first need to +% define the \xmpprop{Iptc4xmpCore:ContactInfo} structure. +% \begin{macrocode} + \hyxmp@add@to@xml{% +______________^^J% +____________^^J% +____________^^J% +______________^^J% +________________^^J% +__________________ContactInfo^^J% +__________________http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/^^J% +__________________Iptc4xmpCore^^J% +__________________% + Basic set of information to get in contact with a person% + ^^J% +__________________^^J% +____________________^^J% + }% + \hyxmp@declare@field + {CiAdrCity}% + {Contact information city}% + \hyxmp@declare@field + {CiAdrCtry}% + {Contact information country}% + \hyxmp@declare@field + {CiAdrExtadr}% + {Contact information address}% + \hyxmp@declare@field + {CiAdrPcode}% + {Contact information local postal code}% + \hyxmp@declare@field + {CiAdrRegion}% + {Contact information regional information such as state or province}% + \hyxmp@declare@field + {CiEmailWork}% + {Contact information email address(es)}% + \hyxmp@declare@field + {CiTelWork}% + {Contact information telephone number(s)}% + \hyxmp@declare@field + {CiUrlWork}% + {Contact information Web URL(s)}% + \hyxmp@add@to@xml{% +____________________^^J% +__________________^^J% +________________^^J% +______________^^J% +____________^^J% +__________^^J% + }% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@prism@extensions} +% \changes{v4.0}{2019/03/15}{Added this macro} +% Because \acro{PRISM} metadata are not recognized by the \acro{PDF/A} +% standard, \acro{PDF/A} conversion would normally fail for documents +% that utilize \acro{PRISM} metadata. Declaring the \acro{PRISM} +% metadata we support enables the document to be converted to +% \acro{PDF/A} format. +% \begin{macrocode} +\newcommand*{\hyxmp@prism@extensions}{% + \hyxmp@begin@ext@decl + {PRISM Basic Metadata}% + {prism}% + {http://prismstandard.org/namespaces/basic/2.1/}% + \hyxmp@declare@property + {complianceProfile}% + {internal}% + {PRISM specification compliance profile to which this document adheres}% + \hyxmp@declare@property + {publicationName}% + {external}% + {Publication name}% + \hyxmp@declare@property + {aggregationType}% + {external}% + {Publication type}% + \hyxmp@declare@property + {bookEdition}% + {external}% + {Edition of the book in which the document was published}% + \hyxmp@declare@property + {volume}% + {external}% + {Publication volume number}% + \hyxmp@declare@property + {number}% + {external}% + {Publication issue number within a volume}% + \hyxmp@declare@property + {pageRange}% + {external}% + {Page range for the document within the print version of its publication}% + \hyxmp@declare@property + {issn}% + {external}% + {ISSN for the printed publication in which the document was published}% + \hyxmp@declare@property + {eIssn}% + {external}% + {ISSN for the electronic publication in which the document was published}% + \hyxmp@declare@property + {isbn}% + {external}% + {ISBN for the publication in which the document was published}% + \hyxmp@declare@property + {doi}% + {external}% + {Digital Object Identifier for the document}% + \hyxmp@declare@property + [URL] + {url}% + {external}% + {URL at which the document can be found}% + \hyxmp@declare@property + [Integer] + {byteCount}% + {internal}% + {Approximate file size in octets}% + \hyxmp@declare@property + [Integer] + {pageCount}% + {internal}% + {Number of pages in the print version of the document}% + \hyxmp@declare@property + {subtitle}% + {external}% + {Document's subtitle}% + \hyxmp@end@ext@decl +}% +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@declare@extensions} +% Declare all \acro{XMP} extension schemata. We'll always have at least +% one, the \acro{XMP} Media Management extensions, because we +% automatically generate \xmpprop{xmpMM:DocumentID} and +% \xmpprop{xmpMM:InstanceID} +% values. +% \begin{macrocode} +\newcommand*{\hyxmp@declare@extensions}{% + \hyxmp@begin@extension@decls +% \end{macrocode} +% Declare the \acro{XMP} Media Management extensions (always present). +% \begin{macrocode} + \hyxmp@mm@extensions +% \end{macrocode} +% Declare the \acro{PDF/A} Identification extensions, but only when +% generating a \acro{PDF/A} document. +% \begin{macrocode} + \ifHy@pdfa + \hyxmp@mm@extensions + \fi +% \end{macrocode} +% Conditionally declare \acro{IPTC} photo metadata extensions. +% \begin{macrocode} + \ifx\hyxmp@iptc@data\@empty + \else + \hyxmp@iptc@extensions + \fi +% \end{macrocode} +% Conditionally declare \acro{PRISM} basic metadata extensions. +% \begin{macrocode} + \ifx\hyxmp@prism@data\@empty + \else + \hyxmp@prism@extensions + \fi +% \end{macrocode} +% \begin{macrocode} + \hyxmp@end@extension@decls +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Combining schemata into an XMP packet} +% \label{sec:combining-schemata} +% +% \begin{macro}{\hyxmp@bom} +% Define a macro for the \term{Unicode} byte-order marker (\acro{BOM}). +% \changes{v2.0}{2012/08/02}{Added by Heiko Oberdiek} +% \begin{macrocode} +\begingroup + \ifhyxmp@unicodetex + \lccode`\!="FEFF % + \lowercase{% + \gdef\hyxmp@bom{!} + }% + \else + \catcode`\^^ef=12 + \catcode`\^^bb=12 + \catcode`\^^bf=12 + \gdef\hyxmp@bom{^^ef^^bb^^bf}% + \fi +\endgroup +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@construct@packet} +% \changes{v1.1}{2006/05/21}{Explicitly set the category codes of +% characters \string\meta{EF}, \string\meta{BB}, and +% \string\meta{BF} to ``letter''. Thanks to Daniel Sch\"omer for +% the bug report} +% \changes{v2.0}{2012/08/02}{Modified by Heiko Oberdiek to use an +% appropriate \acro{BOM} representation via +% \texttt{\string\string\string\hyxmp@bom}} +% \begin{macro}{\hyxmp@xml} +% Successively add \acro{XML} data to |\hyxmp@xml| until we have +% something we can insert into the document's \acro{PDF} catalog. +% \begin{macrocode} +\def\hyxmp@construct@packet{% + \gdef\hyxmp@xml{}% + \hyxmp@add@to@xml{^^J% +^^J% +__^^J% +____^^J% + }% +% \end{macrocode} +% Declare non-standard schemata. +% \begin{macrocode} + \hyxmp@check@iptc@data + \hyxmp@check@prism@data + \hyxmp@declare@extensions +% \end{macrocode} +% Insert all the metadata we know how to insert. +% \begin{macrocode} + \hyxmp@pdf@schema + \hyxmp@xmpRights@schema + \hyxmp@dc@schema + \hyxmp@photoshop@schema + \hyxmp@xmp@basic@schema + \hyxmp@pdfa@id@schema + \hyxmp@mm@schema + \hyxmp@iptc@schema + \hyxmp@prism@schema + \hyxmp@add@to@xml{% +____^^J% +__^^J% +^^J% +\hyxmp@padding +^^J% + }% +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% +% \subsection{Embedding the XMP packet} +% +% The \acro{PDF} specification says that ``a metadata stream may be +% attached to a document through the \pdfterm{Metadata} entry in the +% document catalogue''~\cite{Adobe2008:PDF} so that's what we do here. +% +% \begin{macro}{\hyxmp@embed@packet} +% \begin{macro}{\hyxmp@driver} +% Determine which \pkgname{hyperref} driver is in use and invoke the +% appropriate embedding function. +% \begin{macrocode} +\newcommand*{\hyxmp@embed@packet}{% + \hyxmp@construct@packet + \def\hyxmp@driver{hpdftex}% + \ifx\hyxmp@driver\Hy@driver + \hyxmp@embed@packet@pdftex + \else + \def\hyxmp@driver{hluatex}% + \ifx\hyxmp@driver\Hy@driver + \hyxmp@embed@packet@luatex + \else + \def\hyxmp@driver{hdvipdfm}% + \ifx\hyxmp@driver\Hy@driver + \hyxmp@embed@packet@dvipdfm + \else + \def\hyxmp@driver{hxetex}% + \ifx\hyxmp@driver\Hy@driver + \hyxmp@embed@packet@xetex + \else + \@ifundefined{pdfmark}{% + \PackageWarningNoLine{hyperxmp}{% + Unrecognized hyperref driver `\Hy@driver'.\MessageBreak + \jobname.tex's XMP metadata will *not* be\MessageBreak + embedded in the resulting file}% + }{% + \hyxmp@embed@packet@pdfmark + }% + \fi + \fi + \fi + \fi +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsubsection{Embedding using \pdfTeX} +% +% Up to version~0.85, \LuaTeX\ supported the \pdfTeX\ primitives, and +% \pkgname{hyperref} didn't distinguish the two backends. However, from +% \pkgname{hyperxmp}'s perspective there is one key difference: the +% effect of |\pdfcompresslevel| is local to a group in \pdfTeX\ but is +% global in \LuaTeX\@. +% +% The \acro{PDF} object representing the \acro{XMP} packet is supposed +% to include an uncompressed stream so it can be read by +% non-\acro{PDF}-aware tools. However, we don't want to unnecessarily +% uncompress \emph{every} \acro{PDF} stream. The solution, provided by +% Hans Hagen on the |luatex| mailing list (thread: +% \href{http://tug.org/pipermail/luatex/2016-July/006077.html}{``Leaving +% a single \acro{PDF} object uncompressed''}, 6\,\textsc{Jul}\,2016), +% is to provide the |uncompressed| flag to |\pdfobj|. Our definition of +% |\hyxmp@embed@packet@pdftex| uses the \pkgname{ifluatex} package to +% distinguish the \pdfTeX\ case from the pre-0.85 \LuaTeX\ case. +% +% \begin{macrocode} +\RequirePackage{ifluatex} +% \end{macrocode} +% +% \begin{macro}{\hyxmp@embed@packet@pdftex} +% Embed the \acro{XMP} packet using \pdfTeX\ primitives, which are +% supported by both \pdfTeX\ and pre-0.85 \LuaTeX. The only difference +% is that in the former case we locally specify |\pdfcompresslevel=0| to +% leave the \acro{PDF} object uncompressed while in the latter case we +% pass the |uncompressed| flag to |\pdfobj| to achieve the same effect. +% \changes{v3.1}{2016/07/06}{Leave the \protect\acro{XMP} packet---and +% only the \protect\acro{XMP} packet---uncompressed in both +% \protect\pdfTeX\ and pre-0.85 \protect\LuaTeX} +% \begin{macrocode} +\newcommand*{\hyxmp@embed@packet@pdftex}{% + \bgroup + \ifluatex + \else + \pdfcompresslevel=0 + \fi + \immediate\pdfobj \ifluatex uncompressed\fi stream attr {% + /Type /Metadata + /Subtype /XML + }{\hyxmp@xml}% + \pdfcatalog {/Metadata \the\pdflastobj\space 0 R}% + \egroup +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Embedding using \LuaTeX~0.85+} +% +% \begin{macro}{\hyxmp@embed@packet@luatex} +% Embed the \acro{XMP} packet using \LuaTeX~0.85+ primitives. +% \changes{v3.0}{2016/07/02}{Added this macro} +% \changes{v3.1}{2016/07/06}{Updated to use +% \protect\texttt{\string\string\string\pdfextension\ obj uncompressed} +% as suggested by Hans Hagen} +% \begin{macrocode} +\newcommand*{\hyxmp@embed@packet@luatex}{% + \immediate\pdfextension obj uncompressed stream attr {% + /Type /Metadata + /Subtype /XML + }{\hyxmp@xml}% + \pdfextension catalog {/Metadata \the\numexpr\pdffeedback lastobj\relax\space 0 R}% +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Embedding using any \texttt{pdfmark}-based backend} +% +% \begin{macro}{\hyxmp@embed@packet@pdfmark} +% Embed the \acro{XMP} packet using \pkgname{hyperref}'s |\pdfmark| +% command. I believe |\pdfmark| is used by the \optname{dvipdf}, +% \optname{dvipsone}, \optname{dvips}, \optname{dviwindo}, +% \optname{nativepdf}, \optname{pdfmark}, \optname{ps2pdf}, +% \optname{textures}, and \optname{vtexpdfmark} options to +% \pkgname{hyperref}, but I've tested only a few of those. +% \begin{macrocode} +\newcommand*{\hyxmp@embed@packet@pdfmark}{% + \pdfmark{% + pdfmark=/NamespacePush + }% + \pdfmark{% + pdfmark=/OBJ, + Raw={/_objdef \string{hyxmp@Metadata\string} /type /stream}% + }% + \pdfmark{% + pdfmark=/PUT, + Raw={\string{hyxmp@Metadata\string} + 2 dict begin + /Type /Metadata def + /Subtype /XML def + currentdict + end + }% + }% + \pdfmark{% + pdfmark=/PUT, + Raw={\string{hyxmp@Metadata\string} (\hyxmp@xml)}% + }% + \pdfmark{% + pdfmark=/Metadata, + Raw={\string{Catalog\string} \string{hyxmp@Metadata\string}}% + }% + \pdfmark{% + pdfmark=/NamespacePop + }% +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Embedding using \texttt{dvipdfm}} +% +% \begin{macro}{\hyxmp@embed@packet@dvipdfm} +% Embed the \acro{XMP} packet using \cmdname{dvipdfm}-specific +% |\special| commands. Note that \cmdname{dvipdfm} rather irritatingly +% requires us to count the number of characters in the |\hyxmp@xml| +% stream ourselves. +% \begin{macrocode} +\newcommand*{\hyxmp@embed@packet@dvipdfm}{% + \hyxmp@string@len{\hyxmp@xml}% + \special{pdf: object @hyxmp@Metadata + << + /Type /Metadata + /Subtype /XML + /Length \the\@tempcnta + >> + stream^^J\hyxmp@xml endstream% + }% + \special{pdf: docview + << + /Metadata @hyxmp@Metadata + >> + }% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@string@len} +% Set |\@tempcnta| to the number of characters in a given string~(|#1|). +% The approach is first to tally the number of space characters then to +% tally the number of non-space characters. While this is rather +% sloppy I haven't found a better way to achieve the same effect, +% especially given that all of the characters in |#1| have already been +% assigned their category codes. +% \begin{macrocode} +\newcommand*{\hyxmp@string@len}[1]{% + \@tempcnta=0 + \expandafter\hyxmp@count@spaces#1 {} % + \expandafter\hyxmp@count@non@spaces#1{}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@count@spaces} +% Count the number of spaces in a given string. We rely on the built-in +% pattern matching of \tex's |\def| primitive to pry one word at a time +% off the head of the input string. +% \begin{macrocode} +\def\hyxmp@count@spaces#1 {% + \def\hyxmp@one@token{#1}% + \ifx\hyxmp@one@token\@empty + \advance\@tempcnta by -1 + \else + \advance\@tempcnta by 1 + \expandafter\hyxmp@count@spaces + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hyxmp@count@non@spaces} +% Count the number of non-spaces in a given string. Ideally, we'd count +% both spaces and non-spaces but \tex\ won't bind |#1| to a space +% character (category code~10). Hence, in each iteration, |#1| is bound +% to the next non-space character only. +% \begin{macrocode} +\newcommand*{\hyxmp@count@non@spaces}[1]{% + \def\hyxmp@one@token{#1}% + \ifx\hyxmp@one@token\@empty + \else + \advance\@tempcnta by 1 + \expandafter\hyxmp@count@non@spaces + \fi +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Embedding using \XeTeX} +% +% \begin{macro}{\hyxmp@embed@packet@xetex} +% Embed the \acro{XMP} packet using \cmdname{xdvipdfmx}-specific +% |\special| commands. I don't know how to tell \cmdname{xdvipdfmx} +% always to leave the \pdfterm{Metadata} stream uncompressed, so the +% \acro{XMP} metadata is likely to be missed by non-\acro{PDF}-aware +% \acro{XMP} viewers. +% \begin{macrocode} +\newcommand*{\hyxmp@embed@packet@xetex}{% + \special{pdf:stream @hyxmp@Metadata (\hyxmp@xml) + << + /Type /Metadata + /Subtype /XML + >> + }% + \special{pdf:put @catalog + << + /Metadata @hyxmp@Metadata + >> + }% +} +% \end{macrocode} +% \end{macro} +% +% +% \subsection{Final clean-up} +% \label{sec:clean-up} +% +% Having saved the category code of +% ``\,{\fontencoding{T1}\selectfont\textquotedbl}\,'' at the start of +% the package code (Section~\ref{sec:initial-prep}), we now restore that +% character's original category code. +% +% \begin{macrocode} +\catcode`\"=\hyxmp@dq@code +% \end{macrocode} +% +% \Finale +\endinput -- cgit v1.2.3