% \iffalse meta-comment % % Copyright (C) 2006 by Scott Pakin % ------------------------------------------------------- % % 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 %<*driver> \ProvidesFile{hyperxmp.dtx} % %\NeedsTeXFormat{LaTeX2e}[1999/12/01] %\ProvidesPackage{hyperxmp} %<*package> [2006/05/21 v1.1 Store hyperref metadata in XMP format] % % %<*driver> \documentclass{ltxdoc} \usepackage{tocbibind} \usepackage{hyperxmp} \usepackage[bookmarksopen]{hyperref} \EnableCrossrefs \CodelineIndex \RecordChanges \begin{document} \DocInput{hyperxmp.dtx} \PrintChanges \PrintIndex \end{document} % % \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 \& % 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} % % \GetFileInfo{hyperxmp.dtx} % % \DoNotIndex{\#,\&,\<,\>,\\,\_,\|,\ } % \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} % % % 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 |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} % % % % % energy quanta, Hertz effect, % quantum physics % pdfeTeX-1.10b % % % application/pdf % % % On a heuristic viewpoint % concerning the production and transformation of % light % % % % % photoelectric effect % % % % % Copyright (C) 1905, % Albert Einstein % % % % % Albert Einstein % % % % % energy quanta % Hertz effect % quantum physics % % % % % 2006-04-19 % % % % % uuid:c4188820-aef2-0a82-626ce4182b62 % uuid:9b62b67f-d754-626c-4c959595fd75 % % % % % \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 |<|, |>|, and |&| % (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 || 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 \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 ``|&|'', ``|<|'', % and ``|>|'' 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~``|&|'' % and map |\hyxmp@other@amp| to its \acro{XML} entity, |&|. % \begin{macrocode} \bgroup \catcode`\&=11 \gdef\hyxmp@xml@amp{&} \global\let\hyxmp@other@amp=& \gdef\hyxmp@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~``|<|'' % and map |\hyxmp@other@lt| to its \acro{XML} entity, |<|. % \begin{macrocode} \catcode`\<=11 \gdef\hyxmp@xml@lt{<} \global\let\hyxmp@other@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~``|>|'' % and map |\hyxmp@other@gt| to its \acro{XML} entity, |>|. % \begin{macrocode} \catcode`\>=11 \gdef\hyxmp@xml@gt{>} \global\let\hyxmp@other@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 ``|<|'', ``|>|'', and ``|&|'' 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~``|<|'' % replaced with~|<|, all occurrences of~``|>|'' replaced with~|>|, % and all occurrences of~``|&|'' replaced with~|&|. % % 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 | |. % \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 ``|<|'' with |<|. % \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 ``|>|'' with |>|. % \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 ``|&|'' with |&|. % \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 |&#|\meta{ddd}|;|. For example, |\100|, % the octal code for ``|@|'', is represented in \acro{XML} as~|@|. % \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} | | % (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<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<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<10 \xdef\hyxmp@today{\hyxmp@today-0\the\month}% \else \xdef\hyxmp@today{\hyxmp@today-\the\month}% \fi \ifnum\day<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{% ______^^J% }% \ifx\@pdfkeywords\@empty \else \hyxmp@xmlify{\@pdfkeywords}% \hyxmp@add@to@xml{% ___________\hyxmp@xmlified^^J% }% \fi \ifx\@pdfproducer\@empty \else \hyxmp@xmlify{\@pdfproducer}% \hyxmp@add@to@xml{% ___________\hyxmp@xmlified^^J% }% \fi \hyxmp@add@to@xml{% ______^^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{% _________^^J% ____________^^J% _______________\hyxmp@xmlified^^J% ____________^^J% _________^^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{% _________^^J% ____________^^J% }% \bgroup \hyxmp@commas@to@list\hyxmp@list{#3}% \def\@elt##1{% \hyxmp@xmlify{##1}% \hyxmp@add@to@xml{% _______________\hyxmp@xmlified^^J% }% }% \hyxmp@list \egroup \hyxmp@add@to@xml{% ____________^^J% _________^^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{% ______^^J% _________application/pdf^^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{% ______^^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{% ______^^J% _________True^^J% _________\hyxmp@xmlified^^J% ______^^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{% ______^^J% _________\hyxmp@DocumentID^^J% _________\hyxmp@InstanceID^^J% ______^^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{% ^^J% ^^J% ___^^J% }% \hyxmp@pdf@schema \hyxmp@xapRights@schema \hyxmp@dc@schema \hyxmp@mm@schema \hyxmp@add@to@xml{% ___^^J% ^^J% \hyxmp@padding ^^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}% << /Type /Metadata /Subtype /XML >> }% }% \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}% << /Metadata \string{hyxmp@Metadata\string}% >> }% }% } % \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 << /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} % % \Finale \endinput