<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">% \iffalse meta-comment
%
% Copyright (C) 2006 by Scott Pakin &lt;scott+hyxmp@pakin.org&gt;
% -------------------------------------------------------
%
% This file may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3b
% 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.3b or later is part of all distributions of LaTeX
% version 2006/01/07 or later.
%
% \fi
%
% \iffalse
%&lt;*driver&gt;
\ProvidesFile{hyperxmp.dtx}
%&lt;/driver&gt;
%&lt;package&gt;\NeedsTeXFormat{LaTeX2e}[1999/12/01]
%&lt;package&gt;\ProvidesPackage{hyperxmp}
%&lt;*package&gt;
    [2006/05/21 v1.1 Store hyperref metadata in XMP format]
%&lt;/package&gt;
%
%&lt;*driver&gt;
\documentclass{ltxdoc}
\usepackage{tocbibind}
\usepackage{hyperxmp}
\usepackage[bookmarksopen]{hyperref}
\EnableCrossrefs
\CodelineIndex
\RecordChanges
\begin{document}
  \DocInput{hyperxmp.dtx}
  \PrintChanges
  \PrintIndex
\end{document}
%&lt;/driver&gt;
% \fi
%
% \CheckSum{756}
%
% \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     \&amp;
%   Acute accent  \'     Left paren    \(     Right paren   \)
%   Asterisk      \*     Plus          \+     Comma         \,
%   Minus         \-     Point         \.     Solidus       \/
%   Colon         \:     Semicolon     \;     Less than     \&lt;
%   Equals        \=     Greater than  \&gt;     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}
%
% \GetFileInfo{hyperxmp.dtx}
%
% \DoNotIndex{\#,\&amp;,\&lt;,\&gt;,\\,\_,\|,\ }
% \DoNotIndex{\@cons,\@elt,\@empty,\@ifpackageloaded,\@ifundefined}
% \DoNotIndex{\@tempcnta,\@tempcntb}
% \DoNotIndex{\advance,\afterassignment,\aftergroup}
% \DoNotIndex{\begingroup,\bgroup}
% \DoNotIndex{\catcode}
% \DoNotIndex{\def,\divide}
% \DoNotIndex{\edef,\egroup,\else,\endgroup,\expandafter}
% \DoNotIndex{\fi,\futurelet}
% \DoNotIndex{\gdef,\global}
% \DoNotIndex{\if,\ifcase,\ifnum,\ifx,\immediate}
% \DoNotIndex{\let,\loop}
% \DoNotIndex{\MessageBreak,\multiply}
% \DoNotIndex{\newcommand,\noexpand}
% \DoNotIndex{\or}
% \DoNotIndex{\relax,\repeat}
% \DoNotIndex{\space,\string}
% \DoNotIndex{\the,\toks}
% \DoNotIndex{\xdef}
%
% ^^A  Define a few logical styles.
% \DeclareRobustCommand{\term}[1]{#1\SortIndex{#1}{#1}}
% \DeclareRobustCommand{\pkgname}[1]{\textsf{#1}\SortIndex{#1}{\textsf{#1}}}
% \DeclareRobustCommand{\xmpterm}[1]{\textsf{#1}\SortIndex{#1}{\textsf{#1}}}
% \DeclareRobustCommand{\pdfterm}[1]{\textsf{#1}\SortIndex{#1}{\textsf{#1}}}
% \DeclareRobustCommand{\acrostyle}[1]{\textsc{\MakeLowercase{#1}}}
% \DeclareRobustCommand{\acro}[1]{^^A
%   \acrostyle{#1}^^A
%   \SortIndex{#1}{\acrostyle{#1}}^^A
% }
%
% ^^A  Specify this document's metadata.
% \title{The \pkgname{hyperxmp} package\thanks{This document
%   corresponds to \pkgname{hyperxmp}~\fileversion, dated \filedate.}}
% \author{Scott Pakin \\ \texttt{scott+hyxmp@pakin.org}}
% \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) 2006, Scott Pakin},
%   pdflicenseurl={http://www.latex-project.org/lppl/}
% }
%
% \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 recently been promoting
% \acro{XMP}~\cite{Adobe2005: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 of \acro{PDF}, \acrostyle{JPEG},
% \acrostyle{HTML}, or any other type, 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}
%    &lt;dc:creator&gt;
%      &lt;rdf:Seq&gt;
%        &lt;rdf:li&gt;Jack Napier&lt;/rdf:li&gt;
%        &lt;rdf:li&gt;Edward Nigma&lt;/rdf:li&gt;
%        &lt;rdf:li&gt;Harvey Dent&lt;/rdf:li&gt;
%      &lt;/rdf:Seq&gt;
%    &lt;/dc:creator&gt;
% \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 |dc:creator| property surrounds the list of
% authors.  The |rdf| namespace is the
% \href{http://www.w3.org/RDF/}{Resource Description Framework}, which
% defines |rdf:Seq| as an ordered list of values.  Each author is
% represented by an individual list item (|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 each of the following types of
% metadata within a document:
%
% \begin{itemize}
%   \item authors (|dc:creator|)
%   \item copyright (|dc:rights|)
%   \item date (|dc:date|)
%   \item document identifier (|xapMM:DocumentID|)
%   \item document instance identifier (|xapMM:InstanceID|)
%   \item format (|dc:format|)
%   \item keywords (|pdf:Keyword| and |dc:subject|)
%   \item license \acro{URL} (|xapRights:WebStatement|)
%   \item \acro{PDF}-generating tool (|pdf:Producer|)
%   \item summary (|dc:description|)
%   \item title (|dc:title|)
% \end{itemize}
%
% \noindent
% More types of metadata may be added in a future release.
%
% \paragraph{How does \textsf{hyperxmp} compare with 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 pdf\LaTeX\ 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} provides no commands of its own.  Rather, it
% processes some of the package options honored by \pkgname{hyperref}.
% To use \pkgname{hyperxmp}, merely put a |\usepackage{hyperxmp}|
% somewhere in your document's preamble.  \pkgname{hyperxmp} will
% construct its \acro{XMP} data using the following \pkgname{hyperref} options:
%
% \begin{itemize}
%   \item |pdfauthor|,
%   \item |pdfkeywords|,
%   \item |pdfproducer|,
%   \item |pdfsubject|, and
%   \item |pdftitle|.
% \end{itemize}
%
% \noindent
% \pkgname{hyperxmp} instructs \pkgname{hyperref} also to accept the
% following options, which have meaning only to \pkgname{hyperxmp}:
%
% \begin{itemize}
%   \item |pdfcopyright| and
%   \item |pdflicenseurl|.
% \end{itemize}
%
% \noindent
% |\pdfcopyright| defines the copyright text.  |pdflicenseurl| defines a
% \acro{URL} that points to the document's license agreement.
%
% It's usually more convenient to provide values for those options using
% \pkgname{hyperref}'s |\hypersetup| command than on the |\usepackage|
% command line.  See
% \href{ftp://tug.ctan.org/pub/tex-archive/macros/latex/contrib/hyperref/doc/manual.pdf}{the
% \pkgname{hyperref} manual} for more information.  The following is a
% sample \LaTeX\ document that provides values for most of the metadata
% options that \pkgname{hyperxmp} recognizes:
%
% \begin{verbatim}
%     \documentclass{article}
%     \usepackage{hyperxmp}
%     \usepackage{hyperref}
%     \title{%
%       On a heuristic viewpoint concerning the production and
%       transformation of light}
%     \author{Albert Einstein}
%     \hypersetup{%
%       pdftitle={%
%         On a heuristic viewpoint concerning the production and
%         transformation of light},
%       pdfauthor={Albert Einstein},
%       pdfcopyright={Copyright (C) 1905, Albert Einstein},
%       pdfsubject={photoelectric effect},
%       pdfkeywords={energy quanta, Hertz effect, quantum physics}
%     }
%     \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}
%
% Compile the document to \acro{PDF} using any of the following approaches:
%
% \begin{itemize}
%   \item pdf\LaTeX
%   \item \LaTeX~$+$ Dvipdfm
%   \item \LaTeX~$+$ Dvips~$+$ Ghostscript
% \end{itemize}
%
% The combination \LaTeX~$+$ Dvips~$+$ Adobe Acrobat Distiller
% \emph{almost} works but is hampered by a Distiller bug (at least in
% version~7.0.5) that incorrectly replaces the first author with the
% complete list of authors in the generated \acro{PDF} file.  That is,
% if a document's authors are Jack Napier, Edward Nigma, and Harvey
% Dent, Distiller replaces ``Jack Napier'' with a single author named
% ``Jack Napier, Edward Nigma, Harvey Dent'' and leaves ``Edward Nigma''
% and ``Harvey Dent'' as the second and third authors, respectively.
% Until Adobe fixes this bug, Adobe Acrobat Distiller is not recommended
% for use with \pkgname{hyperxmp}.
%
% Besides the approaches listed above, other approaches may work as well
% but have not been tested.  Note that in many \TeX\ distributions
% |ps2pdf| is a convenience script that calls Ghostscript with the
% appropriate options for converting PostScript to \acro{PDF} and
% |dvipdf| is a convenience script that calls |dvips| and |ps2pdf|; both
% |ps2pdf| and |dvipdf| should be compatible with \pkgname{hyperxmp}.
%
% \bigskip
%
% The resulting \acro{PDF} file will contain an \acro{XMP} packet that
% looks something like this:
%
% \begin{verbatim}
%     &lt;?xpacket begin="???" id="W5M0MpCehiHzreSzNTczkc9d"?&gt;
%     &lt;x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="3.1-702"&gt;
%        &lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"&gt;
%           &lt;rdf:Description rdf:about=""
%                xmlns:pdf="http://ns.adobe.com/pdf/1.3/"&gt;
%                &lt;pdf:Keywords&gt;energy quanta, Hertz effect,
%                quantum physics&lt;/pdf:Keywords&gt;
%                &lt;pdf:Producer&gt;pdfeTeX-1.10b&lt;/pdf:Producer&gt;
%           &lt;/rdf:Description&gt;
%           &lt;rdf:Description rdf:about=""
%                 xmlns:dc="http://purl.org/dc/elements/1.1/"&gt;
%              &lt;dc:format&gt;application/pdf&lt;/dc:format&gt;
%              &lt;dc:title&gt;
%                 &lt;rdf:Alt&gt;
%                    &lt;rdf:li xml:lang="x-default"&gt;On a heuristic viewpoint
%                    concerning the production and transformation of
%                    light&lt;/rdf:li&gt;
%                 &lt;/rdf:Alt&gt;
%              &lt;/dc:title&gt;
%              &lt;dc:description&gt;
%                 &lt;rdf:Alt&gt;
%                    &lt;rdf:li xml:lang="x-default"&gt;photoelectric effect&lt;/rdf:li&gt;
%                 &lt;/rdf:Alt&gt;
%              &lt;/dc:description&gt;
%              &lt;dc:rights&gt;
%                 &lt;rdf:Alt&gt;
%                    &lt;rdf:li xml:lang="x-default"&gt;Copyright (C) 1905,
%                    Albert Einstein&lt;/rdf:li&gt;
%                 &lt;/rdf:Alt&gt;
%              &lt;/dc:rights&gt;
%              &lt;dc:creator&gt;
%                 &lt;rdf:Seq&gt;
%                    &lt;rdf:li&gt;Albert Einstein&lt;/rdf:li&gt;
%                 &lt;/rdf:Seq&gt;
%              &lt;/dc:creator&gt;
%              &lt;dc:subject&gt;
%                 &lt;rdf:Bag&gt;
%                    &lt;rdf:li&gt;energy quanta&lt;/rdf:li&gt;
%                    &lt;rdf:li&gt;Hertz effect&lt;/rdf:li&gt;
%                    &lt;rdf:li&gt;quantum physics&lt;/rdf:li&gt;
%                 &lt;/rdf:Bag&gt;
%              &lt;/dc:subject&gt;
%              &lt;dc:date&gt;
%                 &lt;rdf:Seq&gt;
%                    &lt;rdf:li&gt;2006-04-19&lt;/rdf:li&gt;
%                 &lt;/rdf:Seq&gt;
%              &lt;/dc:date&gt;
%           &lt;/rdf:Description&gt;
%           &lt;rdf:Description rdf:about=""
%                 xmlns:xapMM="http://ns.adobe.com/xap/1.0/mm/"&gt;
%              &lt;xapMM:DocumentID&gt;uuid:c4188820-aef2-0a82-626ce4182b62&lt;/xapMM:DocumentID&gt;
%              &lt;xapMM:InstanceID&gt;uuid:9b62b67f-d754-626c-4c959595fd75&lt;/xapMM:InstanceID&gt;
%           &lt;/rdf:Description&gt;
%        &lt;/rdf:RDF&gt;
%     &lt;/x:xmpmeta&gt;
%     &lt;?xpacket end="w"?&gt;
% \end{verbatim}
%
% \pkgname{hyperxmp} splits the |pdfauthor| and |pdfkeywords| lists at
% commas.  Therefore, when specifying |pdfauthor| and |pdfkeywords|, you
% should separate items with commas.  Also, omit ``|and|'' and other
% text that does not belong to any list item.  The following example
% should serve as clarification:
%
% \begin{description}
%   \item[Wrong:] |pdfauthor={Jack Napier, Edward Nigma, and Harvey Dent}|
%   \item[Wrong:] |pdfauthor={Jack Napier; Edward Nigma; Harvey Dent}|
%   \item[Right:] |pdfauthor={Jack Napier, Edward Nigma, Harvey Dent}|
% \end{description}
%
% If you desperately need to include a comma within an author or keyword
% list you can define your own comma macro as follows:
%
% \begin{verbatim}
%     \bgroup
%     \catcode`,=11
%     \gdef\mycomma{,}
%     \egroup
% \end{verbatim}
%
% \noindent
% Thereafter, you can use |\mycomma| as a literal comma:
%
% \begin{verbatim}
%     pdfauthor={Napier\mycomma\ Jack,
%                Nigma\mycomma\ Edward,
%                Dent\mycomma\ Harvey}
% \end{verbatim}
%
%
% \StopEventually{^^A
% \begin{thebibliography}{1}
% \bibitem{Adobe2004:PDF}
% Adobe Systems, Inc., San Jose, California.
% \newblock {\em {PDF} Reference, Fifth Edition: {A}dobe Portable Document Format
%   Version~1.6}, November 2004.
% \newblock Available from
%   \url{http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf}.
%
% \bibitem{Adobe2005:pdfmark}
% Adobe Systems, Inc., San Jose, California.
% \newblock {\em {A}dobe {A}crobat~7.0.5 pdfmark Reference Manual}, October~2,
%   2005.
% \newblock Available from
%   \url{http://partners.adobe.com/public/developer/en/acrobat/sdk/pdf/pdf_creation_apis_and_specs/pdfmarkReference.pdf}.
%
% \bibitem{Adobe2005:XMP}
% Adobe Systems, Inc., San Jose, California.
% \newblock {\em {XMP} Specification}, June 2005.
% \newblock Available from
%   \url{http://partners.adobe.com/public/developer/en/xmp/sdk/xmpspecification.pdf}.
%
% \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}.
% \end{thebibliography}
% }
%
%
% \section{Implementation}
%
% This section presents the commented \LaTeXe\ source code for
% \pkgname{hyperxmp}.  Read this section only if you want to learn how
% \pkgname{hyperxmp} is implemented.
%
%
% \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 the
% \pkgname{hyperref} |pdftitle|, |pdfauthor|, |pdfsubject|, and
% |pdfkeywords| options plus two new options, |pdfcopyright| and
% |pdflicenseurl|, introduced by \pkgname{hyperxmp}.
%
%    \begin{macrocode}
\RequirePackage{keyval}
%    \end{macrocode}
%
% \begin{macro}{\@pdfcopyright}
% Prepare to store the document's copyright statement.  For consistency
% with \pkgname{hyperref}'s document-metadata naming conventions (which
% are in turn based on \LaTeXe's document-metadata naming conventions),
% we do not prefix the macro name with our package-specific |\hyxmp@|
% prefix.
%    \begin{macrocode}
\def\@pdfcopyright{}
\define@key{Hyp}{pdfcopyright}{\pdfstringdef\@pdfcopyright{#1}}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@pdflicenseurl}
% Prepare to store the \acro{URL} containing the document's license
% agreement.  For consistency with \pkgname{hyperref}'s
% document-metadata naming conventions (which are in turn based on
% \LaTeXe's document-metadata naming conventions), we do not prefix the
% macro name with our package-specific |\hyxmp@| prefix.
%    \begin{macrocode}
\def\@pdflicenseurl{}
\define@key{Hyp}{pdflicenseurl}{\pdfstringdef\@pdflicenseurl{#1}}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\hyxmp@find@metadata}
% Issue a warning message if the author failed to include any metadata
% at all.
%    \begin{macrocode}
\newcommand*{\hyxmp@find@metadata}{%
  \ifx\@pdfauthor\@empty
    \ifx\@pdfcopyright\@empty
      \ifx\@pdfkeywords\@empty
        \ifx\@pdflicenseurl\@empty
          \ifx\@pdfsubject\@empty
            \ifx\@pdftitle\@empty
              \PackageWarningNoLine{hyperxmp}{%
\jobname.tex did not specify any metadata to\MessageBreak
include in the XMP packet.\space\space Please see the hyperxmp\MessageBreak
documentation for instructions on how to provide\MessageBreak
metadata values to hyperxmp%
              }%
            \fi
          \fi
        \fi
      \fi
    \fi
  \fi
}
%    \end{macrocode}
% \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}
% 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}
      \AtEndDocument{%
        \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 the \pkgname{hyperref}
% |\hypersetup| command.  The functions in this section convert
% author-supplied lists (e.g.,~|pdfkeywords={foo, bar, baz}|) into
% \LaTeXe\ lists (e.g.,~|\@elt {foo}| |\@elt {bar}| |\@elt {baz}|) that
% can be more easily manipulated (Section~\ref{sec:list-manip}); define
% macros for the \acro{XML} entites |&amp;lt;|, |&amp;gt;|, and |&amp;amp;|
% (Section~\ref{sec:xml-entities}); 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 |&lt;scott+hyxmp@pakin.org&gt;| to
% |&amp;lt;scott+hyxmp@pakin.org&amp;gt;|).
%
% \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 \LaTeXe\
% |\@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}
%
% \subsubsection{Character-code and XML entity definitions}
% \label{sec:xml-entities}
%
% The \pkgname{hyperref} package invokes |\pdfstringdef| on its metadata
% parameters, setting every character to \TeX\ category code~11
% (``other'').  To match against these, we have to define a few category
% code~11 characters of our own.  Furthermore, because \acro{XMP} is an
% \acro{XML} format, we have to replace the characters ``|&amp;|'', ``|&lt;|'',
% and ``|&gt;|'' with equivalent \acro{XML} entities.
%
% \begin{macro}{\hyxmp@xml@amp}
% \begin{macro}{\hyxmp@other@amp}
% \begin{macro}{\hyxmp@amp}
% Define category code~11 (``other'') versions of the character~``|&amp;|''
% and map |\hyxmp@other@amp| to its \acro{XML} entity, |&amp;amp;|.
%    \begin{macrocode}
\bgroup
\catcode`\&amp;=11
\gdef\hyxmp@xml@amp{&amp;amp;}
\global\let\hyxmp@other@amp=&amp;
\gdef\hyxmp@amp{&amp;}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\hyxmp@xml@lt}
% \begin{macro}{\hyxmp@other@lt}
% Define a category code~11 (``other'') version of the character~``|&lt;|''
% and map |\hyxmp@other@lt| to its \acro{XML} entity, |&amp;lt;|.
%    \begin{macrocode}
\catcode`\&lt;=11
\gdef\hyxmp@xml@lt{&amp;lt;}
\global\let\hyxmp@other@lt=&lt;
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\hyxmp@xml@gt}
% \begin{macro}{\hyxmp@other@gt}
% Define a category code~11 (``other'') version of the character~``|&gt;|''
% and map |\hyxmp@other@gt| to its \acro{XML} entity, |&amp;gt;|.
%    \begin{macrocode}
\catcode`\&gt;=11
\gdef\hyxmp@xml@gt{&amp;gt;}
\global\let\hyxmp@other@gt=&gt;
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\hyxmp@other@space}
% \begin{macro}{\next}
% Define a category code~11 (``other'') version of the space character.
%    \begin{macrocode}
\def\next#1{#1}
\next{\global\let\hyxmp@other@space= } %
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\hyxmp@other@bs}
% Define a category code~11 (``other'') version of the character~``|\|''.
%    \begin{macrocode}
\catcode`\|=0
\catcode`\\=11
|global|let|hyxmp@other@bs=\
|egroup
%    \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 ``|&lt;|'', ``|&gt;|'', and ``|&amp;|'' characters are significant to \acro{XML}.
% We therefore need to escape them in any author-supplied text.
%
% \begin{macro}{\hyxmp@xmlify}
% \begin{macro}{\hyxmp@xmlified}
% 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~``|&lt;|''
% replaced with~|&amp;lt;|, all occurrences of~``|&gt;|'' replaced with~|&amp;gt;|,
% and all occurrences of~``|&amp;|'' replaced with~|&amp;amp;|.
%
% If |\pdfmark| is defined then there's a chance the user will run
% |dvips| on the resulting \acro{DVI} file and |dvips| may convert some
% of the spaces to newlines, which is problematic for the proper display
% of an \acro{XMP} packet.  We therefore conditionally invoke
% |\hyxmp@obscure@spaces| to replace all spaces with |&amp;#32;|.
%    \begin{macrocode}
\newcommand*{\hyxmp@xmlify}[1]{%
  \gdef\hyxmp@xmlified{}%
  \expandafter\hyxmp@xmlify@i#1\@empty
  \@ifundefined{pdfmark}{}{%
    \expandafter\hyxmp@obscure@spaces\expandafter{\hyxmp@xmlified}%
  }%
}
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\hyxmp@xmlify@i}
% \begin{macro}{\hyxmp@one@token}
% Bind the next token in the input stream to |\hyxmp@one@token| and
% invoke |\hyxmp@xmlify@ii|.  |\hyxmp@xmlify@i| (and therefore
% |\hyxmp@xmlify@ii|) is invoked on each character in the text supplied
% to |\hyxmp@xmlify|.
%    \begin{macrocode}
\def\hyxmp@xmlify@i{\futurelet\hyxmp@one@token\hyxmp@xmlify@ii}
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\hyxmp@xmlify@ii}
% \begin{macro}{\next}
% Given a token in |\hyxmp@one@token|, define |\next| to consume the
% token, append the corresponding text to |\hyxmp@xmlified|, and
% recursively invoke |\hyxmp@xmlify@i| to consume subsequent tokens.
%    \begin{macrocode}
\def\hyxmp@xmlify@ii{%
  \if\hyxmp@one@token\hyxmp@other@lt
%    \end{macrocode}
% Replace ``|&lt;|'' with |&amp;lt;|.
%    \begin{macrocode}
    \def\next##1{%
      \xdef\hyxmp@xmlified{\hyxmp@xmlified\hyxmp@xml@lt}%
      \hyxmp@xmlify@i
    }%
  \else
    \if\hyxmp@one@token\hyxmp@other@gt
%    \end{macrocode}
% Replace ``|&gt;|'' with |&amp;gt;|.
%    \begin{macrocode}
      \def\next##1{%
        \xdef\hyxmp@xmlified{\hyxmp@xmlified\hyxmp@xml@gt}%
        \hyxmp@xmlify@i
      }%
    \else
      \if\hyxmp@one@token\hyxmp@other@amp
%    \end{macrocode}
% Replace ``|&amp;|'' with |&amp;amp;|.
%    \begin{macrocode}
        \def\next##1{%
          \xdef\hyxmp@xmlified{\hyxmp@xmlified\hyxmp@xml@amp}%
          \hyxmp@xmlify@i
        }%
      \else
        \ifx\hyxmp@one@token\hyxmp@other@space
%    \end{macrocode}
% Store spaces.  We need a special case for this to avoid inadvertently
% discarding spaces.
%    \begin{macrocode}
          \def\next##1{%
            \g@addto@macro\hyxmp@xmlified{ }%
            \hyxmp@xmlify@i##1%
          }%
        \else
          \if\hyxmp@one@token\hyxmp@other@bs
%    \end{macrocode}
% Replace |\|\meta{ooo} with |&amp;#|\meta{ddd}|;|.  For example, |\100|,
% the octal code for ``|@|'', is represented in \acro{XML} as~|&amp;#64;|.
%    \begin{macrocode}
            \def\next##1{\futurelet\hyxmp@one@token\hyxmp@xmlify@iii}
          \else
            \ifx\hyxmp@one@token\@empty
%    \end{macrocode}
% End the recursion upon encountering |\@empty|.
%    \begin{macrocode}
              \def\next##1{}%
            \else
%    \end{macrocode}
% In most cases we merely append the next character in the input to
% |\hyxmp@xmlified| without any special processing.
%    \begin{macrocode}
              \def\next##1{%
                \g@addto@macro\hyxmp@xmlified{##1}%
                \hyxmp@xmlify@i
              }%
            \fi
          \fi
        \fi
      \fi
    \fi
  \fi
%    \end{macrocode}
% Recursively process the next character in the input stream.
%    \begin{macrocode}
  \next
}
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\hyxmp@xmlify@iii}
% \begin{macro}{\next}
% \pkgname{hyperref}'s |\pdfstringdef| macro converts certain special
% characters to a backslash followed by a three-digit octal number.
% However, it also replaces ``|(|'' and ``|)|'' with ``|\(|'' and
% ``|\)|''.  The |\hyxmp@xmlify@iii| macro is called after encountering
% (and removing) a backslash.  If the next character in the input stream
% (|\hyxmp@one@token|) is a parenthesis, |\hyxmp@xmlify@iii| leaves it
% alone.  Otherwise, |\hyxmp@xmlify@iii| assumes it's an octal number
% and replaces it with its \acro{XML} equivalent.
%    \begin{macrocode}
\def\hyxmp@xmlify@iii{%
  \def\next##1##2##3{%
    \@tempcnta='##1##2##3
    \xdef\hyxmp@xmlified{\hyxmp@xmlified
      \hyxmp@amp\hyxmp@hash\the\@tempcnta;%
    }%
    \hyxmp@xmlify@i
  }%
  \if\hyxmp@one@token(
    \let\next=\hyxmp@xmlify@i
  \else
    \if\hyxmp@one@token)
      \let\next=\hyxmp@xmlify@i
    \fi
  \fi
  \next
}
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\hyxmp@obscure@spaces}
% The |dvips| backend rather obnoxiously word-wraps text.  Doing so can
% cause \acro{XMP} metadata to be displayed incorrectly.  For example,
% Adobe Acrobat displays the document's |dc:rights| (copyright notice)
% within a single-line field.  By introducing an extra line break in the
% middle of the copyright notice, |dvips| implicitly causes it to be
% truncated when displayed.
%
% To thwart |dvips|'s word-wrapping, we define |\hyxmp@obscure@spaces|
% to replace each space in a given piece of text with an \acro{XML} |&amp;#32;|
% (space) entity.
%    \begin{macrocode}
\newcommand*{\hyxmp@obscure@spaces}[1]{%
  \gdef\hyxmp@xmlified{}%
  \expandafter\hyxmp@obscure@spaces@i#1 {} %
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\hyxmp@obscure@spaces@i}
% \begin{macro}{\hyxmp@one@token}
% \begin{macro}{\next}
% Do all of the work for |\hyxmp@obscure@spaces|.
%    \begin{macrocode}
\def\hyxmp@obscure@spaces@i #1 #2 {%
  \def\hyxmp@one@token{#2}%
  \ifx\hyxmp@one@token\@empty
    \xdef\hyxmp@xmlified{\hyxmp@xmlified#1}%
    \let\next=\relax
  \else
    \xdef\hyxmp@xmlified{\hyxmp@xmlified#1\hyxmp@amp\hyxmp@hash32;}%
    \def\next{\expandafter\hyxmp@obscure@spaces@i\expandafter#2 }%
  \fi
  \next
}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
%
% \subsection{UUID generation}
%
% We use a linear congruential generator to produce pseudorandom
% \acro{UUID}s.  True, this method has its flaws but it's simple to
% implement in \TeX\ and is good enough for producing the \acro{XMP}
% \xmpterm{DocumentID} and \xmpterm{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&lt;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@iv}
% Invoke |\hyxmp@append@hex| four times.
%    \begin{macrocode}
\def\hyxmp@append@hex@iv#1{%
  \hyxmp@append@hex#1%
  \hyxmp@append@hex#1%
  \hyxmp@append@hex#1%
  \hyxmp@append@hex#1%
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\hyxmp@create@uuid}
% Define macro~|#1| as a \acro{UUID} of the form
% ``|uuid:|\textit{xxxxxxxx}|-|\textit{xxxx}|-|\textit{xxxx}|-|\textit{xxxxxxxxxxxx}''
% in which each ``\textit{x}'' is a lowercase hexadecimal digit.  We
% assume that the random-number generator is already seeded.  Note that
% |\hyxmp@create@uuid| overwrites both |\@tempcnta| and |\@tempcntb|.
%    \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{-}%
  \hyxmp@append@hex@iv#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}
% 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}.
%    \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}
%
% \begin{macro}{\hyxmp@def@InstanceID}
% \begin{macro}{\hyxmp@InstanceID}
% Seed the random-number generator with a function of the current
% filename, \acro{PDF} document title, \acro{PDF} author, and the
% current day, month, year, and minutes since midnight, then invoke
% |\hyxmp@create@uuid| to define |\hyxmp@InstanceID| as a random
% \acro{UUID}.
%    \begin{macrocode}
\newcommand*{\hyxmp@def@InstanceID}{%
  \edef\hyxmp@seed@string{%
    \jobname:\@pdftitle:\@pdfauthor:%
    \the\year/\the\month/\the\day:%
    \the\time
  }%
  \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}
%
%
% \subsection{Constructing the XMP packet}
%
% An \acro{XMP} packet comprises a header, ``serialized \acro{XMP}'',
% padding, and a trailer~\cite{Adobe2005:XMP}.  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}), and \acro{XMP} Media Management
% (Section~\ref{sec:xmp-media}).  The |\hyxmp@construct@packet| macro
% 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 append the result to the |\hyxmp@xml| macro.
%    \begin{macrocode}
\newcommand*{\hyxmp@add@to@xml}[1]{%
  \bgroup
    \@tempcnta=0
    \loop
      \lccode\@tempcnta=\@tempcnta
      \advance\@tempcnta by 1
      \ifnum\@tempcnta&lt;256
    \repeat
    \lccode`\_=`\ \relax
    \lowercase{\xdef\hyxmp@xml{\hyxmp@xml#1}}%
  \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~\cite{Adobe2005:XMP} recommends leaving a
% few kilobytes of whitespace at the end of each \acro{XMP} packet to
% facilitate editing the packet in place.  |\hyxmp@padding| is defined
% to contain 32~lines of 50~spaces and a newline apiece for a total of
% 1632 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@today}
% Define today's date in \textit{YYYY}-\textit{MM}-\textit{DD} format.
%    \begin{macrocode}
\xdef\hyxmp@today{\the\year}%
\ifnum\month&lt;10
  \xdef\hyxmp@today{\hyxmp@today-0\the\month}%
\else
  \xdef\hyxmp@today{\hyxmp@today-\the\month}%
\fi
\ifnum\day&lt;10
  \xdef\hyxmp@today{\hyxmp@today-0\the\day}%
\else
  \xdef\hyxmp@today{\hyxmp@today-\the\day}%
\fi
%    \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}
% \begin{macro}{\hyxmp@have@any}
% Include an Adobe \acro{PDF} schema block if at least one of
% |\@pdfkeywords| and |\@pdfproducer| is defined.
%    \begin{macrocode}
  \let\hyxmp@have@any=!%
  \ifx\@pdfkeywords\@empty
    \ifx\@pdfproducer\@empty
      \let\hyxmp@have@any=\@empty
    \fi
  \fi
  \ifx\hyxmp@have@any\@empty
  \else
%    \end{macrocode}
% Add a block of \acro{XML} to |\hyxmp@xml| that lists the document's keywords
% (the \xmpterm{Keywords} property) and the tools used to produce the
% \acro{PDF} file (the \xmpterm{Producer} property).
%    \begin{macrocode}
    \hyxmp@add@to@xml{%
______&lt;rdf:Description rdf:about=""^^J%
___________xmlns:pdf="http://ns.adobe.com/pdf/1.3/"&gt;^^J%
    }%
    \ifx\@pdfkeywords\@empty
    \else
      \hyxmp@xmlify{\@pdfkeywords}%
      \hyxmp@add@to@xml{%
___________&lt;pdf:Keywords&gt;\hyxmp@xmlified&lt;/pdf:Keywords&gt;^^J%
      }%
    \fi
    \ifx\@pdfproducer\@empty
    \else
      \hyxmp@xmlify{\@pdfproducer}%
      \hyxmp@add@to@xml{%
___________&lt;pdf:Producer&gt;\hyxmp@xmlified&lt;/pdf:Producer&gt;^^J%
      }%
    \fi
    \hyxmp@add@to@xml{%
______&lt;/rdf:Description&gt;^^J%
    }%
  \fi
}
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsubsection{The Dublin Core schema}
% \label{sec:dublin-core}
%
% \begin{macro}{\hyxmp@rdf@dc}
% Given a Dublin Core property~(|#1|) and a macro containing some
% |\pdfstringdef|-defined text~(|#2|), append the appropriate block of
% \acro{XML} to the |\hyxmp@xml| macro but only if |#2| is non-empty.
%    \begin{macrocode}
\newcommand*{\hyxmp@rdf@dc}[2]{%
  \ifx#2\@empty
  \else
    \hyxmp@xmlify{#2}%
    \hyxmp@add@to@xml{%
_________&lt;dc:#1&gt;^^J%
____________&lt;rdf:Alt&gt;^^J%
_______________&lt;rdf:li xml:lang="x-default"&gt;\hyxmp@xmlified&lt;/rdf:li&gt;^^J%
____________&lt;/rdf:Alt&gt;^^J%
_________&lt;/dc:#1&gt;^^J%
    }%
  \fi%
}%
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\hyxmp@list@to@xml}
% Given a Dublin Core property~(|#1|), an RDF array~(|#2|), and a macro
% containing a comma-separated list~(|#3|), append the appropriate block
% of \acro{XML} to the |\hyxmp@xml| macro but only if |#3| is non-empty.
%    \begin{macrocode}
\newcommand*{\hyxmp@list@to@xml}[3]{%
  \ifx#3\@empty
  \else
    \hyxmp@add@to@xml{%
_________&lt;dc:#1&gt;^^J%
____________&lt;rdf:#2&gt;^^J%
    }%
    \bgroup
      \hyxmp@commas@to@list\hyxmp@list{#3}%
      \def\@elt##1{%
        \hyxmp@xmlify{##1}%
        \hyxmp@add@to@xml{%
_______________&lt;rdf:li&gt;\hyxmp@xmlified&lt;/rdf:li&gt;^^J%
        }%
      }%
      \hyxmp@list
    \egroup
    \hyxmp@add@to@xml{%
____________&lt;/rdf:#2&gt;^^J%
_________&lt;/dc:#1&gt;^^J%
    }%
  \fi
}
%    \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 \xmpterm{title} property
% if the author specified a |pdftitle|, the \xmpterm{description}
% property if the author specified a |pdfsubject|, the \xmpterm{rights}
% property if the author specified a |pdfcopyright|, the
% \xmpterm{creator} property if the author specified a |pdfauthor|, and
% the \xmpterm{subject} property if the author specified |pdfkeywords|.
% We also specify the \xmpterm{date} property using the date the
% document was run through \LaTeX.
%    \begin{macrocode}
\newcommand*{\hyxmp@dc@schema}{%
  \hyxmp@add@to@xml{%
______&lt;rdf:Description rdf:about=""^^J%
____________xmlns:dc="http://purl.org/dc/elements/1.1/"&gt;^^J%
_________&lt;dc:format&gt;application/pdf&lt;/dc:format&gt;^^J%
  }%
  \hyxmp@rdf@dc{title}{\@pdftitle}%
  \hyxmp@rdf@dc{description}{\@pdfsubject}%
  \hyxmp@rdf@dc{rights}{\@pdfcopyright}%
  \hyxmp@list@to@xml{creator}{Seq}{\@pdfauthor}%
  \hyxmp@list@to@xml{subject}{Bag}{\@pdfkeywords}%
  \hyxmp@list@to@xml{date}{Seq}{\hyxmp@today}%
  \hyxmp@add@to@xml{%
______&lt;/rdf:Description&gt;^^J%
  }%
}
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{The XMP Rights Management schema}
% \label{sec:xmp-rights}
%
% \begin{macro}{\hyxmp@xapRights@schema}
% Add properties defined by the XMP Rights Management schema to the
% |\hyxmp@xml| macro.  Currently, these are only the \xmpterm{Marked}
% property and the \xmpterm{WebStatement} property and only if the
% author defined a |pdflicenseurl|.
%    \begin{macrocode}
\newcommand*{\hyxmp@xapRights@schema}{%
  \ifx\@pdflicenseurl\@empty
  \else
    \hyxmp@xmlify{\@pdflicenseurl}%
    \hyxmp@add@to@xml{%
______&lt;rdf:Description rdf:about=""^^J%
___________xmlns:xapRights="http://ns.adobe.com/xap/1.0/rights/"&gt;^^J%
_________&lt;xapRights:Marked&gt;True&lt;/xapRights:Marked&gt;^^J%
_________&lt;xapRights:WebStatement&gt;\hyxmp@xmlified&lt;/xapRights:WebStatement&gt;^^J%
______&lt;/rdf:Description&gt;^^J%
    }%
  \fi
}
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{The XMP Media Management schema}
% \label{sec:xmp-media}
%
% \begin{macro}{\hyxmp@mm@schema}
% Add properties defined by the XMP Media Management schema to the
% |\hyxmp@xml| macro.  Although the \xmpterm{DocumentID} property is
% defined in the \acro{XMP} specification~\cite{Adobe2005:XMP}, the
% \xmpterm{InstanceID} property is not.  However, an
% \xmpterm{InstanceID} field is produced by Adobe Acrobat~7.0 (the
% latest version at the time of this writing) so it's probably worth
% including here.
%    \begin{macrocode}
\gdef\hyxmp@mm@schema{%
  \hyxmp@def@DocumentID
  \hyxmp@def@InstanceID
  \hyxmp@add@to@xml{%
______&lt;rdf:Description rdf:about=""^^J%
____________xmlns:xapMM="http://ns.adobe.com/xap/1.0/mm/"&gt;^^J%
_________&lt;xapMM:DocumentID&gt;\hyxmp@DocumentID&lt;/xapMM:DocumentID&gt;^^J%
_________&lt;xapMM:InstanceID&gt;\hyxmp@InstanceID&lt;/xapMM:InstanceID&gt;^^J%
______&lt;/rdf:Description&gt;^^J%
  }%
}
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Constructing the XMP packet}
%
% \begin{macro}{\hyxmp@construct@packet}
% \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.  The \acro{XMP}
% specification~\cite{Adobe2005:XMP} states that the argument to the
% \xmpterm{begin} attribute must be ``the Unicode `zero-width
% non-breaking space character'~(U+FEFF)''.  However, Adobe Acrobat~7.0
% (the latest version at the time of this writing) inserts the sequence
% \meta{EF}\meta{BB}\meta{BF} so that's what we use here.
%
% We explicitly mark characters \meta{EF}, \meta{BB}, \meta{BF} as
% character code~12 (``letter'') because the \pkgname{inputenc} package
% re-encodes them as character code~13 (``active''), which causes
% \LaTeX\ to abort with an ``\texttt{Undefined control sequence}'' error
% upon invoking |\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}
%    \begin{macrocode}
\bgroup
\catcode`\^^ef=12
\catcode`\^^bb=12
\catcode`\^^bf=12
\gdef\hyxmp@construct@packet{%
  \gdef\hyxmp@xml{}%
  \hyxmp@add@to@xml{%
&lt;?xpacket begin="^^ef^^bb^^bf" id="W5M0MpCehiHzreSzNTczkc9d"?&gt;^^J%
&lt;x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="3.1-702"&gt;^^J%
___&lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns\hyxmp@hash"&gt;^^J%
  }%
  \hyxmp@pdf@schema
  \hyxmp@xapRights@schema
  \hyxmp@dc@schema
  \hyxmp@mm@schema
  \hyxmp@add@to@xml{%
___&lt;/rdf:RDF&gt;^^J%
&lt;/x:xmpmeta&gt;^^J%
\hyxmp@padding
&lt;?xpacket end="w"?&gt;^^J%
  }%
}
\egroup
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%
% \subsection{Embedding the XMP packet}
%
% The \acro{PDF} specification~\cite{Adobe2004:PDF} says that ``a
% metadata stream can be attached to a document through the
% \pdfterm{Metadata} entry in the document catalog'' so that's what we
% do here.  \pkgname{hyperxmp} does not currently support the embedding
% of \acro{XMP} in any format other than \acro{PDF}.
%
% \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{hdvipdfm}%
    \ifx\hyxmp@driver\Hy@driver
      \hyxmp@embed@packet@dvipdfm
    \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
}
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsubsection{Embedding using pdf\TeX}
%
% \begin{macro}{\hyxmp@embed@packet@pdftex}
% Embed the \acro{XMP} packet using pdf\TeX\ primitives.
%    \begin{macrocode}
\newcommand*{\hyxmp@embed@packet@pdftex}{%
  \bgroup
    \pdfcompresslevel=0
    \immediate\pdfobj stream attr {%
      /Type /Metadata
      /Subtype /XML
    }{\hyxmp@xml}%
    \pdfcatalog {/Metadata \the\pdflastobj\space 0 R}%
  \egroup
}
%    \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 |dvipdf|, |dvipsone|,
% |dvips|, |dviwindo|, |nativepdf|, |pdfmark|, |ps2pdf| |textures|, and
% |vtexpdfmark| options to \pkgname{hyperref} but I've tested only a few
% of those.
%    \begin{macrocode}
\newcommand*{\hyxmp@embed@packet@pdfmark}{%
  \pdfmark{%
    pdfmark=/OBJ,
    Raw={/_objdef \string{hyxmp@Metadata\string} /type /stream}%
  }%
  \pdfmark{%
    pdfmark=/PUT,
    Raw={\string{hyxmp@Metadata\string}%
      &lt;&lt;
        /Type /Metadata
        /Subtype /XML
      &gt;&gt;
    }%
  }%
  \pdfmark{%
    pdfmark=/PUT,
    Raw={\string{hyxmp@Metadata\string} (\hyxmp@xml)}%
  }%
  \pdfmark{%
    pdfmark=/CLOSE,
    Raw={\string{hyxmp@Metadata\string}}%
  }%
%    \end{macrocode}
% Adobe's |pdfmark| reference~\cite{Adobe2005:pdfmark} indicates that a
% metadata stream can be added to the document catalog by specifying the
% \pdfterm{Metadata} |pdfmark| instead of the \pdfterm{PUT} |pdfmark|.
% I see no advantage to this alternative mechanism and, furthermore, it
% works only with Adobe Acrobat Distiller and only with versions~6.0
% onwards.  Consequently, \pkgname{hyperxmp} uses the traditional
% \pdfterm{PUT} mechanism to point the document catalog to our metadata
% stream.
%    \begin{macrocode}
  \pdfmark{%
    pdfmark=/PUT,
    Raw={\string{Catalog\string}%
      &lt;&lt;
        /Metadata \string{hyxmp@Metadata\string}%
      &gt;&gt;
    }%
  }%
}
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Embedding using \texttt{dvipdfm}}
%
% \begin{macro}{\hyxmp@embed@packet@dvipdfm}
% Embed the \acro{XMP} packet using a |dvipdfm|-specific |\special| command.
% Note that |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
    &lt;&lt;
      /Type /Metadata
      /Subtype /XML
      /Length \the\@tempcnta
    &gt;&gt;
    stream^^J\hyxmp@xml endstream%
  }%
  \special{pdf: docview
    &lt;&lt;
      /Metadata @hyxmp@Metadata
    &gt;&gt;
  }%
}
%    \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}
%
% \Finale
\endinput
</pre></body></html>