% \iffalse meta-comment
%
%% File: l3pdfmeta.dtx
%
% Copyright (C) 2018-2025 The LaTeX Project
%
% It may be distributed and/or modified under the conditions of the
% LaTeX Project Public License (LPPL), either version 1.3c of this
% license or (at your option) any later version.  The latest version
% of this license is in the file
%
%    http://www.latex-project.org/lppl.txt
%
% This file is part of the "LaTeX PDF management bundle" (The Work in LPPL)
% and all files in that bundle must be distributed together.
%
% -----------------------------------------------------------------------
%
% The development version of the bundle can be found at
%
%    https://github.com/latex3/pdfresources
%
% for those people who are interested.
%
%<*driver>
\DocumentMetadata{tagging=on,pdfstandard=ua-2}
\documentclass[full]{l3doc}
\usepackage{latex-lab-testphase-l3doc} %
% see issue https://github.com/latex3/tagging-project/issues/925
\EditInstance{blockenv}{displayblock}{inner-instance =}
\usepackage{array,booktabs}
\hypersetup{pdfauthor=The LaTeX Project,pdftitle=l3pdfmeta (LaTeX PDF management bundle)}

\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \title{^^A
%   The \pkg{l3pdfmeta} module\\ PDF standards  ^^A
%   \\
%   \LaTeX{} PDF management bundle
% }
%
% \author{^^A
%  The \LaTeX{} Project\thanks
%    {^^A
%      E-mail:
%        \href{mailto:latex-team@latex-project.org}
%          {latex-team@latex-project.org}^^A
%    }^^A
% }
%
% \date{Version 0.96x, released 2025-11-12}
%
% \maketitle
% \begin{documentation}
%
% \section{\pkg{l3pdfmeta} documentation}
% This module sets up some tools and commands needed
% for PDF standards in general.
% The goal is to collect the requirements and to provide code to check and fulfill them.
%
%
% \subsection{Verifying requirements of PDF standards}
%
% Standards like pdf/A set requirements on a PDF: Some things have be in the PDF,
% e.g. the catalog has to contain a /Lang entry and an colorprofile and
% an /OutputIntent, some other things are forbidden or restricted, e.g.
% the action dictionary of an annotation should not contain Javascript.
%
% The \pkg{l3pdfmeta} module collects a number of relevant requirements,
% tries to enforce the ones which can be enforced and offers some tools
% for package authors to test if an action is allowed in the standard or not.
%
% This is work in progress and more tests will be added. But it should be noted
% that it will probably never be possible to prevent all forbidden actions
% or enforce all required ones or even to simply check all of them.
% The commands here don't replace a check with an external validator.
%
% Verifying against a PDF-standard involves two different task:
%
% \begin{itemize}
%   \item Check if you are allowed to ignore the requirement.
%   \item Decide which action to take if the answer to the first question is NO.
% \end{itemize}
%
% The following conditionals address the first task. Because of the second task
% a return value |FALSE| means that the standard requires you to do some
% special action. |TRUE| means that you can ignore this
% requirement.\footnote{One could also make the logic the
% other way round---there are arguments for both---but I had to decide.}
%
% In most cases it only matters if a requirement is in the standard,
% for example |Catalog_no_OCProperties| means \enquote{don't use |/OCProperties|
% in the catalog}. For a small number of requirements it is also needed to
% test a user value against a standard value. For example, |named_actions|
% restricts the allowed named actions in an annotation of subtype |/Named|,
% in this case it is needed to check not only if the requirement is
% in the standard but also if the user value is in the allowed list.
%
% \begin{function}[EXP,pTF]{\pdfmeta_standard_verify:n}
% \begin{syntax}
% \cs{pdfmeta_standard_verify:n}\Arg{requirement}
% \end{syntax}
%
% This checks if \meta{requirement} is listed in the standard.
% |FALSE| as result means that the requirement is in the standard and
% that probably some special action is required---which one depends
% on the requirement, see the descriptions below.
% |TRUE| means that the requirement is not there and so no special
% action is needed.
% This check can be used for simple requirements where neither
% a user nor a standard value is of importance.
% \end{function}
%
% \begin{function}[TF]{\pdfmeta_standard_verify:nn}
% \begin{syntax}
% \cs{pdfmeta_standard_verify:nn}\Arg{requirement}\Arg{value}
% \end{syntax}
%
% This checks if \meta{requirement} is listed in the standard,
% if yes it tries to find a predefined test handler for
% the requirement and passes \meta{value} and the value recorded
% in the standard to it. The handler returns |FALSE| if some special
% action is needed (e.g. if \meta{value} violates the rule)
% and |TRUE| if no special action is needed. If no handler exists
% this commands works like \cs{pdfmeta_standard_verify:n}.
% \end{function}
%
% In some cases one needs to query the value in the standard,
% e.g. to correct a wrong minimal PDF version you need to know
% which version is required by |min_pdf_version|.
%  For this two commands to access the value are provided:
%
% \begin{function}[EXP]{\pdfmeta_standard_item:n}
% \begin{syntax}
% \cs{pdfmeta_standard_item:n}\Arg{requirement}
% \end{syntax}
% This retrieves the value of \meta{requirement} and leaves it in the input.
% If the requirement isn't in the standard the result is empty,
% that means that requirements not in the standard and
% requirement without values can not be distinguished here.
% \end{function}
%
%
% \begin{function}{\pdfmeta_standard_get:nN}
% \begin{syntax}
% \cs{pdfmeta_standard_get:nN}\Arg{requirement} \meta{tl var}
% \end{syntax}
% This retrieves the value of \meta{requirement} and stores
% it in the \meta{token list variable}.
% If the \meta{requirement} is not found the special
% value |\q_no_value| is used.
% The  \meta{token list variable} is assigned locally.
% \end{function}
%
%
% The following describe the requirements which can be currently tested.
% Requirements with a value should use \cs{pdfmeta_standard_verify:nn}
% or \cs{pdfmeta_standard_verify:nnN} to test a local value against the standard.
% The rule numbers refer to \url{https://docs.verapdf.org/validation/pdfa-part1/}
%
% \subsubsection{Simple tests without handler}
%
% \begin{description}
%
% \item[|outputintent_A|] requires to embed a color profile and
%  reference it in a /Outputintent and that all output intents reference
%  the same colorprofile. The value stores the subtype.
%  {\em This requirement is detected and fulfilled by \pkg{l3pdfmeta} if the
%   provided interface in \cs{DocumentMetadata} or \verb+\SetKeys[document/metadata]+
%   is used, see below}.
%
% \item[|annot_flags|] in annotations the |Print| flag should be true,
%  |Hidden|, |Invisible|, |NoView| should be false.
%  {\em This requirement is detected  and set by \pkg{l3pdfmeta} for annotations
%  created with the \pkg{l3pdfannot}.
%  A new check is only needed if the flags are changed
%  or if links are created by other means.}
%
% \item[|no_encryption|] don't encrypt
% \item[|no_external_content|] no |/F|, |/FFilter|, or |/FDecodeParms|
%  in stream dictionaries
% \item[|no_embed_content|]    no |/EF| key in filespec, no |/Type/EmbeddedFiles|.
%  \emph{This will be checked in future by \pkg{l3pdffiles}
%  for the files it embeds.}
%  The restriction is set only for PDF/A-1 versions.
%  PDF/A-2 and PDF/A-3 lifted this restriction: PDF/A-2 allows
%  to embed other PDF documents conforming to either PDF/A-1 or PDF/A-2,
%  and PDF/A-3 and PDF/A-4F allows any embedded files.
%
% \item[|only_pdfa_embed_content|]
%  This is set for PDF/A-2a, PDF/A-2b, PDF/A-2u and PDF/A-4.
%  I don't see a way to test the
%  PDF/A-2 requirement so currently it will simply allow everything. Perhaps
%  a test for at least the PDF-format will be added in future.
%
% \item[|Catalog_no_OCProperties|] don't add |/OCProperties| to the catalog
% {\em l3pdfmeta removes this entry at the end of the document}
% \item[|Catalog_OCProperties_no_AS|]
% do not use |/AS| optional content configuration dictionary.
% \item[|Catalog_EmbeddedFiles|] ensure that an |EmbeddedFiles| name tree is
% in the catalog. This is required for PDF/A-4f.
% \item[|annot_widget_no_AA|] (rule 6.6.2-1)
%  no AA dictionary in widget annotation,
%  this will e.g. be checked by the new hyperref driver.
% \item[|annot_widget_no_A_AA|] (rule 6.9-2)  no A and AA dictionary in widget.
% \item[|form_no_AA|] (6.9-3)  no /AA dictionary in form field
% \item[|unicode|] that is set in the U-standards, A-2u and A-3u and means that
% every text should be in unicode. This is not something that can be enforced or
% tested from TeX, but in a current LaTeX normally ToUnicode are set for all fonts.
% \item[|tagged|] that is set in A-2a and A-3a and means that the pdf must be
% tagged. This is currently neither tested not enforced somewhere.
% \item[|no_CharSet|] CharSet is deprecated is pdf 2.0 and should not
% be used in A-4. l3pdfmeta will therefore suppress it for the
% engines pdftex and luatex (the other engines have no suitable option)
% \item[|omit_CID|] This avoids with PDF/A-2 and newer a failure
% because of with missing CID identifications
% (e.g. from rule ISO 19005-2:2011, Clause: 6.2.11.4.2)
% It has only with luatex an effect.
% \item[|Trailer_no_Info|] The \texttt{Info} dictionary
% has been deprecated since quite some time. Metadata should be set with
% XMP-data instead. In PDF A-4 now the \texttt{Info} dictionary
% shall not be present in the trailer dictionary at all
% (unless there exists a PieceInfo entry in the Catalog). And if it is present
% it should only contain the \texttt{/ModDate} entry. In
% texlive 2023 the engines pdftex and luatex have primitives
% to suppress the dictionary
% and l3pdfmeta will make use of it.
% \end{description}
%
% \subsubsection{Tests with values and special handlers}
%
% \begin{description}
%
% \item[|min_pdf_version|]  stores the minimal PDF version needed for
%  a standard.
%  It should be checked against the current PDF version (\cs{pdf_version:}).
%  A failure means that the version should be changed.
%  Currently there is only one hard requirement which leads to a failure in
%  a validator like verapdf: The A-4 standard should use PDF 2.0.
%  As PDF A-1 is based on PDF 1.4 and PDF A-2 and A-3 are based
%  on PDF 1.7 \pkg{l3pdfmeta} also sets these versions also as requirements.
%  UA-2 requires PDF 2.0.
%  These requirements are checked by \pkg{l3pdfmeta} when the standard is set with
%  \cs{DocumentMetadata} or \verb+\SetKeys[document/metadata]+
%  and the version is if needed changed. At the begin of the document another
%  check is done and error is issued if the version doesn't fit as this means that
%  the document has used conflicting standard and version setting.
%
%
% \item[|max_pdf_version|]  stores the maximal PDF version.
%  It should be checked against the current PDF version (\cs{pdf_version:}).
%  A failure means that the version should be changed.
%  The check is currently relevant only for the A-1 to A-3 standards:
%  PDF 2.0 leads to a failure in a validator like verapdf so the maximal
%  version should be PDF 1.7.
%  These requirements are checked by \pkg{l3pdfmeta} when the standard is set with
%  \cs{DocumentMetadata} or \verb+\SetKeys[document/metadata]+
%  and the version is if needed changed. At the begin of the document another
%  check is done and error is issued if the version doesn't fit as this means that
%  the document has used conflicting standard and version setting.
%
% \item[|named_actions|]    this requirement restricts the list of
% allowed named actions to |NextPage|, |PrevPage|, |FirstPage|, |LastPage|.
% The check should supply the named action without slash
% (e.g. |View| (failure) or |NextPage| (pass)).
%
% \item[|annot_action_A|] (rule 6.6.1-1) this requirement restricts
%  the allowed subtypes of the
% |/A| dictionary of an action. The check should supply the user
%  subtype without slash e.g. as |GoTo| (pass) or |Movie| (failure).
% \end{description}
%
% \subsection{Colorprofiles and OutputIntent}
%
% The pdf/A standards require that a color profile is embedded and
% referenced in the catalog in the |/OutputIntent| array.
%
% The problem is that the pdf/A standards also require, that if the PDF has more then
% one entry in the |/OutputIntent| array (which is allowed), their |/DestOutputProfile|
% should all reference the same color profile\footnote{see rule 6.2.2-2 at
% \url{https://docs.verapdf.org/validation/pdfa-part1/}}.
%
% Enforcing this fully is impossible if entries are added manually by users or
% packages with |\pdfmanagement_add:nnn {Catalog}{OutputIntents}{|\meta{object reference}|}|
% as it is difficult to inspect and remove entries from the |/OutputIntent| array.
%
% So we provide a dedicated interface to avoid the need of manual
% user settings and allow the code to handle the requirements of the standard.
% The interface doesn't handle yet all finer points for PDF/X standards, e.g.
% named profiles, it is meant as a starting point to get at least PDF/A validation
% here.
%
% \begin{NOTE}{UF}
% The interface has to handle the following points:
% \begin{itemize}
% \item  We have to assume that some documents wants to add more
%  than one OutputIntent with varying subtypes.
% \item While currently only |/GTS_PDFA1| and |/GTS_PDFX| seem to
% be relevant, we have to assume that the list of subtypes is open.
% \item But we can imho assume that every subtype is there at most once.
% \item The referenced color profile can be used also other means, e.g. an /ICCBased
% color space. We must avoid that it is embedded twice in this case.
% This will need coordination with l3color. It should probably provide the
% code to embed the profile.
% \item While we can predeclare some standard icc-profiles, an interface to
% setup more is needed. This is currently not handled, as it needs
% coordination with a setup in l3color too.
% \item The implementation doesn't really handle yet all finer points for pdf/X
% see \url{tn0002_color_in_pdfa-1_2008-03-141.pdf}
% \end{itemize}
% \end{NOTE}
% The interface looks like this
% \begin{verbatim}
%  \DocumentMetadata
%    {
%      %other options for example pdfstandard
%       colorprofiles=
%        {
%          A = sRGB.icc, %or a or longer GTS_PDFA1 = sRGB.icc
%          X = FOGRA39L_coated.icc, % or x or longer GTS_PDFX
%          ISO_PDFE1 = whatever.icc
%        }
%
%    }
% \end{verbatim}
% or
% \begin{verbatim}
%  \RequirePackage{pdfmanagement}
%  \Setkeys[document/metadata]
%    {
%      %other options for example pdfstandard
%       colorprofiles=
%        {
%          A = sRGB.icc, %or a or longer GTS_PDFA1 = sRGB.icc
%          X = FOGRA39L_coated.icc, % or x or longer GTS_PDFX
%          ISO_PDFE1 = whatever.icc
%        }
%
%    }
% \end{verbatim}
%
% |sRGB.icc| and |FOGRA39L_coated.icc| (from the \pkg{colorprofiles} package
% are predefined and will work directly\footnote{The \texttt{dvips} route
% will require that \texttt{ps2pdf} is called with \texttt{-dNOSAFER},
% and that the color profiles are in the current folder as \texttt{ps2pdf} doesn't
% use \texttt{kpathsea} to find them.}. |whatever.icc| will need special setup in
% the document preamble to declare the values for the
% |OutputIntent| dictionary, but the interface hasn't be added yet. This will be
% decided later.
%
%
% If an A-standard is detected or set which requires
% that all |/DestOutputProfile| reference the same
% color profile, the setting is changed to the equivalent of
%
% \begin{verbatim}
%  \SetKeys[document/metadata]
%    {
%      %other options
%       pdfstandard=A-2b,
%       colorprofiles=
%        {
%          A = sRGB.icc, %or longer GTS_PDFA1 = sRGB.icc
%          X = sRGB.icc,
%          ISO_PDFE1 = sRGB.icc
%        }
%
%    }
% \end{verbatim}
%
% The pdf/A standards will use |A=sRGB.icc| by default, so this doesn't
% need to be declared explicitly.
%
% \subsection{Regression tests}
% When doing regression tests one has to set various metadata to fix values.

% \begin{function}{\pdfmeta_set_regression_data:}
% \begin{syntax}
% \cs{pdfmeta_set_regression_data:}
% \end{syntax}
% This sets various metadata to values needed by the \LaTeX{}
% regression tests.
% It also sets the seed for random functions.
% If a current l3backend is used and \cs{c_sys_timestamp_str} is available,
% the command does not set dates, but
% assumes that the environment variable \verb+SOURCE_DATE_EPOCH+ is used.
% \end{function}
%
%  \section{XMP-metadata}
% XMP-metadata are data in XML format embedded in a stream
% inside the PDF and referenced from the |/Catalog|.
% Such a XMP-metadata stream contains various document related data,
% is required by various PDF standards and can replace
% or extend the data in the |/Info| dictionary.
% In PDF 2.0 the /Info dictionary is actually deprecated
% and only XMP-metadata should be used for the metadata of the PDF.
%
% The content of a XMP-metadata stream is not a fix set of data.
% Typically fields like the title, the author, the language and keywords will
% be there. But standards like e.g. ZUGferd (a standard for electronic bills)
% can require to add more fields, and it is also possible
% to define and add purely local data.
%
% In some workflows (e.g. if dvips + ghostscript is used)
% a XMP-metadata stream with some standard content is added automatically by
% the backend, but normally it must be created with code.
%
% For this task the packages \pkg{hyperxmp}, \pkg{xmpincl} or \pkg{pdfx}
% (which uses \pkg{xmpincl})
% can be used, but all these packages are not compatible with the
% pdfmanagement\footnote{\pkg{hyperxmp} was partly compatible as the pdfmanagement
% contained some patches for it, but these patches have now been removed.}.
% The following code is meant as replacement for these packages.
%
% \pkg{hyperxmp} uses |\hypersetup| as user interface to enter the XMP-metadata.
% This syntax is also supported by the new code\footnote{with a number of changes which
% are discussed in more details below}, so if \pkg{hyperref}
% has been loaded, e.g. |pdftitle=xxx| can be used to set the title.
% But XMP-metadata shouldn't require to use \pkg{hyperref} and in a future
% version an interface without \pkg{hyperref} will be added.
%
% There is currently no full user interface command to extend the XMP-metadata
% with for example the code needed for ZUGferd,
% they will be added in a second step.
%
% \subsection{Debug option}
%
% The resulting XMP-packet can be written to an external file by activating a debug
% option
%
% \begin{verbatim}
% \DocumentMetadata{debug={xmp-export}}
% %or
% \DocumentMetadata{debug={xmp-export=true}}
% %or
% \DocumentMetadata{debug={xmp-export=filename}}
% %or
% \SetKeys[document/metadata]{debug={xmp-export}}
% \end{verbatim}
%
% By default the data are written to \verb+\jobname.xmpi+, if a \texttt{filename} is
% given, then \verb+filename.xmpi+ is used instead. \verb+xmp-export=false+ deactivates
% the export.
%
% \subsection{Encoding and escaping}
%
% XMP-metadata are stored as UTF-8 in the PDF. This mean if you open a PDF in an editor
% a content like \enquote{grüße} will be shown probably as \enquote{grÃ¼ÃŸe}.
% As XMP-metadata are in XML format special chars like |<|, |>|, and |&| and
% \texttt{\textquotestraightdblbase} must be escaped.
%
% \pkg{hyperxmp} hooks into \pkg{hyperref} and passes all
% input through |\pdfstringdef|. This means a word like
% \enquote{hallo} is first converted by |\pdfstringdef| into\\
% |\376\377\000h\000a\000l\000l\000o| and then back to UTF-8 by
% \pkg{hyperxmp} and in the course of this action
% the XML-escapings are applied.
% \pkg{pdfx} uses |\pdfstringdef| together with
% a special fontencoding (similar to the PU-encoding of \pkg{hyperref})
% for a similar aim.
% The code here is based on |\text_purify:n| followed by a few replacements for the
% escaping.
%
% User data should normally be declared in the preamble (or even in the
% |\DocumentMetadata| command), and consist of rather simple text; |&| can be entered
% as |\&| (but directly |&| will normally work too),
% babel shorthands should not be used. Some data are interpreted as comma lists,
% in this cases commas which are part of the text should be protected by braces.
% In some cases a text in brackets like |[en]| is interpreted as language tag,
% if they are part of a text they should be protected by braces too.
% XMP-metadata are stored uncompressed in the PDF so if you are unsure
% if a value has
% been passed correctly, open the PDF in an editor, copy the whole block and
% pass it to a validator, e.g. \url{https://www.w3.org/RDF/Validator/}.
%
% \subsection{User interfaces and differences to \pkg{hyperxmp} }
%
% \subsubsection{PDF standards}
%
% The \pkg{hyperxmp}/\pkg{hyperref} keys |pdfapart|, |pdfaconformance|, |pdfuapart|,
% |pdfxstandard| and |pdfa| are ignored by this code. Standards must be set with the
% |pdfstandard| key of |\DocumentMetadata| or |\SetKeys[document/metadata]|.
% This key can be used more than once,
% e.g. \\
% |pdfstandard=A-2b,pdfstandard=X-4,pdfstandard=UA-1|.
% \\ Note that using these
% keys doesn't mean that the document actually follows the standard. \LaTeX{}
% can neither ensure nor check all requirements of a standard, and not everything
% it can do theoretically has already been implemented.
% When setting an |A| standard, the code will e.g. insert a color profile and
% warn if the PDF version doesn't fit, but |X| and |UA| currently only
% adds the relevant declarations to the XMP-metadata.
% It is up to the author to ensure and validate
% that the document actually follows the standard.
%
% \subsubsection{Declarations}
% PDF knows beside standards also a more generic method to declare conformance
% to some specification by adding a declaration,
% see \url{https://pdfa.org/wp-content/uploads/2019/09/PDF-Declarations.pdf}).
% Such declarations can be added as a simple url which identify the specification or
% with additional details regarding date and credentials. An example would be
%
% \begin{verbatim}
% \RequirePackage{pdfmanagement} %or \DocumentMetadata{...}
% \documentclass{article}
% \ExplSyntaxOn
% \pdfmeta_xmp_add_declaration:e {https://pdfa.org/declarations\c_hash_str iso32005}
% \pdfmeta_xmp_add_declaration:ennnn
%  {https://pdfa.org/declarations\c_hash_str wcag21A}{}{2023-11-20}{}{}
% \pdfmeta_xmp_add_declaration:nnnnn
%   {https://github.com/TikZlings/no-duck-harmed}
%   {Ulrike~Fischer}{2023-11-20}{Bär}{https://github.com/u-fischer/bearwear}
% \pdfmeta_xmp_add_declaration:nnnnn
%  {https://github.com/TikZlings/no-duck-harmed}
%  {Ulrike~Fischer}{2023-11-20}{Paulo}{https://github.com/cereda/sillypage}
% \ExplSyntaxOff
% \begin{document}
%  text
% \end{document}
%
% \end{verbatim}
%
% \subsubsection{Dates}
% \begin{itemize}
% \item
% The dates |xmp:CreateDate|, |xmp:ModifyDate|, |xmp:MetadataDate|
% are normally set automatically to the current date/time when the compilation
% started. If they should be changed
% (e.g. for regression tests to produce reproducible documents) they can
% be set with |\hypersetup| with the keys
% |pdfcreationdate|, |pdfmoddate| and |pdfmetadate|.
%
% \begin{verbatim}
% \hypersetup{pdfcreationdate=D:20010101205959-00'00'}
% \end{verbatim}
%
% The format should be a full date/time in PDF format, so one of these (naturally
% the numbers can change):
% \begin{verbatim}
%  D:20010101205959-00'00'
%  D:20010101205959+00'00'
%  D:20010101205959Z
% \end{verbatim}
%
% \item The date |dc:date| is an \enquote{author date} and so
% should normally be set to the same date as
% given by |\date|. This can be done  with the key |pdfdate|\footnote{Extracting
% the value automatically from \texttt{\textbackslash date} is not really possible
% as authors often put formatting or additional info in this command.}.
% The value should be a date in ISO 8601 format:
%
% \begin{verbatim}
% 2022             %year
% 2022-09-04       %year-month-day
% 2022-09-04T19:20 %year-month-day hour:minutes
% 2022-09-04T19:20:30 % year-month-day hour:minutes:second
% 2022-09-04T19:20:30.45 % year-month-day hour:minutes:second with fraction
% 2022-09-04T19:20+01:00 % with time zone designator
% 2022-09-04T19:20-02:00 % time zone designator
% 2022-09-04T19:20Z      % time zone designator
% \end{verbatim}
%
% It is also possible to give the date as a full date in PDF format as described
% above. If not set the current date/time is used.
%\end{itemize}
%
% \subsection{Language}
% The code assumes that a default language is always declared
% (as the pdfmanagement gives the |/Lang| entry in the catalog a default value)
% This language can be changed with the key |lang| of |\DocumentMetadata| and |\SetKeys[document/metadata]|.
% This is the preferred method. \pkg{babel} will look for this value and
% adjust its language settings.
% The \pkg{hyperref} key |pdflang| is also honored. Its value should be a
% simple language tag like |de| or |de-DE|.
%
% The main language is also used in a number of attributes in the XMP data,
% if wanted a different language can be set here with the
% \pkg{hyperref}/\pkg{hyperxmp} key |pdfmetalang|.
%
% A number of entries can be given a language tag. Such a language is
% given by using an \enquote{optional argument} before the text:
%
% \begin{verbatim}
% \hypersetup{pdftitle={[en]english,[de]deutsch}}
% \hypersetup{pdfsubtitle={[en]subtitle in english}}
% \end{verbatim}
%
% \subsection{Rights}
% The keys |pdfcopyright| and |pdflicenseurl| work similar as in \pkg{hyperxmp}.
% But differently to \pkg{hyperxmp} the code doesn't set the |xmpRights:Marked|
% property, as I have some doubts that one deduce its value simply
% by checking if the other keys have been used; if needed it can be added by
% using one of these settings (true means with copyright, false means public domain).
% \begin{verbatim}
% \AddToDocumentProperties[document]{copyright}{true}
% \AddToDocumentProperties[document]{copyright}{false}
% \end{verbatim}
%
% \subsection{PDF related data}
% The PDF producer is for all engines by default built from the engine
% name and the engine version and doesn't use the banners as with \pkg{hyperxmp}
% and \pkg{pdfx}, it can be set manually with the |pdfproducer| key.
%
% The key |pdftrapped| is ignored. |Trapped| is deprecated in PDF 2.0.
%
% \subsection{Document data}
%
% The authors should be given with the |pdfauthor| key, separated by commas. If an
% author contains a comma, protect/hide it by a brace.

% \subsection{User commands}
%  The XMP-meta data are added automatically. This can be suppressed with the
%  |document/metadata| key |xmp|.
%
% \begin{function}{\pdfmeta_xmp_add:n}
% \begin{syntax}
% \cs{pdfmeta_xmp_add:n}\Arg{XML}
% \end{syntax}
% With this command additional XML code can be added to the Metadata.
% The content is added unchanged, and not sanitized.
% \end{function}
% \begin{function}{\pdfmeta_xmp_xmlns_new:nn}
% \begin{syntax}
% \cs{pdfmeta_xmp_xmlns_new:nn}\Arg{prefix}\Arg{uri}
% \end{syntax}
% With this command a xmlns name space can be added. The \meta{uri}
% argument is expanded, a hash can be input with |\c_hash_str|.
% \end{function}
%
% With the two following commands PDF declarations can be added to the XMP metadata
% (see \url{https://pdfa.org/wp-content/uploads/2019/09/PDF-Declarations.pdf}).
% \begin{function}{\pdfmeta_xmp_add_declaration:n,\pdfmeta_xmp_add_declaration:e}
% \begin{syntax}
% \cs{pdfmeta_xmp_add_declaration:n}\Arg{uri}
% \end{syntax}
% This add a PDF declaration with the required |conformsTo| property to the XMP metadata.
% \meta{uri} should not be empty and is a URI specifying
% the standard or profile referred to by the PDF
% Declaration. If the uri contains a hash, use \cs{c_hash_str} to escape it
% and use the \texttt{e} variant to expand it.
% \end{function}
%
% \begin{function}{\pdfmeta_xmp_add_declaration:nnnnn,
%  \pdfmeta_xmp_add_declaration:ennnn,
%  \pdfmeta_xmp_add_declaration:eeenn}
% \begin{syntax}
% \cs{pdfmeta_xmp_add_declaration:nnnnn}\Arg{uri}\Arg{By}\Arg{Date}\Arg{Credentials}\Arg{Report}
% \end{syntax}
% This add a PDF declaration to the XMP metadata similar
% to \cs{pdfmeta_xmp_add_declaration:n}.
% With \meta{By}, \meta{Date}, \meta{Credentials}, \meta{Report} the optional
% fields |claimBy| (text), |claimDate| (iso date), |claimCredentials| (text) and
% |claimReport| (uri) of the |claimData| property can be given.
% If  \cs{pdfmeta_xmp_add_declaration:nnnnn} is used twice with the same \meta{uri}
% argument the |claimData| are concatenated. There is no check if the |claimData| are identical.
% \end{function}
%
% The following two commands can be used to extend the schema declarations in
% the XMP metadata. This is for example needed to implement a standard like ZUGferd/Factur X
% for invoices. A schema declaration should be added only once but as this task
% is probably not needed frequently only light guards are there to avoid duplicated entries.
%
% \begin{function}{\pdfmeta_xmp_schema_new:nnn}
% \begin{syntax}
% \cs{pdfmeta_xmp_schema_new:nnn}\Arg{text}\Arg{prefix}\Arg{uri}
% \end{syntax}
% \meta{text} is some string describing the schema, e.g. |PDF/A~Identification~Schema|,
% \meta{prefix} is the unique prefix used by the schema. This prefix
% must be declared first with |\pdfmeta_xmp_xmlns_new:nn|. If a schema with this prefix
% has already been declared, it will currently be ignored with a warning.
% The \meta{uri} is expanded, so a
% hash can for example be given as |\c_hash_str|.
% \end{function}

% \begin{function}{\pdfmeta_xmp_property_new:nnnnn}
% \begin{syntax}
% \cs{pdfmeta_xmp_property_new:nnnnn}\Arg{schema prefix}\Arg{name}\Arg{type}\Arg{category}\Arg{description}
% \end{syntax}
% If the new property already exists in the schema
% (as identified by the combination of \meta{schema prefix}
% and \meta{name} the property is silently ignore.
% \meta{schema prefix} is the prefix declared with the previous command.
% schema, e.g. |PDF/A~Identification~Schema|,
% \meta{name} is a short string that identifies the property, e.g. |xmpMM| or |year|. It must be
% unique in the properties of a schema. \meta{type} is e.g. |URI| or |Integer| or |Text|,
% \meta{category} is e.g. |internal| or |external|, \meta{description} is a free description string.
% \end{function}
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{l3pdfmeta} implementation}
%
%    \begin{macrocode}
%<@@=pdfmeta>
%<*header>
\ProvidesExplPackage{l3pdfmeta}{2025-11-12}{0.96x}
  {PDF-Standards---LaTeX PDF management bundle}
%</header>
%    \end{macrocode}
% Message for unknown standards
%    \begin{macrocode}
%<*package>
\msg_new:nnn  {pdf }{unknown-standard}{The~standard~'#1'~is~unknown~and~has~been~ignored}
%    \end{macrocode}
% Message for not fitting pdf version
%    \begin{macrocode}
\msg_new:nnn  {pdf }{wrong-pdfversion}
  {PDF~version~#1~is~too~#2~for~standard~'#3'.\\
   Check~if~there~are~conflicting~PDF~standard\\
   and~PDF~version~settings!}
%    \end{macrocode}
% Messages for embedded files
%    \begin{macrocode}
\msg_new:nnn  {pdf }{validation-failure}
  {
    PDF~standard~validation~failure.\\
    #1
  }
%    \end{macrocode}
%
% \begin{variable}{\l_@@_tmpa_tl,\l_@@_tmpb_tl,\l_@@_tmpa_str,
%  \g_@@tmpa_str,\l_@@_tmpa_seq,\l_@@_tmpb_seq}
%    \begin{macrocode}
\tl_new:N  \l_@@_tmpa_tl
\tl_new:N  \l_@@_tmpb_tl
\str_new:N \l_@@_tmpa_str
\str_new:N \g_@@_tmpa_str
\seq_new:N \l_@@_tmpa_seq
\seq_new:N \l_@@_tmpb_seq
%    \end{macrocode}
% \end{variable}
% \subsection{Standards (work in progress)}
% \subsubsection{Tools and tests}
% This internal property will contain for now the settings for the document.
% \begin{variable}{\g_@@_standard_prop}
%    \begin{macrocode}
\prop_new:N \g_@@_standard_prop
%    \end{macrocode}
% \end{variable}
% \subsubsection{Functions to check a requirement}
% At first two commands to get the standard value if needed:
% \begin{macro}[EXP]{\pdfmeta_standard_item:n}
%    \begin{macrocode}
\cs_new:Npn \pdfmeta_standard_item:n #1
 {
   \prop_item:Nn \g_@@_standard_prop {#1}
 }
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\pdfmeta_standard_get:nN}
%    \begin{macrocode}
\cs_new_protected:Npn \pdfmeta_standard_get:nN #1 #2
 {
   \prop_get:NnN \g_@@_standard_prop {#1} #2
 }
%    \end{macrocode}
% \end{macro}
% Now two functions to check the requirement. A simple and one value/handler based.
% \begin{macro}[pTF]{\pdfmeta_standard_verify:n}
%  This is a simple test is the requirement is in the prop.
%    \begin{macrocode}
\prg_new_conditional:Npnn \pdfmeta_standard_verify:n #1 {T,F,TF}
  {
     \prop_if_in:NnTF \g_@@_standard_prop {#1}
       {
         \prg_return_false:
       }
       {
         \prg_return_true:
       }
  }
%    \end{macrocode}
% \end{macro}
% \begin{macro}[TF]{\pdfmeta_standard_verify:nn}
% This allows to test against a user value. It calls a test handler if this
% exists and passes the user and the standard value to it. The test
% handler should return true or false.
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \pdfmeta_standard_verify:nn #1 #2  {T,F,TF}
  {
    \prop_if_in:NnTF \g_@@_standard_prop {#1}
      {
        \cs_if_exist:cTF {@@_standard_verify_handler_#1:nn}
          {
            \exp_args:Nnne
            \use:c
              {@@_standard_verify_handler_#1:nn}
              { #2 }
              { \prop_item:Nn \g_@@_standard_prop {#1} }
          }
          {
            \prg_return_false:
          }
      }
      {
        \prg_return_true:
      }
   }
%    \end{macrocode}
% \end{macro}
%
% Now we setup a number of handlers.
%
% The first actually ignores the user values and tests against the
% current pdf version. If this is smaller than the minimum we report a failure.
% |#1| is the user value, |#2| the reference value from the standard.
% \begin{macro}{\@@_standard_verify_handler_min_pdf_version:nn}
%    \begin{macrocode}
%
\cs_new_protected:Npn \@@_standard_verify_handler_min_pdf_version:nn #1 #2
 {
   \pdf_version_compare:NnTF <
     { #2 }
     {\prg_return_false:}
     {\prg_return_true:}
 }
%    \end{macrocode}
% \end{macro}
% The next is the counter part and checks that the version is not to high
% \begin{macro}{\@@_standard_verify_handler_max_pdf_version:nn}
%    \begin{macrocode}
%
\cs_new_protected:Npn \@@_standard_verify_handler_max_pdf_version:nn #1 #2
 {
   \pdf_version_compare:NnTF >
     { #2 }
     {\prg_return_false:}
     {\prg_return_true:}
 }
%    \end{macrocode}
% \end{macro}
% The next checks if the user value is in the list and returns a failure if not.
% \begin{macro}{\@@_standard_verify_handler_named_actions:nn}
%    \begin{macrocode}

\cs_new_protected:Npn \@@_standard_verify_handler_named_actions:nn #1 #2
 {
   \tl_if_in:nnTF { #2 }{ #1 }
     {\prg_return_true:}
     {\prg_return_false:}
 }
%    \end{macrocode}
% \end{macro}
% The next checks if the user value is in the list and returns a failure if not.
% \begin{macro}{\@@_standard_verify_handler_annot_action_A:nn}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_standard_verify_handler_annot_action_A:nn #1 #2
 {
   \tl_if_in:nnTF { #2 }{ #1 }
     {\prg_return_true:}
     {\prg_return_false:}
 }
%    \end{macrocode}
% \end{macro}
% This check is probably not needed, but for completeness
% \begin{macro}{\@@_standard_verify_handler_outputintent_subtype:nn}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_standard_verify_handler_outputintent_subtype:nn #1 #2
 {
   \tl_if_eq:nnTF { #2 }{ #1 }
     {\prg_return_true:}
     {\prg_return_false:}
 }
%    \end{macrocode}
% \end{macro}
% \subsubsection{Enforcing requirements}
%  A number of requirements can sensibly be enforced by us.
%  \paragraph{Annot flags}
% pdf/A require a number of settings here, we store them in a command which
% can be added to the property of the standard:
%    \begin{macrocode}
\cs_new_protected:Npn \@@_verify_pdfa_annot_flags:
  {
    \bitset_set_true:Nn  \l_pdfannot_F_bitset {Print}
    \bitset_set_false:Nn \l_pdfannot_F_bitset {Hidden}
    \bitset_set_false:Nn \l_pdfannot_F_bitset {Invisible}
    \bitset_set_false:Nn \l_pdfannot_F_bitset {NoView}
    \pdfannot_dict_put:nnn {link/URI}{F}{ \bitset_to_arabic:N \l_pdfannot_F_bitset }
    \pdfannot_dict_put:nnn {link/GoTo}{F}{ \bitset_to_arabic:N \l_pdfannot_F_bitset }
    \pdfannot_dict_put:nnn {link/GoToR}{F}{ \bitset_to_arabic:N \l_pdfannot_F_bitset }
    \pdfannot_dict_put:nnn {link/Launch}{F}{ \bitset_to_arabic:N \l_pdfannot_F_bitset }
    \pdfannot_dict_put:nnn {link/Named}{F}{ \bitset_to_arabic:N \l_pdfannot_F_bitset }
  }
%    \end{macrocode}
% At begin document this should be checked:
%    \begin{macrocode}
\hook_gput_code:nnn {begindocument} {pdf}
  {
    \pdfmeta_standard_verify:nF { annot_flags }
     { \@@_verify_pdfa_annot_flags: }
    \pdfmeta_standard_verify:nF { Trailer_no_Info }
     { \__pdf_backend_omit_info:n {1} }
    \pdfmeta_standard_verify:nF { no_CharSet }
     { \__pdf_backend_omit_charset:n {1} }
    \pdfmeta_standard_verify:nF { omit_CID }
     { \__pdf_backend_omit_cidset:n {1} }
    \pdfmeta_standard_verify:nnF { min_pdf_version }
     { \pdf_version: }
     { \msg_warning:nneee {pdf}{wrong-pdfversion} %TODO make error
       {\pdf_version:}{low}
       {
        \pdfmeta_standard_item:n{type}
        -
        \pdfmeta_standard_item:n{level}
       }
     }
    \pdfmeta_standard_verify:nnF { max_pdf_version }
     { \pdf_version: }
     { \msg_warning:nneee {pdf}{wrong-pdfversion}
       {\pdf_version:}{high}
       {
        \pdfmeta_standard_item:n{type}
        -
        \pdfmeta_standard_item:n{level}
       }
     }
  }
%    \end{macrocode}
%
%
%  \subsubsection{pdf/A}
%  We use global properties so that follow up standards can be
%  copied and then adjusted.
%  Some note about requirements for more standard can
%  be found in info/pdfstandard.tex.
%  \begin{variable}{
%   \g_@@_standard_pdf/A-1B_prop ,
%   \g_@@_standard_pdf/A-2A_prop ,
%   \g_@@_standard_pdf/A-2B_prop ,
%   \g_@@_standard_pdf/A-2U_prop ,
%   \g_@@_standard_pdf/A-3A_prop ,
%   \g_@@_standard_pdf/A-3B_prop ,
%   \g_@@_standard_pdf/A-3U_prop ,
%   \g_@@_standard_pdf/A-4_prop ,
%   \g_@@_standard_pdf/A-4F_prop ,
%   }
%     \begin{macrocode}
\prop_new:c { g_@@_standard_pdf/A-1B_prop }
\prop_gset_from_keyval:cn { g_@@_standard_pdf/A-1B_prop }
  {
    ,name             = pdf/A-1B
    ,type             = A
    ,level            = 1
    ,conformance      = B
    ,year             = 2005
    ,min_pdf_version  = 1.4        %minimum
    ,max_pdf_version  = 1.4        %minimum
    ,no_encryption    =
    ,no_external_content =  % no F, FFilter, or FDecodeParms in stream dicts
    ,no_embed_content = % no EF key in filespec, no /Type/EmbeddedFiles
    ,max_string_size  = 65535
    ,max_array_size   = 8191
    ,max_dict_size    = 4095
    ,max_obj_num      = 8388607
    ,max_nest_qQ      = 28
    ,named_actions    = {NextPage, PrevPage, FirstPage, LastPage}
    ,annot_flags      =
    %booleans. Only the existence of the key matter.
    %If the entry is added it means a requirements is there
    %(in most cases "don't use ...")
    %
    %===============
    % Rule 6.1.13-1 CosDocument, isOptionalContentPresent == false
    ,Catalog_no_OCProperties =
    % Rule 6.9-4 The AS key shall not appear in any optional content configuration dictionary.
    % actually only starting with A-2 but doesn't harm here either
    ,Catalog_OCProperties_no_AS=
    %===============
    % Rule 6.6.1-1: PDAction, S == "GoTo" || S == "GoToR" || S == "Thread"
    %               || S == "URI" || S == "Named" || S == "SubmitForm"
    % means: no /S/Launch, /S/Sound, /S/Movie, /S/ResetForm, /S/ImportData,
    %        /S/JavaScript, /S/Hide
      ,annot_action_A        = {GoTo,GoToR,Thread,URI,Named,SubmitForm}
    %===============
    % Rule 6.6.2-1: PDAnnot, Subtype != "Widget" || AA_size == 0
    % means: no AA dictionary
      ,annot_widget_no_AA      =
    %===============
    % Rule 6.9-2: PDAnnot, Subtype != "Widget" || (A_size == 0 && AA_size == 0)
    % (looks like a tightening of the previous rule)
      ,annot_widget_no_A_AA    =
    %===============
    % Rule 6.9-1 PDAcroForm, NeedAppearances == null || NeedAppearances == false
    ,form_no_NeedAppearances =
    %===============
    %Rule 6.9-3 PDFormField, AA_size == 0
    ,form_no_AA              =
    %===============
    % to be continued https://docs.verapdf.org/validation/pdfa-part1/
    % - Outputintent/colorprofiles requirements
    % an outputintent should be loaded and is unique.
    ,outputintent_A         = {GTS_PDFA1}
    % - no Alternates key in image dictionaries
    % - no OPI, Ref, Subtype2 with PS key in xobjects
    % - Interpolate  = false in images
    % - no TR, TR2 in ExtGstate
  }

%A-2b ==============
\prop_new:c { g_@@_standard_pdf/A-2B_prop }
\prop_gset_eq:cc
  { g_@@_standard_pdf/A-2B_prop }
  { g_@@_standard_pdf/A-1B_prop }
\prop_gput:cnn
  { g_@@_standard_pdf/A-2B_prop }{name}{pdf/A-2B}
\prop_gput:cnn
  { g_@@_standard_pdf/A-2B_prop }{year}{2011}
\prop_gput:cnn
  { g_@@_standard_pdf/A-2B_prop }{level}{2}
% embedding files is allowed (with restrictions)
\prop_gremove:cn
  { g_@@_standard_pdf/A-2B_prop }
  { no_embed_content }
\prop_gput:cnn
  { g_@@_standard_pdf/A-2B_prop }
  { only_pdfa_embed_content }
  {}
\prop_gput:cnn
  { g_@@_standard_pdf/A-2B_prop }{max_pdf_version}{1.7}
\prop_gput:cnn
  { g_@@_standard_pdf/A-2B_prop }{omit_CID}{}
% OCG layers are allowed (with restrictions)
\prop_gremove:cn
  { g_@@_standard_pdf/A-2B_prop }
  { Catalog_no_OCProperties }

  %A-2u ==============
\prop_new:c { g_@@_standard_pdf/A-2U_prop }
\prop_gset_eq:cc
  { g_@@_standard_pdf/A-2U_prop }
  { g_@@_standard_pdf/A-2B_prop }
\prop_gput:cnn
  { g_@@_standard_pdf/A-2U_prop }{name}{pdf/A-2U}
\prop_gput:cnn
  { g_@@_standard_pdf/A-2U_prop }{conformance}{U}
\prop_gput:cnn
  { g_@@_standard_pdf/A-2U_prop }{unicode}{}

%A-2a ==============
\prop_new:c { g_@@_standard_pdf/A-2A_prop }
\prop_gset_eq:cc
  { g_@@_standard_pdf/A-2A_prop }
  { g_@@_standard_pdf/A-2B_prop }
\prop_gput:cnn
  { g_@@_standard_pdf/A-2A_prop }{name}{pdf/A-2A}
\prop_gput:cnn
  { g_@@_standard_pdf/A-2A_prop }{conformance}{A}
\prop_gput:cnn
  { g_@@_standard_pdf/A-2A_prop }{tagged}{}


%A-3b ==============
\prop_new:c { g_@@_standard_pdf/A-3B_prop }
\prop_gset_eq:cc
  { g_@@_standard_pdf/A-3B_prop }
  { g_@@_standard_pdf/A-2B_prop }
\prop_gput:cnn
  { g_@@_standard_pdf/A-3B_prop }{name}{pdf/A-3B}
\prop_gput:cnn
  { g_@@_standard_pdf/A-3B_prop }{year}{2012}
\prop_gput:cnn
  { g_@@_standard_pdf/A-3B_prop }{level}{3}
% embedding files is allowed
\prop_gremove:cn
  { g_@@_standard_pdf/A-3B_prop }
  { only_pdfa_embed_content }
%A-3u ==============
\prop_new:c { g_@@_standard_pdf/A-3U_prop }
\prop_gset_eq:cc
  { g_@@_standard_pdf/A-3U_prop }
  { g_@@_standard_pdf/A-3B_prop }
\prop_gput:cnn
  { g_@@_standard_pdf/A-3U_prop }{name}{pdf/A-3U}
\prop_gput:cnn
  { g_@@_standard_pdf/A-3U_prop }{conformance}{U}
\prop_gput:cnn
  { g_@@_standard_pdf/A-3U_prop }{unicode}{}

%A-3a ==============
\prop_new:c { g_@@_standard_pdf/A-3A_prop }
\prop_gset_eq:cc
  { g_@@_standard_pdf/A-3A_prop }
  { g_@@_standard_pdf/A-3B_prop }
\prop_gput:cnn
  { g_@@_standard_pdf/A-3A_prop }{name}{pdf/A-3A}
\prop_gput:cnn
  { g_@@_standard_pdf/A-3A_prop }{conformance}{A}
\prop_gput:cnn
  { g_@@_standard_pdf/A-3A_prop }{tagged}{}

%A-4 ==============
\prop_new:c { g_@@_standard_pdf/A-4_prop }
\prop_gset_eq:cc
  { g_@@_standard_pdf/A-4_prop }
  { g_@@_standard_pdf/A-3U_prop }
\prop_gput:cnn
  { g_@@_standard_pdf/A-4_prop }{name}{pdf/A-4}
\prop_gput:cnn
  { g_@@_standard_pdf/A-4_prop }{level}{4}
\prop_gput:cnn
  { g_@@_standard_pdf/A-4_prop }{min_pdf_version}{2.0}
\prop_gput:cnn
  { g_@@_standard_pdf/A-4_prop }{year}{2020}
\prop_gput:cnn
  { g_@@_standard_pdf/A-4_prop }{no_CharSet}{}
\prop_gput:cnn
  { g_@@_standard_pdf/A-4_prop }{Trailer_no_Info}{}
\prop_gput:cnn
  { g_@@_standard_pdf/A-4_prop }{only_pdfa_embed_content}{}
\prop_gremove:cn
  { g_@@_standard_pdf/A-4_prop }{conformance}
\prop_gremove:cn
  { g_@@_standard_pdf/A-4_prop }{max_pdf_version}
\prop_gremove:cn
  { g_@@_standard_pdf/A-4_prop }{Catalog_OCProperties_no_AS}
%A-4f ==============
\prop_new:c { g_@@_standard_pdf/A-4F_prop }
\prop_gset_eq:cc
  { g_@@_standard_pdf/A-4F_prop }
  { g_@@_standard_pdf/A-4_prop }
\prop_gput:cnn
  { g_@@_standard_pdf/A-4F_prop }{conformance}{F}
% containsEmbeddedFiles == true ISO 19005-4:2020, Clause: 6.9, Test number: 5
\prop_gput:cnn
  { g_@@_standard_pdf/A-4F_prop }{Catalog_EmbeddedFiles}{}
% can contain any file
\prop_gremove:cn
  { g_@@_standard_pdf/A-4F_prop }{only_pdfa_embed_content}
%    \end{macrocode}
% \end{variable}
%
% \subsubsection{Embedded Files}
% Standard 4-AF is needed if we add AF files for
% tagging but it also requires an EmbeddedFiles name tree,
% so we test at the end if the name tree is empty and add a small readme if
% yes
%    \begin{macrocode}
\AddToHook{begindocument/end}
{
  \pdfmeta_standard_verify:nF{Catalog_EmbeddedFiles}
   {
    \tl_gput_right:Nn\g__kernel_pdfmanagement_end_run_code_tl
     {
       \pdfdict_if_empty:nT { g__pdf_Core/Catalog/Names/EmbeddedFiles }
        {
          \group_begin:
          \pdfdict_put:nne {l_pdffile/Filespec} {Desc}{(note~about~PDF/A-4F)}
          \pdfdict_put:nnn { l_pdffile/Filespec }{AFRelationship} { /Unspecified }
          \pdffile_embed_stream:nnN
            {The~document~was~declared~to~be~of~type~PDF/A-4f~but~hasn't~any~attachments.~
             LaTeX~therefore~added~this~dummy~file.}
            {pdf-A4f.txt}
            \l_@@_tmpa_tl
          \exp_args:Nne \__pdf_backend_Names_gpush:nn{EmbeddedFiles}{(pdf-A4f)~\l_@@_tmpa_tl}
          \group_end:
        }
     }
   }
}
%    \end{macrocode}
% Before writing the xml we check if there are embedded files we know of. For A-4 we adjust
% the standard to A-4F is needed.
%    \begin{macrocode}
\AddToHook{pdfmeta/xmp}
 {
    \pdfmeta_standard_verify:nF{no_embed_content}
      {
        \bool_lazy_or:nnT
          { ! \int_if_zero_p:n { \g_pdffile_embed_pdfa_int } }
          { ! \int_if_zero_p:n { \g_pdffile_embed_nonpdfa_int } }
          {
            \prop_get:NnNT\g_@@_standard_prop { name }\l_@@_tmpa_tl
             {
               \msg_warning:nne { pdf } { validation-failure }
                 {
                   Embedded~files~detected.\iow_newline:
                   This~is~not~allowed~in~standard~\l_@@_tmpa_tl
                 }
             }
          }
      }
    \pdfmeta_standard_verify:nF {only_pdfa_embed_content}
      {
        \int_if_zero:nF  { \g_pdffile_embed_nonpdfa_int }
          {
           \prop_get:NnNT\g_@@_standard_prop { name }\l_@@_tmpa_tl
             {
               \str_if_eq:VnTF {\l_@@_tmpa_tl} { pdf/A-4 }
                {
                  \prop_gset_eq:cc
                    { g_@@_standard_prop }
                    { g_@@_standard_pdf/A-4F_prop }
                  \msg_warning:nne { pdf } { validation-failure }
                   {
                     Embedded~non-PDF~files~detected.\iow_newline:
                     Switching~standard~from~PDF/A-4~to~PDF/A-4F
                   }
                }
                {
                  \msg_warning:nne { pdf } { validation-failure }
                   {
                     Embedded~non-PDF~files~detected.\iow_newline:
                     This~is~not~allowed~in~standard~\l_@@_tmpa_tl
                   }
                }
             }
          }
      }
 }
%    \end{macrocode}
%
% \subsubsection{Colorprofiles and Outputintents}
% The following provides a minimum of interface to add a color profile
% and an outputintent need for PDF/A for now. There will be need to extend it later,
% so we try for enough generality.
%
% Adding a profile and an intent is technically easy:
% \begin{enumerate}
% \item Embed the profile as stream with
% \begin{verbatim}
%  \pdf_object_unnamed_write:nn{fstream} {{/N~4}{XXX.icc}}
% \end{verbatim}
% \item Write a |/OutputIntent| dictionary for this
% \begin{verbatim}
% \pdf_object_unnamed_write:ne {dict}
%  {
%    /Type /OutputIntent
%    /S /GTS_PDFA1  % or GTS_PDFX or ISO_PDFE1 or ...
%    /DestOutputProfile \pdf_object_ref_last: % ref the color profile
%    /OutputConditionIdentifier ...
%    ... %more info
%  }
% \end{verbatim}
% \item Reference the dictionary in the catalog:
% \begin{verbatim}
% \pdfmanagement_add:nne {Catalog}{OutputIntents}{\pdf_object_ref_last:}
% \end{verbatim}
% \end{enumerate}
% But we need to do a bit more work, to get the interface right.
% The object for the profile should be named, to allow l3color to reuse it
% if needed. And we need container to store the profiles, to handle the
% standard requirements.
%
% \begin{variable}{\g_@@_outputintents_prop}
% This variable will hold the profiles for the subtypes. We assume
% that every subtype has only only color profile.
%    \begin{macrocode}
\prop_new:N \g_@@_outputintents_prop
%    \end{macrocode}
% \end{variable}
% Some keys to fill the property.
%    \begin{macrocode}
\keys_define:nn { document / metadata }
  {
    colorprofiles .code:n =
     {
       \keys_set:nn { document / metadata / colorprofiles }{#1}
     }
  }
\keys_define:nn { document / metadata / colorprofiles }
 {
   ,A .code:n =
      {
        \tl_if_blank:nF {#1}
          {
            \prop_gput:Nnn \g_@@_outputintents_prop
             { GTS_PDFA1  } {#1}
          }
      }
   ,a .code:n =
      {
        \tl_if_blank:nF {#1}
          {
            \prop_gput:Nnn \g_@@_outputintents_prop
              { GTS_PDFA1  } {#1}
          }
      }
   ,X .code:n =
      {
        \tl_if_blank:nF {#1}
          {
             \prop_gput:Nnn \g_@@_outputintents_prop
              { GTS_PDFX  } {#1}
          }
      }
   ,x .code:n =
      {
        \tl_if_blank:nF {#1}
          {
            \prop_gput:Nnn \g_@@_outputintents_prop
              { GTS_PDFX  } {#1}
          }
      }
   ,unknown .code:n =
     {
       \tl_if_blank:nF {#1}
          {
           \exp_args:NNo
            \prop_gput:Nnn \g_@@_outputintents_prop
              { \l_keys_key_str  } {#1}
          }
     }
 }
%    \end{macrocode}
% At first we setup our two default profiles. This is internal as
% the public interface is still undecided.
%    \begin{macrocode}
\pdfdict_new:n   {l_pdfmeta/outputintent}
\pdfdict_put:nnn {l_pdfmeta/outputintent}
  {Type}{/OutputIntent}
\prop_const_from_keyval:cn { c_@@_colorprofile_sRGB.icc}
  {
    ,OutputConditionIdentifier=IEC~sRGB
    ,Info=IEC~61966-2.1~Default~RGB~colour~space~-~sRGB
    ,RegistryName=http://www.iec.ch
    ,N = 3
  }
\prop_const_from_keyval:cn { c_@@_colorprofile_FOGRA39L_coated.icc}
  {
    ,OutputConditionIdentifier=FOGRA39L~Coated
    ,Info={Offset~printing,~according~to~ISO~12647-2:2004/Amd~1,~OFCOM,~ %
           paper~type~1~or~2~=~coated~art,~115~g/m2,~tone~value~increase~
           curves~A~(CMY)~and~B~(K)}
    ,RegistryName=http://www.fogra.org
    ,N = 4
  }
%    \end{macrocode}
% \begin{macro}{\@@_embed_colorprofile:n,\@@_write_outputintent:nn}
% The commands embed the profile, and write the dictionary and add it to
% the catalog. The first command should perhaps be moved to l3color
% as it needs such profiles too. We used named objects so that we can
% check if the profile is already there. This is not foolproof if paths are
% used.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_embed_colorprofile:n #1%#1 file name
  {
    \pdf_object_if_exist:nF { __color_icc_ #1 }
      {
        \pdf_object_new:n  { __color_icc_ #1 }
        \pdf_object_write:nne { __color_icc_ #1 } { fstream }
         {
           {/N\c_space_tl
             \prop_item:cn{c_@@_colorprofile_#1}{N}
           }
           {#1}
         }
      }
  }

\cs_new_protected:Npn \@@_write_outputintent:nn #1 #2 %#1 file name, #2 subtype
  {
    \group_begin:
     \pdfdict_put:nne {l_pdfmeta/outputintent}{S}{/\str_convert_pdfname:n{#2}}
     \pdfdict_put:nne {l_pdfmeta/outputintent}
       {DestOutputProfile}
       {\pdf_object_ref:n{ __color_icc_ #1 }}
     \clist_map_inline:nn { OutputConditionIdentifier, Info, RegistryName }
       {
         \prop_get:cnNT
          { c_@@_colorprofile_#1}
          { ##1 }
          \l_@@_tmpa_tl
          {
            \pdf_string_from_unicode:nVN {utf8/string}\l_@@_tmpa_tl\l_@@_tmpa_str
            \pdfdict_put:nne
              {l_pdfmeta/outputintent}{##1}{\l_@@_tmpa_str}
          }
       }
     \pdf_object_unnamed_write:ne {dict}{\pdfdict_use:n {l_pdfmeta/outputintent} }
     \pdfmanagement_add:nne {Catalog}{OutputIntents}{\pdf_object_ref_last:}
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
% Now the verifying code.
% If no requirement is set we simply loop over the property
%    \begin{macrocode}

\AddToHook{begindocument/end}
  {
    \pdfmeta_standard_verify:nTF {outputintent_A}
      {
        \prop_map_inline:Nn \g_@@_outputintents_prop
           {
             \prop_if_exist:cTF {c_@@_colorprofile_#2}
               {
                 \@@_embed_colorprofile:n
                  {#2}
                 \@@_write_outputintent:nn
                  {#2}
                  {#1}
               }
               {
                \msg_warning:nnn{pdfmeta}{colorprofile-undefined}{#2}
               }
           }
      }
%    \end{macrocode}
% If an output intent is required for pdf/A we need to ensure, that the key of
% default subtype has a value, as default we take sRGB.icc.
% Then we loop but take always the same profile.
%    \begin{macrocode}
      {
         \exp_args:NNe
         \prop_if_in:NnF
           \g_@@_outputintents_prop
           { \pdfmeta_standard_item:n { outputintent_A } }
           {
             \exp_args:NNe
             \prop_gput:Nnn
               \g_@@_outputintents_prop
               { \pdfmeta_standard_item:n { outputintent_A } }
               { sRGB.icc }
           }
         \exp_args:NNe
         \prop_get:NnN
           \g_@@_outputintents_prop
           { \pdfmeta_standard_item:n { outputintent_A } }
           \l_@@_tmpb_tl
         \prop_if_exist:cTF {c_@@_colorprofile_\l_@@_tmpb_tl}
          {
            \exp_args:NV \@@_embed_colorprofile:n \l_@@_tmpb_tl
            \prop_map_inline:Nn \g_@@_outputintents_prop
              {
                \exp_args:NV
                 \@@_write_outputintent:nn
                   \l_@@_tmpb_tl
                   { #1 }
              }
          }
          {
            \msg_warning:nne{pdfmeta}{colorprofile-undefined}{\l_@@_tmpb_tl}
          }
       }
   }
%    \end{macrocode}
%
% \subsection{Regression test}
% This is simply a copy of the backend function.
%    \begin{macrocode}
\cs_new_protected:Npn \pdfmeta_set_regression_data:
   { \__pdf_backend_set_regression_data: }
%    \end{macrocode}
%
% \section{XMP-Metadata implementation}

% \begin{variable}{\g_@@_xmp_bool}
% This boolean decides if the metadata are included
%    \begin{macrocode}
\bool_new:N\g_@@_xmp_bool
\bool_gset_true:N \g_@@_xmp_bool
%    \end{macrocode}
% \end{variable}
% Preset the two fields to avoid problems with standards.
%    \begin{macrocode}
\hook_gput_code:nnn{pdfmanagement/add}{pdfmanagement}
  {
   \pdfmanagement_add:nne {Info}{Producer}{(\c_sys_engine_exec_str-\c_sys_engine_version_str)}
   \pdfmanagement_add:nne {Info}{Creator}{(LaTeX)}
  }
%    \end{macrocode}
% \subsection{New document keys}
%    \begin{macrocode}
\cs_generate_variant:Nn\pdf_version_gset:n{e}
%    \end{macrocode}
% if the pdf version is wrong for the standard we force the highest possible.
%    \begin{macrocode}
\cs_new_protected:Nn\@@_force_standard_pdfversion:
 {
   \pdfmeta_standard_verify:nnF { min_pdf_version }
    { \pdf_version: }
    {
      \pdf_version_gset:e{ \pdfmeta_standard_item:n{ max_pdf_version } }
    }
  \pdfmeta_standard_verify:nnF { max_pdf_version }
    { \pdf_version: }
    {
      \pdf_version_gset:e{ \pdfmeta_standard_item:n{ max_pdf_version } }
    }
 }
\keys_define:nn { document / metadata }
 {
   _pdfstandard .choices:nn =
      {A-1B,A-2A,A-2B,A-2U,A-3A,A-3B,A-3U,A-4}
      {
        \prop_gset_eq:Nc \g__pdfmeta_standard_prop { g__pdfmeta_standard_pdf/#1 _prop }
        \@@_force_standard_pdfversion:
        \AddToDocumentProperties [document]{pdfstandard}{#1}
      },
   _pdfstandard / A-4F .code:n =
      {
        \prop_gset_eq:Nc \g__pdfmeta_standard_prop { g__pdfmeta_standard_pdf/A-4F_prop }
        \@@_force_standard_pdfversion:
        \AddToDocumentProperties [document]{pdfstandard}{A-4F}
      },
   _pdfstandard / A-4E .code:n =
      {
        \prop_gset_eq:Nc \g__pdfmeta_standard_prop { g__pdfmeta_standard_pdf/A-4E_prop }
        \@@_force_standard_pdfversion:
        \AddToDocumentProperties [document]{pdfstandard}{A-4E}
      },
   _pdfstandard / unknown .code:n =
      {
        \msg_error:nnn{pdf}{unknown-standard}{#1}
      },
   _pdfstandard / X-4 .code:n =
    {
      \AddToDocumentProperties [document]{pdfstandard-X}{PDF/X-4}
      \@@_xmp_add_pdfxid:
    },
   _pdfstandard / X-4p .code:n =
    {
      \AddToDocumentProperties [document]{pdfstandard-X}{PDF/X-4p}
      \@@_xmp_add_pdfxid:
    },
   _pdfstandard / X-5g .code:n =
    {
      \AddToDocumentProperties [document]{pdfstandard-X}{PDF/X-5g}
      \@@_xmp_add_pdfxid:
    },
   _pdfstandard / X-5n .code:n =
    {
      \AddToDocumentProperties [document]{pdfstandard-X}{PDF/X-5n}
      \@@_xmp_add_pdfxid:
    },
   _pdfstandard / X-5pg .code:n =
    {
      \AddToDocumentProperties [document]{pdfstandard-X}{PDF/X-5pg}
      \@@_xmp_add_pdfxid:
    },
   _pdfstandard / X-6 .code:n =
    {
      \AddToDocumentProperties [document]{pdfstandard-X}{PDF/X-6p}
      \@@_xmp_add_pdfxid:
    },
   _pdfstandard / X-6n .code:n =
    {
      \AddToDocumentProperties [document]{pdfstandard-X}{PDF/X-6n}
      \@@_xmp_add_pdfxid:
    },
   _pdfstandard / X-6p .code:n =
    {
      \AddToDocumentProperties [document]{pdfstandard-X}{PDF/X-6p}
      \@@_xmp_add_pdfxid:
    },
   _pdfstandard / UA-1 .code:n =
    {
     \pdf_version_compare:NnF < {2.0}
      {
        \pdf_version_gset:n {1.7}
      }
     \AddToDocumentProperties [document]{pdfstandard-UA}{{1}{}}
     \AddToHook{begindocument/before}
       {
         \prop_gput:Nnn \g__pdfmeta_standard_prop {omit_CID}{}
         \pdf_version_compare:NnF < {2.0}
           {
             \msg_warning:nneee %TODO make an error
              {pdf}{wrong-pdfversion}
              {\pdf_version:}{high}{UA-1}
           }
       }
    },
%    \end{macrocode}
% currently it is not possible to merge requirements - these need some thoughts as
% every standard has some common keys like the name or the yes.
% We therefore add some requirements manually.
%
%    \begin{macrocode}
   _pdfstandard / UA-2 .code:n =
    {
      \pdf_version_compare:NnT < {2.0}
       {
         \pdf_version_gset:n {2.0}
       }
      \AddToDocumentProperties [document]{pdfstandard-UA}{{2}{2024}}
%    \end{macrocode}
% 2025-06-11 \verb+Trailer_no_Info+ is only a should not a shall in UA-2 so we do not force it.
%    \begin{macrocode}
      %\AddToHook{begindocument/before}
       %{\prop_gput:Nnn \g__pdfmeta_standard_prop {Trailer_no_Info}{}}
      \AddToHook{begindocument/before}
       {
         \@@_xmp_wtpdf_accessibility_declaration:
         \@@_xmp_wtpdf_reuse_declaration:
         \pdf_version_compare:NnT < {2.0}
           {
             \msg_warning:nneee %TODO make an error
              {pdf}{wrong-pdfversion}
              {\pdf_version:}{low}{UA-2}
           }
       }
    },
   xmp  .choice:,
   xmp / true  .code:n = { \bool_gset_true:N \g_@@_xmp_bool },
   xmp / false .code:n = { \bool_gset_false:N \g_@@_xmp_bool},
   xmp .default:n = true,
%    \end{macrocode}
% These keys allow to disable or force the wtpdf declarations.
% Currently the content can not be changed and once they have been disabled there
% are gone. This will perhaps change.
%    \begin{macrocode}
   xmp / wtpdf .code:n =
    {
      \keys_set:nn {@@/xmp}{#1}
    },
 }
\keys_define:nn {@@/xmp}
 {
   reuse .choice:,
   reuse / true .code:n = \@@_xmp_wtpdf_reuse_declaration:,
   reuse / false .code:n =
    {
      \cs_set_eq:NN \@@_xmp_wtpdf_reuse_declaration: \prg_do_nothing:
    },
   accessibility .choice:,
   accessibility / true .code:n = \@@_xmp_wtpdf_accessibility_declaration:,
   accessibility /false .code:n =
    {
      \cs_set_eq:NN \@@_xmp_wtpdf_accessibility_declaration: \prg_do_nothing:
    },
  }
%    \end{macrocode}
% XMP debugging option
%    \begin{macrocode}
\bool_new:N \g_@@_xmp_export_bool
\str_new:N  \g_@@_xmp_export_str

\keys_define:nn { document / metadata }
  {
    ,debug .code:n =
      {
        \keys_set:nn { document / metadata / debug } {#1}
      }
    ,debug / xmp-export .choice:
    ,debug / xmp-export / true .code:n=
      {
        \bool_gset_true:N \g_@@_xmp_export_bool
        \str_gset_eq:NN \g_@@_xmp_export_str \c_sys_jobname_str
      }
    ,debug / xmp-export / false .code:n =
      {
        \bool_gset_false:N \g_@@_xmp_export_bool
      }
    ,debug / xmp-export /unknown .code:n =
      {
        \bool_gset_true:N \g_@@_xmp_export_bool
        \str_gset:Nn \g_@@_xmp_export_str { #1 }
      }
    ,debug / xmp-export .default:n = true
  }
%    \end{macrocode}
% \subsection{Messages}
%    \begin{macrocode}
\msg_new:nnn{pdfmeta}{xmp-defined}{The~XMP~#1~`#2`~is~already~declared}
\msg_new:nnn{pdfmeta}{xmp-undefined}{The~XMP~#1~`#2`~is~undefined}
\msg_new:nnn{pdfmeta}{colorprofile-undefined}{The~colorprofile~`#1`~is~unknown}
%    \end{macrocode}
% \subsection{Some helper commands}
% \subsubsection{Generate a BOM}
% \begin{macro}{\@@_xmp_generate_bom:}
%    \begin{macrocode}
\bool_lazy_or:nnTF
  { \sys_if_engine_luatex_p: }
  { \sys_if_engine_xetex_p: }
  {
    \cs_new:Npn \@@_xmp_generate_bom:
      { \char_generate:nn {"FEFF}{12} }
  }
  {
    \cs_new:Npn \@@_xmp_generate_bom:
      {
        \char_generate:nn {"EF}{12}
        \char_generate:nn {"BB}{12}
        \char_generate:nn {"BF}{12}
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Indentation}
% We provide a command which indents the xml based
% on a counter, and one which accepts a fix number.
% The counter can be increased and decreased.
%
% \begin{variable}{\l_@@_xmp_indent_int}
%    \begin{macrocode}
\int_new:N  \l_@@_xmp_indent_int
%    \end{macrocode}
% \end{variable}
% \begin{macro}{\@@_xmp_indent:,
%               \@@_xmp_indent:n,
%               \@@_xmp_incr_indent:,
%               \@@_xmp_decr_indent:}
%    \begin{macrocode}
\cs_new:Npn \@@_xmp_indent:
  {
    \iow_newline:
    \prg_replicate:nn {\l_@@_xmp_indent_int}{\c_space_tl}
  }

\cs_new:Npn \@@_xmp_indent:n #1
  {
    \iow_newline:
    \prg_replicate:nn {#1}{\c_space_tl}
  }

\cs_new_protected:Npn \@@_xmp_incr_indent:
  {
    \int_incr:N \l_@@_xmp_indent_int
  }

\cs_new_protected:Npn \@@_xmp_decr_indent:
  {
    \int_decr:N \l_@@_xmp_indent_int
  }
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Date and time handling}
% If the date is given in PDF format we have to split it to create
% the XMP format. We use a precompiled regex for this.
% To some extend the regex can also handle incomplete dates.
%
% \begin{variable}{\l_@@_xmp_date_regex}
%    \begin{macrocode}
\regex_new:N \l_@@_xmp_date_regex
\regex_set:Nn \l_@@_xmp_date_regex
 {D:(\d{4})(\d{2})(\d{2})(\d{2})?(\d{2})?(\d{2})?([Z\+\-])?(?:(\d{2})\')?(?:(\d{2})\')?}
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\@@_xmp_date_split:nN}
% This command takes a date in PDF format, splits it with the regex and
% stores the captures in a sequence.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_date_split:nN #1 #2 %#1 date, #2 seq
  {
    \regex_split:NnN \l_@@_xmp_date_regex {#1} #2
  }
\cs_generate_variant:Nn \@@_xmp_date_split:nN {VN,eN}
%    \end{macrocode}
% \end{macro}

% \begin{macro}[EXP]{\@@_xmp_print_date:N}
% This prints the date stored in a sequence as created
% by the previous command.
%
%    \begin{macrocode}
\cs_new:Npn\@@_xmp_print_date:N #1 % seq
  {
    \tl_if_blank:eTF { \seq_item:Nn #1 {1} }
     {
       \seq_item:Nn #1 {2} %year
        -
       \seq_item:Nn #1 {3} %month
        -
       \seq_item:Nn #1 {4} % day
       \tl_if_blank:eF
         { \seq_item:Nn #1 {5} }
         { T \seq_item:Nn #1 {5} } %hour
       \tl_if_blank:eF
         { \seq_item:Nn #1 {6} }
         { : \seq_item:Nn #1 {6} } %minutes
       \tl_if_blank:eF
         { \seq_item:Nn #1 {7} }
         { : \seq_item:Nn #1 {7} } %seconds
       \seq_item:Nn #1 {8}  %Z,+,-
       \seq_item:Nn #1 {9}
       \tl_if_blank:eF
         { \seq_item:Nn #1 {10} }
         { : \seq_item:Nn #1 {10} }
      }
      {
        \seq_item:Nn #1 {1}
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{variable}{\l_@@_xmp_currentdate_tl,\l_@@_xmp_currentdate_seq}
% The tl var contains the date of the log-file in PDF format,
% the seq the result split with the regex.
%    \begin{macrocode}
\tl_new:N  \l_@@_xmp_currentdate_tl
\seq_new:N \l_@@_xmp_currentdate_seq
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\@@_xmp_date_get:nNN}
% This checks a document property and if empty uses the current date.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_date_get:nNN #1 #2 #3
  %#1 property, #2 tl var with PDF date, #3 seq for split date
  {
    \tl_set:Ne #2 { \GetDocumentProperties{#1} }
    \tl_if_blank:VTF #2
      {
        \seq_set_eq:NN #3 \l_@@_xmp_currentdate_seq
        \tl_set_eq:NN  #2 \l_@@_xmp_currentdate_tl
      }
      {
        \@@_xmp_date_split:VN #2 #3
      }
  }
%    \end{macrocode}
% \end{macro}
% \subsubsection{UUID}
% We need a command to generate an uuid
% \begin{macro}{\@@_xmp_create_uuid:nN}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_create_uuid:nN #1 #2
  {
    \str_set:Ne#2 {\str_lowercase:f{\tex_mdfivesum:D{#1}}}
    \str_set:Ne#2
      { uuid:
        \str_range:Nnn #2{1}{8}
        -\str_range:Nnn#2{9}{12}
        -4\str_range:Nnn#2{13}{15}
        -8\str_range:Nnn#2{16}{18}
        -\str_range:Nnn#2{19}{30}
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Purifying and escaping of strings}
%
% \begin{macro}{\@@_xmp_sanitize:nN}
% We have to sanitize the user input. For this we pass
% it through |\text_purify| and then replace a few special chars.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_sanitize:nN #1 #2
%#1 input string, #2 str with the output
  {
    \group_begin:
     \text_declare_purify_equivalent:Nn \& {\tl_to_str:N & }
     \text_declare_purify_equivalent:Nn \texttilde {\c_tilde_str}
     \tl_set:Ne \l_@@_tmpa_tl { \text_purify:n {#1} }
     \str_gset:Ne \g_@@_tmpa_str { \tl_to_str:N \l_@@_tmpa_tl }
     \str_greplace_all:Nnn\g_@@_tmpa_str {&}{&amp;}
     \str_greplace_all:Nnn\g_@@_tmpa_str {<}{&lt;}
     \str_greplace_all:Nnn\g_@@_tmpa_str {>}{&gt;}
     \str_greplace_all:Nnn\g_@@_tmpa_str {"}{&quot;}
    \group_end:
     \str_set_eq:NN #2 \g_@@_tmpa_str
  }

\cs_generate_variant:Nn\@@_xmp_sanitize:nN {VN}
%    \end{macrocode}
% \end{macro}
%
% \subsection{Language handling}
% The language of the metadata is used in various attributes, so we store it in
% command.
% \begin{variable}{\l_@@_xmp_doclang_tl,\l_@@_xmp_metalang_tl}
%    \begin{macrocode}
\tl_new:N \l_@@_xmp_doclang_tl
\tl_new:N \l_@@_xmp_metalang_tl
%    \end{macrocode}
% \end{variable}
%
% The language is retrieved at the start of the packet. We assume that
% |lang| is always set and so don't use the |x-default| value of \pkg{hyperxmp}.
%
% \begin{variable}{\l_@@_xmp_lang_regex}
%    \begin{macrocode}
\regex_new:N\l_@@_xmp_lang_regex
\regex_set:Nn\l_@@_xmp_lang_regex {\A\[([A-Za-z\-]+)\](.*)}
%    \end{macrocode}
% \end{variable}
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_lang_get:nNN #1 #2 #3
% #1 text, #2 tl var for lang match (or default), #3 tl var for text
  {
    \regex_extract_once:NnN \l_@@_xmp_lang_regex {#1}\l_@@_tmpa_seq
    \seq_if_empty:NTF \l_@@_tmpa_seq
      {
        \tl_set:Nn #2 \l_@@_xmp_metalang_tl
        \tl_set:Nn #3 {#1}
      }
      {
        \tl_set:Ne #2 {\seq_item:Nn\l_@@_tmpa_seq{2}}
        \tl_set:Ne #3 {\seq_item:Nn\l_@@_tmpa_seq{3}}
      }
  }
\cs_generate_variant:Nn \@@_xmp_lang_get:nNN {eNN,VNN}
%    \end{macrocode}
%


% \subsection{Filling the packet}
% This tl var that holds the whole packet
% \begin{variable}{\g_@@_xmp_packet_tl}
%    \begin{macrocode}
\tl_new:N \g_@@_xmp_packet_tl
%    \end{macrocode}
% \end{variable}
%
% \subsubsection{Helper commands to add lines and lists}
%
% \begin{macro}{\@@_xmp_add_packet_chunk:n}
% This is the most basic command.
% It is meant to produce a line and will use the current indent.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_add_packet_chunk:n #1
  {
    \tl_gput_right:Ne\g_@@_xmp_packet_tl
      {
        \@@_xmp_indent:  \exp_not:n{#1}
      }
  }
\cs_generate_variant:Nn \@@_xmp_add_packet_chunk:n {e}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\@@_xmp_add_packet_chunk:nN}
% This is the most basic command.
% It is meant to produce a line and will use the current indent.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_add_packet_chunk:nN #1 #2
  {
    \tl_put_right:Ne#2
      {
        \@@_xmp_indent:  \exp_not:n{#1}
      }
  }
\cs_generate_variant:Nn \@@_xmp_add_packet_chunk:nN {eN}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\@@_xmp_add_packet_open:nn}
% This commands opens a xml structure and increases the indent.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_add_packet_open:nn #1 #2 %#1 prefix #2 name
  {
    \@@_xmp_add_packet_chunk:n {<#1:#2>}
    \@@_xmp_incr_indent:
  }
\cs_generate_variant:Nn \@@_xmp_add_packet_open:nn  {ne}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\@@_xmp_add_packet_open_attr:nnn}
% This commands opens a xml structure too but allows also to give an
% attribute.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_add_packet_open_attr:nnn #1 #2 #3
  %#1 prefix #2 name #3 attr
  {
    \@@_xmp_add_packet_chunk:n {<#1:#2~#3>}
    \@@_xmp_incr_indent:
  }
\cs_generate_variant:Nn \@@_xmp_add_packet_open_attr:nnn  {nne}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\@@_xmp_add_packet_close:nn}
% This closes a structure and decreases the indent.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_add_packet_close:nn #1 #2 %#1 prefix #2:name
  {
    \@@_xmp_decr_indent:
    \@@_xmp_add_packet_chunk:n {</#1:#2>}
  }
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_xmp_add_packet_line:nnn}
%   This will produce a full line with open and closing xml.
%   The content is sanitized. We test if there is content to be
%   able to suppress data which has not be set.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_add_packet_line:nnn #1 #2 #3
 %#1 prefix #2 name #3 content
  {
    \tl_if_blank:nF {#3}
     {
      \@@_xmp_sanitize:nN {#3}\l_@@_tmpa_str
      \@@_xmp_add_packet_chunk:e {<#1:#2>\l_@@_tmpa_str</#1:#2>}
     }
  }
\cs_generate_variant:Nn \@@_xmp_add_packet_line:nnn {nne,nnV,nee}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_xmp_add_packet_line:nnnN}
%   This will produce a full line with open and closing xml and store it in
%   the given tl-var. This allows to prebuild blocks and then to test if there are empty.
%   The content is sanitized. We test if there is content to be
%   able to suppress data which has not be set.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_add_packet_line:nnnN #1 #2 #3 #4
 %#1 prefix #2 name #3 content #4 tl_var to prebuilt.
  {
    \tl_if_blank:nF {#3}
     {
      \@@_xmp_sanitize:nN {#3}\l_@@_tmpa_str
      \@@_xmp_add_packet_chunk:eN {<#1:#2>\l_@@_tmpa_str</#1:#2>} #4
     }
  }
\cs_generate_variant:Nn \@@_xmp_add_packet_line:nnnN {nneN}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_xmp_add_packet_line_attr:nnnn}
% A similar command with attribute
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_add_packet_line_attr:nnnn #1 #2 #3 #4
 %#1 prefix #2 name #3 attribute #4 content
  {
    \tl_if_blank:nF {#4}
     {
      \@@_xmp_sanitize:nN {#4}\l_@@_tmpa_str
      \@@_xmp_add_packet_chunk:e {<#1:#2~#3>\l_@@_tmpa_str</#1:#2>}
     }
  }
\cs_generate_variant:Nn \@@_xmp_add_packet_line_attr:nnnn {nnee,nneV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_xmp_add_packet_line_default:nnnn}
%    \begin{macrocode}
 \cs_new_protected:Npn \@@_xmp_add_packet_line_default:nnnn #1 #2 #3 #4
   % #1 prefix #2 name #3 default #4 content
   {
     \tl_if_blank:nTF { #4 }
      {
       \tl_set:Nn  \l_@@_tmpa_tl  {#3}
      }
      {
        \tl_set:Nn \l_@@_tmpa_tl  {#4}
      }
     \@@_xmp_add_packet_line:nnV {#1}{#2}\l_@@_tmpa_tl
   }
\cs_generate_variant:Nn \@@_xmp_add_packet_line_default:nnnn {nnee}
%    \end{macrocode}
% \end{macro}
% Some data are stored as unordered (Bag) or ordered lists (Seq) or (Alt).
% The first variant are for simple text without language support:
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_add_packet_list_simple:nnnn #1 #2 #3 #4
  %#1 prefix, #2 name,  #3 type (Seq/Bag/Alt) #4 a clist
  {
    \clist_if_empty:nF { #4 }
      {
        \@@_xmp_add_packet_open:nn {#1}{#2}
         \@@_xmp_add_packet_open:nn {rdf}{#3}
          \clist_map_inline:nn {#4}
            {
              \@@_xmp_add_packet_line:nnn
               {rdf}{li}{##1}
            }
         \@@_xmp_add_packet_close:nn{rdf}{#3}
        \@@_xmp_add_packet_close:nn {#1}{#2}
     }
   }
\cs_generate_variant:Nn \@@_xmp_add_packet_list_simple:nnnn {nnnV,nnne}
%    \end{macrocode}
% Here we check also for the language.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_add_packet_list:nnnn #1 #2 #3 #4
  %#1 prefix, #2 name,  #3 type (Seq/Bag/Alt) #4 a clist
  {
    \clist_if_empty:nF { #4 }
      {
        \@@_xmp_add_packet_open:nn {#1}{#2}
         \@@_xmp_add_packet_open:nn {rdf}{#3}
          \clist_map_inline:nn {#4}
            {
              \@@_xmp_lang_get:nNN {##1}\l_@@_tmpa_tl\l_@@_tmpb_tl
%    \end{macrocode}
% change 2024-02-22. There should be if possible a x-default
% entry as some viewers need that.  So if the language is equal to the
% main language we use that. This assumes that the user hasn't marked
% every entry as some other language!
% x-default has to be the first entry, see issue \#92,
% so we have to go through the list twice.
%    \begin{macrocode}
               \tl_if_eq:eeT{\l_@@_tmpa_tl}{\l_@@_xmp_metalang_tl}
                 {
                   \@@_xmp_add_packet_line_attr:nneV
                    {rdf}{li}{xml:lang="x-default" }\l_@@_tmpb_tl
                   \@@_xmp_add_packet_line_attr:nneV
                    {rdf}{li}{xml:lang="\l_@@_tmpa_tl" }\l_@@_tmpb_tl
                 }
             }
           \clist_map_inline:nn {#4}
             {
               \@@_xmp_lang_get:nNN {##1}\l_@@_tmpa_tl\l_@@_tmpb_tl
               \tl_if_eq:eeF{\l_@@_tmpa_tl}{\l_@@_xmp_metalang_tl}
                 {
                   \@@_xmp_add_packet_line_attr:nneV
                    {rdf}{li}{xml:lang="\l_@@_tmpa_tl" }\l_@@_tmpb_tl
                 }
             }
         \@@_xmp_add_packet_close:nn{rdf}{#3}
        \@@_xmp_add_packet_close:nn {#1}{#2}
     }
   }
\cs_generate_variant:Nn \@@_xmp_add_packet_list:nnnn {nnne}
%    \end{macrocode}
%
% \subsubsection{Building the main packet}
%
% \begin{macro}{\@@_xmp_build_packet:}
% This is the main command to build the packet.
% As data has to be set and collected first, it will be expanded
% rather late in the document.
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_build_packet:
  {
%    \end{macrocode}
% Get the main languages
% \begin{NOTE}{UF}
% think if an error is needed for empty lang.
% \end{NOTE}
%    \begin{macrocode}
   \tl_set:Ne \l_@@_xmp_doclang_tl  {\GetDocumentProperties{document/lang}}
   \tl_set:Ne \l_@@_xmp_metalang_tl {\GetDocumentProperties{hyperref/pdfmetalang}}
   \tl_if_blank:VT \l_@@_xmp_metalang_tl
    { \cs_set_eq:NN \l_@@_xmp_metalang_tl\l_@@_xmp_doclang_tl}
%    \end{macrocode}
% we preprocess a number of data to be able to suppress them and their schema
% if there are unused. Currently only done for iptc
%    \begin{macrocode}
   \@@_xmp_build_iptc_data:N \l_@@_xmp_iptc_data_tl
   \tl_if_empty:NT \l_@@_xmp_iptc_data_tl
     {
       \seq_remove_all:Nn \l_@@_xmp_schema_seq { Iptc4xmpCore }
     }
%    \end{macrocode}
% The start of the package.
% No need to try to juggle with catcode, this is fix text
%    \begin{macrocode}
     \@@_xmp_add_packet_chunk:e
      {<?xpacket~begin="\@@_xmp_generate_bom:"~id="W5M0MpCehiHzreSzNTczkc9d"?>}
     \@@_xmp_add_packet_open:nn{x}{xmpmeta~xmlns:x="adobe:ns:meta/"}
      \@@_xmp_add_packet_open:ne{rdf}
        {RDF~xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns\c_hash_str"}
%    \end{macrocode}
% The  rdf namespaces
%    \begin{macrocode}
       \@@_xmp_add_packet_open_attr:nne
         {rdf}{Description}{rdf:about="" \g_@@_xmp_xmlns_tl}
%    \end{macrocode}
% The extensions
%    \begin{macrocode}
        \@@_xmp_add_packet_open:nn{pdfaExtension}{schemas}
         \@@_xmp_add_packet_open:nn {rdf}{Bag}
           \seq_map_inline:Nn \l_@@_xmp_schema_seq
              {
                \tl_use:c { g_@@_xmp_schema_##1_tl }
              }
         \@@_xmp_add_packet_close:nn {rdf}{Bag}
        \@@_xmp_add_packet_close:nn {pdfaExtension}{schemas}
%    \end{macrocode}
% Now starts the part with the data.
%    \begin{macrocode}
    % data
        \@@_xmp_build_pdf:
        \@@_xmp_build_xmpRights:
        \@@_xmp_build_standards: %pdfaid,pdfxid,pdfuaid
        \@@_xmp_build_pdfd:
        \@@_xmp_build_dc:
        \@@_xmp_build_photoshop:
        \@@_xmp_build_xmp:
        \@@_xmp_build_xmpMM:
        \@@_xmp_build_prism:
        \@@_xmp_build_iptc:
        \@@_xmp_build_user: %user additions
    % end
      \@@_xmp_add_packet_close:nn {rdf}{Description}
     \@@_xmp_add_packet_close:nn {rdf}{RDF}
    \@@_xmp_add_packet_close:nn {x}{xmpmeta}
    \int_set:Nn  \l_@@_xmp_indent_int{20}
    \prg_replicate:nn{10}{\@@_xmp_add_packet_chunk:n {}}
    \int_zero:N \l_@@_xmp_indent_int
    \@@_xmp_add_packet_chunk:n {<?xpacket~end="w"?>}
 }
%    \end{macrocode}
% \end{macro}

% \subsection{Building the chunks: rdf namespaces}
% This is the list of external names spaces.
% They are rather simple, and we store them directly
% into a string. Special chars should be escaped properly,
% see e.g. |\c_hash_str| for the hash.

% \begin{variable}{\g_@@_xmp_xmlns_tl,\g_@@_xmp_xmlns_prop}
% The string will hold the prepared chunk, the prop stores the name spaces
% so that one can check on the user level for duplicates.
%    \begin{macrocode}
\str_new:N  \g_@@_xmp_xmlns_tl
\prop_new:N \g_@@_xmp_xmlns_prop
%    \end{macrocode}
% \end{variable}
% \begin{macro}{\@@_xmp_xmlns_new:nn}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_xmlns_new:nn #1 #2
  {
    \prop_gput:Nnn \g_@@_xmp_xmlns_prop {#1}{#2}
    \tl_gput_right:Ne \g_@@_xmp_xmlns_tl
      {
        \@@_xmp_indent:n{4} xmlns:\exp_not:n{#1="#2"}
      }
  }
%    \end{macrocode}
% \end{macro}
% Now we fill the data. The list is more or less the same as in hyperxmp
% The pdfxid entry is only added if an X standard is used, see issue \#50
% and the schema below.
%    \begin{macrocode}
\@@_xmp_xmlns_new:nn {pdf}      {http://ns.adobe.com/pdf/1.3/}
\@@_xmp_xmlns_new:nn {xmpRights}{http://ns.adobe.com/xap/1.0/rights/}
\@@_xmp_xmlns_new:nn {dc}       {http://purl.org/dc/elements/1.1/}
\@@_xmp_xmlns_new:nn {photoshop}{http://ns.adobe.com/photoshop/1.0/}
\@@_xmp_xmlns_new:nn {xmp}      {http://ns.adobe.com/xap/1.0/}
\@@_xmp_xmlns_new:nn {xmpMM}    {http://ns.adobe.com/xap/1.0/mm/}
\@@_xmp_xmlns_new:nn {stEvt}
  {http://ns.adobe.com/xap/1.0/sType/ResourceEvent\c_hash_str}
\@@_xmp_xmlns_new:nn {pdfaid}   {http://www.aiim.org/pdfa/ns/id/}
\@@_xmp_xmlns_new:nn {pdfuaid}  {http://www.aiim.org/pdfua/ns/id/}
\@@_xmp_xmlns_new:nn {pdfx}     {http://ns.adobe.com/pdfx/1.3/}
%\@@_xmp_xmlns_new:nn {pdfxid}   {http://www.npes.org/pdfx/ns/id/}
\@@_xmp_xmlns_new:nn {prism}    {http://prismstandard.org/namespaces/basic/3.0/}
%\@@_xmp_xmlns_new:nn {jav}      {http://www.niso.org/schemas/jav/1.0/}
%\@@_xmp_xmlns_new:nn {xmpTPg}   {http://ns.adobe.com/xap/1.0/t/pg/}
\@@_xmp_xmlns_new:nn {stFnt}    {http://ns.adobe.com/xap/1.0/sType/Font\c_hash_str}
\@@_xmp_xmlns_new:nn {Iptc4xmpCore}{http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/}
\@@_xmp_xmlns_new:nn {pdfaExtension}{http://www.aiim.org/pdfa/ns/extension/}
\@@_xmp_xmlns_new:nn {pdfaSchema}{http://www.aiim.org/pdfa/ns/schema\c_hash_str}
\@@_xmp_xmlns_new:nn {pdfaProperty}{http://www.aiim.org/pdfa/ns/property\c_hash_str}
\@@_xmp_xmlns_new:nn {pdfaType} {http://www.aiim.org/pdfa/ns/type\c_hash_str}
\@@_xmp_xmlns_new:nn {pdfaField}{http://www.aiim.org/pdfa/ns/field\c_hash_str}
%    \end{macrocode}

% \subsection{Building the chunks: Extensions}
% In this part local name spaces or additional names in a name space can be declared.
% A \enquote{schema} declaration consist of the declaration of the name, uri and prefix
% which then surrounds a bunch of property declarations.
% The current code doesn't support all syntax options but sticks to
% what is used in \pkg{hyperxmp} and \pkg{pdfx}.
% If needed it can be extended later.
%
% \begin{variable}{\l_@@_xmp_schema_seq}
% This variable will hold the list of prefix so that we can loop
% to produce the final XML
%    \begin{macrocode}
\seq_new:N \l_@@_xmp_schema_seq
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\@@_xmp_schema_new:nnn}
%  With this command a new schema can be declared. The main tl contains the
%  XML wrapper code, it then includes the list of properties which are
%  created with the next command.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_schema_new:nnn #1 #2 #3
  %#1 name #2 prefix, #3 text
  {
    \tl_if_exist:cTF { g_@@_xmp_schema_#2_tl }
      {
       \msg_warning:nnnn{pdfmeta}{xmp-defined}{schema}{#2}
      }
      {
        \seq_put_right:Nn \l_@@_xmp_schema_seq { #2 }
        \tl_new:c { g_@@_xmp_schema_#2_tl }
        \tl_new:c { g_@@_xmp_schema_#2_properties_tl }
        \tl_gput_right:cn { g_@@_xmp_schema_#2_tl }
          {
            \@@_xmp_add_packet_open_attr:nnn{rdf}{li}{rdf:parseType="Resource"}
             \@@_xmp_add_packet_line:nnn {pdfaSchema}{schema}{#1}
             \@@_xmp_add_packet_line:nnn {pdfaSchema}{prefix}{#2}
             \@@_xmp_add_packet_line:nnn {pdfaSchema}{namespaceURI}{#3}
             \@@_xmp_add_packet_open:nn {pdfaSchema}{property}
              \@@_xmp_add_packet_open:nn{rdf}{Seq}
                  \tl_use:c { g_@@_xmp_schema_#2_properties_tl }
              \@@_xmp_add_packet_close:nn{rdf}{Seq}
             \@@_xmp_add_packet_close:nn {pdfaSchema}{property}
            \cs_if_exist_use:c {@@_xmp_schema_#2_additions:}
            \@@_xmp_add_packet_close:nn{rdf}{li}
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}{\@@_xmp_property_new:nnnnn}
%  This adds a property to a schema.
%    \begin{macrocode}
\prop_new:N\g_@@_xmp_schema_property_prop
\cs_new_protected:Npn \@@_xmp_property_new:nnnnn #1 #2 #3 #4 #5 %
    %#1 schema #2 name, #3 type, #4 category #5 description
  {
    \tl_if_exist:cTF { g_@@_xmp_schema_#1_properties_tl }
     {
       \prop_get:NeNF \g_@@_xmp_schema_property_prop {#1:#2}\l_@@_tmpa_tl
        {
          \prop_gput:Nee \g_@@_xmp_schema_property_prop {#1:#2}{#3}
          \tl_gput_right:cn { g_@@_xmp_schema_#1_properties_tl }
            {
              \@@_xmp_add_packet_open:nn {rdf}{li~rdf:parseType="Resource"}
                \@@_xmp_add_packet_line:nnn {pdfaProperty}{name}{#2}
                \@@_xmp_add_packet_line:nnn {pdfaProperty}{valueType}{#3}
                \@@_xmp_add_packet_line:nnn {pdfaProperty}{category}{#4}
                \@@_xmp_add_packet_line:nnn {pdfaProperty}{description}{#5}
              \@@_xmp_add_packet_close:nn{rdf}{li}
           }
        }
      }
      {
        \msg_warning:nnnn{pdfmeta}{xmp-undefined}{schema}{#1}
      }
  }
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\@@_xmp_add_packet_field:nnn}
%  This adds a field to a schema.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_add_packet_field:nnn #1 #2 #3 %
  %#1 name #2 valuetype #3 description
  {
    \@@_xmp_add_packet_open_attr:nnn {rdf}{li}{rdf:parseType="Resource"}
          \@@_xmp_add_packet_line:nnn {pdfaField}{name}{#1}
          \@@_xmp_add_packet_line:nnn {pdfaField}{valueType}{#2}
          \@@_xmp_add_packet_line:nnn {pdfaField}{description}{#3}
    \@@_xmp_add_packet_close:nn{rdf}{li}
  }
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{The extension data}
%
% The list of extension has been reviewed and compared with
% the list of namespaces which can be used in pdf/A-1\footnote{While
% A-1 builds on PDF 1.4 and so it probably no longer relevant, it is
% not quite clear if one can remove this for A-2 and newer, so we stay on the
% safe side.}
%
%
% [1]~\url{https://www.pdfa.org/wp-content/uploads/2011/08/tn0008_predefined_xmp_properties_in_pdfa-1_2008-03-20.pdf}
% and the content of the namespaces as listed here
% [2]~\url{https://developer.adobe.com/xmp/docs/XMPNamespaces/pdf/}
%
%
%  \begin{description}
%  \item[pdf] property: Trapped.
%  We ignore it, it seems to validate without it.
%  \item[xmpMM] properties DocumentID, InstanceID, VersionID,
%  Renditionclass declared by hyperxmp.
%  Properties InstanceID and OriginalDocumentID declared by pdfx (pdfx.xmp)
%  With the exception of OriginalDocumentID all are already allowed and
%  predefined.
%    \begin{macrocode}
\@@_xmp_schema_new:nnn
   {XMP~Media~Management~Schema}
   {xmpMM}
   {http://ns.adobe.com/xap/1.0/mm/}
\@@_xmp_property_new:nnnnn
   {xmpMM}
   {OriginalDocumentID}
   {URI}
   {internal}
   {The~common~identifier~for~all~versions~and~renditions~of~a~document.}
%    \end{macrocode}
%  \item[pdfaid] properties part and conformance are declared by hyperxmp, but
%  no here as already in http://www.aiim.org/pdfa/ns/id/. But we declare
%  year so that it can be used also with older A-standards.
%   \begin{macro}[no-user-doc]{pdfaid~(schema)}
%    \begin{macrocode}
\@@_xmp_schema_new:nnn
   {PDF/A~Identification~Schema}
   {pdfaid}
   {http://www.aiim.org/pdfa/ns/id/}
\@@_xmp_property_new:nnnnn
   {pdfaid}
   {year}
   {Integer}
   {internal}
   {Year~of~standard}
\@@_xmp_property_new:nnnnn
   {pdfaid}
   {rev}
   {Integer}
   {internal}
   {Revision~year~of~standard}
%    \end{macrocode}
% \end{macro}
%  \item[pdfuaid] here we need (?) to declare the property
%  \enquote{part} and \enquote{rev}.
% \begin{macro}[no-user-doc]{pdfuaid~(schema)}
%    \begin{macrocode}
\@@_xmp_schema_new:nnn
   {PDF/UA~Universal~Accessibility~Schema}
   {pdfuaid}
   {http://www.aiim.org/pdfua/ns/id/}
\@@_xmp_property_new:nnnnn
   {pdfuaid}
   {part}
   {Integer}
   {internal}
   {Part~of~ISO~14289~standard}
\@@_xmp_property_new:nnnnn
   {pdfuaid}
   {rev}
   {Integer}
   {internal}
   {Revision~of~ISO~14289~standard}
%    \end{macrocode}
% \end{macro}
% \item[pdfx] According to [1] not an allowed schema, but it seems
% to validate and allow to set the pdf/X version, \pkg{hyperxmp}
% declares here the properties |GTS_PDFXVersion| and |GTS_PDFXConformance|.
% Ignored as only relevant for older pdf/X version not supported by the pdfmanagement.
%
% \item[pdfxid] we set this so that we
% can add the pdf/X version for pdf/X-4 and higher. This is only set if
% a pdf/X standard is used, see issue \#50
% \begin{macro}[no-user-doc]{pdfxid~(schema)}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_add_pdfxid:
 {
  \@@_xmp_xmlns_new:nn {pdfxid}   {http://www.npes.org/pdfx/ns/id/}
  \@@_xmp_schema_new:nnn
      {PDF/X~ID~Schema}
      {pdfxid}
      {http://www.npes.org/pdfx/ns/id/}
  \@@_xmp_property_new:nnnnn
      {pdfxid}
      {GTS_PDFXVersion}
      {Text}
      {internal}
      {ID~of~PDF/X~standard}
 }
%    \end{macrocode}
% \end{macro}

%\item[Prism]
% \begin{macro}[no-user-doc]{prism~(schema)}
%    \begin{macrocode}
\@@_xmp_schema_new:nnn
  {PRISM~Basic~Metadata}
  {prism}
  {http://prismstandard.org/namespaces/basic/3.0/}
\@@_xmp_property_new:nnnnn
  {prism}
  {complianceProfile}
  {Text}
  {internal}
  {PRISM~specification~compliance~profile~to~which~this~document~adheres}
\@@_xmp_property_new:nnnnn
  {prism}
  {publicationName}
  {Text}
  {external}
  {Publication~name}
\@@_xmp_property_new:nnnnn
  {prism}
  {aggregationType}
  {Text}
  {external}
  {Publication~type}
\@@_xmp_property_new:nnnnn
  {prism}
  {bookEdition}
  {Text}
  {external}
  {Edition~of~the~book~in~which~the~document~was~published}
\@@_xmp_property_new:nnnnn
  {prism}
  {volume}
  {Text}
  {external}
  {Publication~volume~number}
\@@_xmp_property_new:nnnnn
  {prism}
  {number}
  {Text}
  {external}
  {Publication~issue~number~within~a~volume}
\@@_xmp_property_new:nnnnn
  {prism}
  {pageRange}
  {Text}
  {external}
  {Page~range~for~the~document~within~the~print~version~of~its~publication}
\@@_xmp_property_new:nnnnn
  {prism}
  {issn}
  {Text}
  {external}
  {ISSN~for~the~printed~publication~in~which~the~document~was~published}
\@@_xmp_property_new:nnnnn
  {prism}
  {eIssn}
  {Text}
  {external}
  {ISSN~for~the~electronic~publication~in~which~the~document~was~published}
\@@_xmp_property_new:nnnnn
  {prism}
  {isbn}
  {Text}
  {external}
  {ISBN~for~the~publication~in~which~the~document~was~published}
\@@_xmp_property_new:nnnnn
  {prism}
  {doi}
  {Text}
  {external}
  {Digital~Object~Identifier~for~the~document}
\@@_xmp_property_new:nnnnn
  {prism}
  {url}
  {URL}
  {external}
  {URL~at~which~the~document~can~be~found}
\@@_xmp_property_new:nnnnn
  {prism}
  {byteCount}
  {Integer}
  {internal}
  {Approximate~file~size~in~octets}
\@@_xmp_property_new:nnnnn
  {prism}
  {pageCount}
  {Integer}
  {internal}
  {Number~of~pages~in~the~print~version~of~the~document}
\@@_xmp_property_new:nnnnn
  {prism}
  {subtitle}
  {Text}
  {external}
  {Document's~subtitle}
%    \end{macrocode}
% \end{macro}
% \item[iptc]
% \begin{macro}[no-user-doc]{iptc (schema)}
%    \begin{macrocode}
\@@_xmp_schema_new:nnn
  {IPTC~Core~Schema}
  {Iptc4xmpCore}
  {http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/}
\@@_xmp_property_new:nnnnn
  {Iptc4xmpCore}
  {CreatorContactInfo}
  {ContactInfo}
  {external}
  {Document~creator's~contact~information}
\cs_new_protected:cpn { @@_xmp_schema_Iptc4xmpCore_additions: }
  {
    \@@_xmp_add_packet_open:nn{pdfaSchema}{valueType}
      \@@_xmp_add_packet_open:nn{rdf}{Seq}
        \@@_xmp_add_packet_open_attr:nnn{rdf}{li}{rdf:parseType="Resource"}
          \@@_xmp_add_packet_line:nnn{pdfaType}{type}{ContactInfo}
          \@@_xmp_add_packet_line:nnn{pdfaType}{namespaceURI}
             {http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/}
          \@@_xmp_add_packet_line:nnn{pdfaType}{prefix}{Iptc4xmpCore}
          \@@_xmp_add_packet_line:nnn{pdfaType}{description}
            {Basic~set~of~information~to~get~in~contact~with~a~person}
          \@@_xmp_add_packet_open:nn{pdfaType}{field}
           \@@_xmp_add_packet_open:nn{rdf}{Seq}
            \@@_xmp_add_packet_field:nnn{CiAdrCity}{Text}
              {Contact~information~city}
            \@@_xmp_add_packet_field:nnn{CiAdrCtry}{Text}
              {Contact~information~country}
            \@@_xmp_add_packet_field:nnn{CiAdrExtadr}{Text}
              {Contact~information~address}
            \@@_xmp_add_packet_field:nnn{CiAdrPcode}{Text}
              {Contact~information~local~postal~code}
            \@@_xmp_add_packet_field:nnn{CiAdrRegion}{Text}
              {Contact~information~regional~information~such~as~state~or~province}
            \@@_xmp_add_packet_field:nnn{CiEmailWork}{Text}
              {Contact~information~email~address(es)}
            \@@_xmp_add_packet_field:nnn{CiTelWork}{Text}
              {Contact~information~telephone~number(s)}
            \@@_xmp_add_packet_field:nnn{CiUrlWork}{Text}
              {Contact~information~Web~URL(s)}
           \@@_xmp_add_packet_close:nn{rdf}{Seq}
         \@@_xmp_add_packet_close:nn{pdfaType}{field}
        \@@_xmp_add_packet_close:nn{rdf}{li}
      \@@_xmp_add_packet_close:nn{rdf}{Seq}
    \@@_xmp_add_packet_close:nn{pdfaSchema}{valueType}
  }
%    \end{macrocode}
% \end{macro}
% \item[jav]: currently ignored
%
% \item[declarations] The PDF Declarations mechanism allows creation and
% editing software to declare, via a PDF Declaration, a PDF file to be in
% conformance with a 3rd party specification or profile
% that may not be related to PDF technology. Their specification is for example
% described in \url{https://pdfa.org/wp-content/uploads/2019/09/PDF-Declarations.pdf}.
%
% If declarations are added to the XMP-metadata they need (for pdf/A compliance) a
% schema declaration. We do not add it by default but define here a command to enable
% it. (This can be done in the document preamble as xmp is built only at the end.)
%
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_schema_enable_pdfd:
 {
  \@@_xmp_xmlns_new:nn {pdfd}{http://pdfa.org/declarations/}
  \@@_xmp_schema_new:nnn
    {PDF~Declarations~Schema}
    {pdfd}
    {http://pdfa.org/declarations/}
  \@@_xmp_property_new:nnnnn
    {pdfd}
    {declarations}
    {Bag~declaration}
    {external}
    {An~unordered~array~of~PDF~Declaration~entries,~where~each~PDF~Declaration~representing~a~statement~of~conformance~with~ an~identified~external~standard~or~profile,~along~with~optional~information~identifying~the~nature~of~the~claim.}
%    \end{macrocode}
% the values are complicated so we use the additions: method to add them.
%    \begin{macrocode}
  \cs_new_protected:cpn { __pdfmeta_xmp_schema_pdfd_additions: }
    {
      \@@_xmp_add_packet_open:nn{pdfaSchema}{valueType}
        \@@_xmp_add_packet_open:nn{rdf}{Seq}
          \@@_xmp_add_packet_open_attr:nnn{rdf}{li}{rdf:parseType="Resource"}
            \@@_xmp_add_packet_line:nnn{pdfaType}{type}{claim}
            \@@_xmp_add_packet_line:nnn{pdfaType}{namespaceURI}
               {http://pdfa.org/declarations/}
            \@@_xmp_add_packet_line:nnn{pdfaType}{prefix}{pdfd}
            \@@_xmp_add_packet_line:nnn{pdfaType}{description}
              {A~structure~describing~properties~of~an~individual claim.}
            \@@_xmp_add_packet_open:nn{pdfaType}{field}
             \@@_xmp_add_packet_open:nn{rdf}{Seq}
              \@@_xmp_add_packet_field:nnn{claimReport}{Text}
                {A~URL~to~a~report~containing~details~of~the~specific~conformance~claim.}
              \@@_xmp_add_packet_field:nnn{claimCredentials}{Text}
                {The~claimant's~credentials.}
              \@@_xmp_add_packet_field:nnn{claimDate}{Text}
                {A~date~identifying~when~the~claim~was~made.}
              \@@_xmp_add_packet_field:nnn{claimBy}{Text}
                {The~name~of~the~organization~and/or~individual~and/or~software~making~the~claim.}
             \@@_xmp_add_packet_close:nn{rdf}{Seq}
           \@@_xmp_add_packet_close:nn{pdfaType}{field}
          \@@_xmp_add_packet_close:nn{rdf}{li}
          \@@_xmp_add_packet_open_attr:nnn{rdf}{li}{rdf:parseType="Resource"}
            \@@_xmp_add_packet_line:nnn{pdfaType}{type}{declaration}
            \@@_xmp_add_packet_line:nnn{pdfaType}{namespaceURI}
               {http://pdfa.org/declarations/}
            \@@_xmp_add_packet_line:nnn{pdfaType}{prefix}{pdfd}
            \@@_xmp_add_packet_line:nnn{pdfaType}{description}
              {A~structure~describing~a~single~PDF~ Declaration~asserting~conformance~with~ an~externally-identified~standard~or~ profile.}
            \@@_xmp_add_packet_open:nn{pdfaType}{field}
             \@@_xmp_add_packet_open:nn{rdf}{Seq}
              \@@_xmp_add_packet_field:nnn{conformsTo}{Text}
                {A~property~containing~a~URI~specifying~the~standard~or~profile~by~the~PDF~Declaration.~This~property~is~ intended~to~mirror~the~Dublin~Core~property~dc:conformsTo.}
              \@@_xmp_add_packet_field:nnn{claimData}{Bag~claim}
                {An~unordered~array~of~claim~data,~where~each~claim~identifies~the~nature~of~the~claim.}
             \@@_xmp_add_packet_close:nn{rdf}{Seq}
           \@@_xmp_add_packet_close:nn{pdfaType}{field}
          \@@_xmp_add_packet_close:nn{rdf}{li}
        \@@_xmp_add_packet_close:nn{rdf}{Seq}
      \@@_xmp_add_packet_close:nn{pdfaSchema}{valueType}
    }
%    \end{macrocode}
% the schema should be added only once so disable it after use:
%    \begin{macrocode}
   \cs_gset_eq:NN \@@_xmp_schema_enable_pdfd: \prg_do_nothing:
 }
%    \end{macrocode}
%
% \end{description}
% \subsection{The actual user / document data}

% \subsubsection{pdf}
% This builds pdf related the data with the (prefix \enquote{pdf}).
%
% \begin{macro}[int]{\@@_xmp_build_pdf:}
% \begin{macro}[int]{
%   Producer/pdfproducer,
%   PDFversion}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_build_pdf:
  {
%    \end{macrocode}
% At first the producer. If not given manually we build it from the exec string
% plus the version number
%    \begin{macrocode}
  \@@_xmp_add_packet_line_default:nnee
    {pdf}{Producer}
    {\c_sys_engine_exec_str-\c_sys_engine_version_str}
    {\GetDocumentProperties{hyperref/pdfproducer}}
%    \end{macrocode}
%  Now the PDF version
%    \begin{macrocode}
   \@@_xmp_add_packet_line:nne{pdf}{PDFVersion}{\pdf_version:}
%    \end{macrocode}
%    \begin{macrocode}
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsubsection{xmp}
% This builds the data with the (prefix \enquote{xmp}).
%
% \begin{macro}[int]{\@@_xmp_build_xmp:}
% \begin{macro}[int]{
%   CreatorTool/pdfcreator,
%   BaseUrl/baseurl}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_build_xmp:
  {
%    \end{macrocode}
% The creator
%    \begin{macrocode}
  \@@_xmp_add_packet_line_default:nnee
    {xmp}{CreatorTool}
    {LaTeX}
    { \GetDocumentProperties{hyperref/pdfcreator} }
%    \end{macrocode}
% The baseurl
%    \begin{macrocode}
   \@@_xmp_add_packet_line_default:nnee
     {xmp}{BaseURL}{}
     { \GetDocumentProperties{hyperref/baseurl} }
%    \end{macrocode}
% CreationDate
%    \begin{macrocode}
    \@@_xmp_date_get:nNN
      {document/creationdate}\l_@@_tmpa_tl\l_@@_tmpa_seq
    \@@_xmp_add_packet_line:nne{xmp}{CreateDate}{\@@_xmp_print_date:N\l_@@_tmpa_seq}
    \pdfmanagement_add:nne{Info}{CreationDate}{(\l_@@_tmpa_tl)}
%    \end{macrocode}
% ModifyDate
%    \begin{macrocode}
    \@@_xmp_date_get:nNN
      {document/moddate}\l_@@_tmpa_tl\l_@@_tmpa_seq
    \@@_xmp_add_packet_line:nne{xmp}{ModifyDate}{\@@_xmp_print_date:N\l_@@_tmpa_seq}
    \pdfmanagement_add:nne{Info}{ModDate}{(\l_@@_tmpa_tl)}
%    \end{macrocode}
% MetadataDate
%    \begin{macrocode}
    \@@_xmp_date_get:nNN
      {hyperref/pdfmetadate}\l_@@_tmpa_tl\l_@@_tmpa_seq
    \@@_xmp_add_packet_line:nne{xmp}{MetadataDate}{\@@_xmp_print_date:N\l_@@_tmpa_seq}
%    \end{macrocode}
%    \begin{macrocode}
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsubsection{Standards}
%    The metadata for standards are taken from the |pdfstandard| key
%    of the |document/metadata| family.
%    The values for A-standards are taken from the property, X and UA are currently
%    taken from the document container, this should be changed when merging of
%    standards are possible.
% \begin{macro}{\@@_xmp_build_standards:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_build_standards:
  {
    \@@_xmp_add_packet_line:nne {pdfaid}{part}{\pdfmeta_standard_item:n{level}}
    \@@_xmp_add_packet_line:nne
      {pdfaid}{conformance}{\pdfmeta_standard_item:n{conformance}}
    \int_compare:nNnTF {0\pdfmeta_standard_item:n{level}}<{4}
     {\@@_xmp_add_packet_line:nne {pdfaid}{year} {\pdfmeta_standard_item:n{year}}}
     {\@@_xmp_add_packet_line:nne {pdfaid}{rev}  {\pdfmeta_standard_item:n{year}}}
    \@@_xmp_add_packet_line:nne
      {pdfxid}{GTS_PDFXVersion}{\GetDocumentProperties{document/pdfstandard-X}}
    \pdfmanagement_get_documentproperties:nNT {document/pdfstandard-UA}\l_@@_tmpa_tl
     {
       \@@_xmp_add_packet_line:nne
        {pdfuaid}{part}{\exp_last_unbraced:No\use_i:nn \l_@@_tmpa_tl}
      \@@_xmp_add_packet_line:nne
        {pdfuaid}{rev}{\exp_last_unbraced:No\use_ii:nn \l_@@_tmpa_tl}
     }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Declarations}
% See \url{https://pdfa.org/wp-content/uploads/2019/09/PDF-Declarations.pdf}
%
% \begin{variable}{\g_@@_xmp_pdfd_data_prop}
% This holds the data for declarations.
%    \begin{macrocode}
\prop_new:N \g_@@_xmp_pdfd_data_prop
%    \end{macrocode}
% \end{variable}
% the main building command used in the xmp generation
% \begin{macro}{\@@_xmp_build_pdfd:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_build_pdfd:
  {
    \prop_if_empty:NF\g_@@_xmp_pdfd_data_prop
      {
        \@@_xmp_add_packet_open:nn{pdfd}{declarations}
        \@@_xmp_add_packet_open:nn{rdf}{Bag}
          \prop_map_inline:Nn \g_@@_xmp_pdfd_data_prop
            {
              \@@_xmp_build_pdfd_claim:nn{##1}{##2}
            }
        \@@_xmp_add_packet_close:nn{rdf}{Bag}
        \@@_xmp_add_packet_close:nn{pdfd}{declarations}
      }
  }
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_xmp_build_pdfd_claim:nn}
% This build the xml for one claim. If there is no
% claimData only the conformsTo is output.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_build_pdfd_claim:nn #1#2
 {
    \@@_xmp_add_packet_open_attr:nnn{rdf}{li}{rdf:parseType="Resource"}
      \@@_xmp_add_packet_line:nnn{pdfd}{conformsTo}{#1}
      \tl_if_empty:nF {#2}
       {
         \@@_xmp_add_packet_open:nn{pdfd}{claimData}
          \@@_xmp_add_packet_open:nn{rdf}{Bag}
            #2
          \@@_xmp_add_packet_close:nn{rdf}{Bag}
         \@@_xmp_add_packet_close:nn{pdfd}{claimData}
       }
    \@@_xmp_add_packet_close:nn{rdf}{li}
 }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Photoshop}
% \begin{macro}{\@@_xmp_build_photoshop:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_build_photoshop:
  {
%    \end{macrocode}
% pdfauthortitle/photoshop:AuthorsPosition
%    \begin{macrocode}
   \@@_xmp_add_packet_line:nne{photoshop}{AuthorsPosition}
     { \GetDocumentProperties{hyperref/pdfauthortitle} }
%    \end{macrocode}
% pdfcaptionwriter/photoshop:CaptionWriter
%    \begin{macrocode}
   \@@_xmp_add_packet_line:nne{photoshop}{CaptionWriter}
     { \GetDocumentProperties{hyperref/pdfcaptionwriter} }
%    \end{macrocode}
%    \begin{macrocode}
  }
%    \end{macrocode}
% \end{macro}
%

% \subsection{XMP Media Management}

% \begin{macro}{\@@_xmp_build_xmpMM:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_build_xmpMM:
  {
%    \end{macrocode}
% pdfdocumentid / xmpMM:DocumentID
%    \begin{macrocode}
    \str_set:Ne\l_@@_tmpa_str {\GetDocumentProperties{hyperref/pdfdocumentid}}
    \str_if_empty:NT \l_@@_tmpa_str
      {
        \@@_xmp_create_uuid:nN
          {\jobname\GetDocumentProperties{hyperref/pdftitle}}
          \l_@@_tmpa_str
      }
    \@@_xmp_add_packet_line:nnV{xmpMM}{DocumentID}
      \l_@@_tmpa_str
%    \end{macrocode}
% pdfinstanceid / xmpMM:InstanceID
%    \begin{macrocode}
    \str_set:Ne\l_@@_tmpa_str {\GetDocumentProperties{hyperref/pdfinstanceid}}
    \str_if_empty:NT \l_@@_tmpa_str
      {
        \@@_xmp_create_uuid:nN
          {\jobname\l_@@_xmp_currentdate_tl}
          \l_@@_tmpa_str
      }
    \@@_xmp_add_packet_line:nnV{xmpMM}{InstanceID}
      \l_@@_tmpa_str
%    \end{macrocode}
% pdfversionid/xmpMM:VersionID
%    \begin{macrocode}
   \@@_xmp_add_packet_line:nne{xmpMM}{VersionID}
     { \GetDocumentProperties{hyperref/pdfversionid} }
%    \end{macrocode}
% pdfrendition/xmpMM:RenditionClass
%    \begin{macrocode}
   \@@_xmp_add_packet_line:nne{xmpMM}{RenditionClass}
     { \GetDocumentProperties{hyperref/pdfrendition} }
%    \end{macrocode}
%    \begin{macrocode}
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Rest of dublin Core data}
%
% \begin{macro}[int]{\@@_xmp_build_dc:}
% \begin{macro}[int]{
%  dc:creator/pdfauthor,
%  dc:subject/pdfkeywords,
%  dc:type/pdftype,
%  dc:publisher/pdfpublisher,
%  dc:description/pdfsubject,
%  dc:language/lang/pdflang,
%  dc:identifier/pdfidentifier,
%  photoshop:AuthorsPosition/pdfauthortitle,
%  photoshop:CaptionWriter/pdfcaptionwriter
%  }
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_build_dc:
  {
%    \end{macrocode}
% pdfauthor/dc:creator
%    \begin{macrocode}
    \@@_xmp_add_packet_list_simple:nnne {dc}{creator}{Seq}
       { \GetDocumentProperties{hyperref/pdfauthor} }
    \int_compare:nNnT {0\pdfmeta_standard_item:n{level}}={1}
       { \pdfmanagement_remove:nn{Info}{Author} }
%    \end{macrocode}
% pdftitle/dc:title. This is rather complex as we want to support a list
% with different languages.
%    \begin{macrocode}
    \@@_xmp_add_packet_list:nnne {dc}{title}{Alt}
       { \GetDocumentProperties{hyperref/pdftitle} }
%    \end{macrocode}
% pdfkeywords/dc:subject
%    \begin{macrocode}
    \@@_xmp_add_packet_list_simple:nnne {dc}{subject}{Bag}
       { \GetDocumentProperties{hyperref/pdfkeywords} }
    \int_compare:nNnT {0\pdfmeta_standard_item:n{level}}={1}
       { \pdfmanagement_remove:nn{Info}{Keywords} }
%    \end{macrocode}
% pdftype/dc:type
%    \begin{macrocode}
  \pdfmanagement_get_documentproperties:nNTF { hyperref/pdftype } \l_@@_tmpa_tl
    {
      \@@_xmp_add_packet_list_simple:nnnV {dc}{type}{Bag}\l_@@_tmpa_tl
    }
    {
      \@@_xmp_add_packet_list_simple:nnnn {dc}{type}{Bag}{Text}
    }
%    \end{macrocode}
% pdfpublisher/dc:publisher
%    \begin{macrocode}
   \@@_xmp_add_packet_list_simple:nnne {dc}{publisher}{Bag}
     { \GetDocumentProperties{hyperref/pdfpublisher} }
%    \end{macrocode}
% pdfsubject/dc:description
%    \begin{macrocode}
   \@@_xmp_add_packet_list:nnne
    {dc}{description}{Alt}
    {\GetDocumentProperties{hyperref/pdfsubject}}
%    \end{macrocode}
% lang/pdflang/dc:language
%    \begin{macrocode}
   \@@_xmp_add_packet_list_simple:nnnV
     {dc}{language}{Bag}\l_@@_xmp_doclang_tl
%    \end{macrocode}
% pdfidentifier/dc:identifier
%    \begin{macrocode}
   \@@_xmp_add_packet_line:nne{dc}{identifier}
     { \GetDocumentProperties{hyperref/pdfidentifier} }
%    \end{macrocode}
% pdfdate/dc:date
%    \begin{macrocode}
   \@@_xmp_date_get:nNN {hyperref/pdfdate}\l_@@_tmpa_tl\l_@@_tmpa_seq
    \@@_xmp_add_packet_list_simple:nnne
     {dc}{date}{Seq}{\@@_xmp_print_date:N\l_@@_tmpa_seq}
%    \end{macrocode}
% The file format
%    \begin{macrocode}
   \@@_xmp_add_packet_line:nnn{dc}{format}{application/pdf}
%    \end{macrocode}
% The source
%    \begin{macrocode}
    \@@_xmp_add_packet_line_default:nnee
     {dc}{source}
     { \c_sys_jobname_str.tex }
     { \GetDocumentProperties{hyperref/pdfsource} }
%    \end{macrocode}
%    \begin{macrocode}
    \@@_xmp_add_packet_list:nnne{dc}{rights}{Alt}
     {\GetDocumentProperties{hyperref/pdfcopyright}}
%    \end{macrocode}
%    \begin{macrocode}
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsection{xmpRights}
% \begin{macro}{\@@_xmp_build_xmpRights:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_build_xmpRights:
  {
    \@@_xmp_add_packet_line:nne
      {xmpRights}
      {WebStatement}
      {\GetDocumentProperties{hyperref/pdflicenseurl}}
    \@@_xmp_add_packet_line:nne
      {xmpRights}
      {Marked}
      {
       \str_case:en {\GetDocumentProperties{document/copyright}}
        {
          {true}{True}
          {false}{False}
        }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{IPTC}
% We want the block and also the resources only if they are actually used. So we pack
% them first in a local variable
% \begin{variable}{\l_@@_xmp_iptc_data_tl}
%    \begin{macrocode}
\tl_new:N\l_@@_xmp_iptc_data_tl
%    \end{macrocode}
% \end{variable}
% \begin{macro}{\@@_xmp_build_iptc_data:N}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_build_iptc_data:N #1
  {
     \tl_clear:N #1
     \@@_xmp_incr_indent:\@@_xmp_incr_indent:\@@_xmp_incr_indent:\@@_xmp_incr_indent:
     \@@_xmp_add_packet_line:nneN
       {Iptc4xmpCore}{CiAdrExtadr}
       {\GetDocumentProperties{hyperref/pdfcontactaddress}}
       #1
     \@@_xmp_add_packet_line:nneN
       {Iptc4xmpCore}{CiAdrCity}
       {\GetDocumentProperties{hyperref/pdfcontactcity}}
       #1
     \@@_xmp_add_packet_line:nneN
       {Iptc4xmpCore}{CiAdrPcode}
       {\GetDocumentProperties{hyperref/pdfcontactpostcode}}
       #1
     \@@_xmp_add_packet_line:nneN
       {Iptc4xmpCore}{CiAdrCtry}
       {\GetDocumentProperties{hyperref/pdfcontactcountry}}
       #1
     \@@_xmp_add_packet_line:nneN
       {Iptc4xmpCore}{CiTelWork}
       {\GetDocumentProperties{hyperref/pdfcontactphone}}
       #1
     \@@_xmp_add_packet_line:nneN
       {Iptc4xmpCore}{CiEmailWork}
       {\GetDocumentProperties{hyperref/pdfcontactemail}}
       #1
     \@@_xmp_add_packet_line:nneN
       {Iptc4xmpCore}{CiUrlWork}
       {\GetDocumentProperties{hyperref/pdfcontacturl}}
       #1
     \@@_xmp_decr_indent:\@@_xmp_decr_indent:\@@_xmp_decr_indent:\@@_xmp_decr_indent:
  }
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_xmp_build_iptc:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_build_iptc:
  {
    \tl_if_empty:NF\l_@@_xmp_iptc_data_tl
     {
       \@@_xmp_add_packet_open_attr:nnn
        {Iptc4xmpCore}{CreatorContactInfo}{rdf:parseType="Resource"}
       \tl_gput_right:Ne\g_@@_xmp_packet_tl { \l_@@_xmp_iptc_data_tl }
       \@@_xmp_add_packet_close:nn
        {Iptc4xmpCore}{CreatorContactInfo}
    }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Prism}
% \begin{macro}[int]{\@@_xmp_build_prism:}
% \begin{macro}[int]
%  {
%   complianceProfile,
%   prism:subtitle/pdfsubtitle,
%  }
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_build_prism:
  {
%    \end{macrocode}
% The compliance profile is a fix value taken from \pkg{hyperxmp}
%    \begin{macrocode}
    \@@_xmp_add_packet_line:nnn
      {prism}{complianceProfile}
      {three}
%    \end{macrocode}
% the next two values can take an optional language argument.
% First subtitle
%    \begin{macrocode}
    \@@_xmp_lang_get:eNN
     {\GetDocumentProperties{hyperref/pdfsubtitle}}
     \l_@@_tmpa_tl\l_@@_tmpb_tl
    \@@_xmp_add_packet_line_attr:nneV
      {prism}{subtitle}
      {xml:lang="\l_@@_tmpa_tl"}
      \l_@@_tmpb_tl
%    \end{macrocode}
% Then publicationName
%    \begin{macrocode}
    \@@_xmp_lang_get:eNN
     {\GetDocumentProperties{hyperref/pdfpublication}}
     \l_@@_tmpa_tl\l_@@_tmpb_tl
    \@@_xmp_add_packet_line_attr:nneV
      {prism}{publicationName}
      {xml:lang="\l_@@_tmpa_tl"}
      \l_@@_tmpb_tl
%    \end{macrocode}
% Now the rest
%    \begin{macrocode}
    \@@_xmp_add_packet_line:nne
      {prism}{bookEdition}
      {\GetDocumentProperties{hyperref/pdfbookedition}}
    \@@_xmp_add_packet_line:nne
      {prism}{aggregationType}
      {\GetDocumentProperties{hyperref/pdfpubtype}}
    \@@_xmp_add_packet_line:nne
      {prism}{volume}
      {\GetDocumentProperties{hyperref/pdfvolumenum}}
    \@@_xmp_add_packet_line:nne
      {prism}{number}
      {\GetDocumentProperties{hyperref/pdfissuenum}}
    \@@_xmp_add_packet_line:nne
      {prism}{pageRange}
      {\GetDocumentProperties{hyperref/pdfpagerange}}
    \@@_xmp_add_packet_line:nne
      {prism}{issn}
      {\GetDocumentProperties{hyperref/pdfissn}}
    \@@_xmp_add_packet_line:nne
      {prism}{eIssn}
      {\GetDocumentProperties{hyperref/pdfeissn}}
    \@@_xmp_add_packet_line:nne
      {prism}{doi}
      {\GetDocumentProperties{hyperref/pdfdoi}}
    \@@_xmp_add_packet_line:nne
      {prism}{url}
      {\GetDocumentProperties{hyperref/pdfurl}}
%    \end{macrocode}
% The page count is take from the previous run or from
% pdfnumpages.
%    \begin{macrocode}
     \tl_set:Ne \l_@@_tmpa_tl { \GetDocumentProperties{hyperref/pdfnumpages} }
     \@@_xmp_add_packet_line:nne
      {prism}{pageCount}
      {\tl_if_blank:VTF \l_@@_tmpa_tl {\PreviousTotalPages}{\l_@@_tmpa_tl}}
%    \end{macrocode}
%    \begin{macrocode}
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsubsection{User additions}
%
% \begin{variable}{\g_@@_xmp_user_packet_str }
%    \begin{macrocode}
\tl_new:N \g_@@_xmp_user_packet_tl
%    \end{macrocode}
% \end{variable}
% \begin{macro}{\@@_xmp_build_user: }
%    \begin{macrocode}
\cs_new_protected:Npn \@@_xmp_build_user:
 {
   \int_zero:N \l_@@_xmp_indent_int
   \g_@@_xmp_user_packet_tl
   \int_set:Nn \l_@@_xmp_indent_int {3}
 }
%    \end{macrocode}
% \end{macro}
% \subsection{Activating the metadata}
% We don't try to get the byte count. So we can put everything
% in the |shipout/lastpage| hook
%    \begin{macrocode}
\hook_new:n { pdfmeta/xmp }
\AddToHook{shipout/lastpage}
  {
    \bool_if:NT\g_@@_xmp_bool
     {
       \str_if_exist:NTF\c_sys_timestamp_str
        {
          \tl_set_eq:NN \l_@@_xmp_currentdate_tl \c_sys_timestamp_str
        }
        {
          \file_get_timestamp:nN{\jobname.log}\l_@@_xmp_currentdate_tl
        }
       \@@_xmp_date_split:VN\l_@@_xmp_currentdate_tl\l_@@_xmp_currentdate_seq
       \hook_use:n { pdfmeta/xmp }
       \@@_xmp_build_packet:
       \pdf_object_new:n {__pdfmeta/xmp}
       \exp_args:No
       \__pdf_backend_metadata_stream:n {\g_@@_xmp_packet_tl}
        \pdfmanagement_add:nne {Catalog} {Metadata}{\pdf_object_ref:n{__pdfmeta/xmp}}
       \bool_if:NT \g_@@_xmp_export_bool
        {
          \iow_open:Nn\g_tmpa_iow{\g_@@_xmp_export_str.xmpi}
          \exp_args:NNo\iow_now:Nn\g_tmpa_iow{\g_@@_xmp_packet_tl}
          \iow_close:N\g_tmpa_iow
        }
     }
  }
%    \end{macrocode}

% \subsection{User commands}

% \begin{macro}{\pdfmeta_xmp_add:n }
%    \begin{macrocode}
\cs_new_protected:Npn \pdfmeta_xmp_add:n #1
  {
    \tl_gput_right:Nn \g_@@_xmp_user_packet_tl
      {
        \@@_xmp_add_packet_chunk:n { #1 }
      }
  }
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\pdfmeta_xmp_xmlns_new:nn }
%    \begin{macrocode}
\cs_new_protected:Npn \pdfmeta_xmp_xmlns_new:nn #1 #2
  {
    \prop_if_in:NnTF \g_@@_xmp_xmlns_prop {#1}
      {\msg_warning:nnnn{pdfmeta}{xmp-defined}{xmlns~namespace}{#1}}
      {\@@_xmp_xmlns_new:nn {#1}{#2}}
  }
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\pdfmeta_xmp_schema_new:nnn}
%    \begin{macrocode}
\cs_set_eq:NN \pdfmeta_xmp_schema_new:nnn \@@_xmp_schema_new:nnn
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\pdfmeta_xmp_property_new:nnnnn}
%    \begin{macrocode}
\cs_set_eq:NN \pdfmeta_xmp_property_new:nnnnn \@@_xmp_property_new:nnnnn
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\pdfmeta_xmp_add_declaration:n,\pdfmeta_xmp_add_declaration:e}
%    \begin{macrocode}
\cs_new_protected:Npn \pdfmeta_xmp_add_declaration:n #1 %conformsTo uri
 {
   \@@_xmp_schema_enable_pdfd:
   \prop_gput:Nnn\g_@@_xmp_pdfd_data_prop{#1}{}
 }
\cs_generate_variant:Nn \pdfmeta_xmp_add_declaration:n {e}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\pdfmeta_xmp_add_declaration:nnnnn,\pdfmeta_xmp_add_declaration:ennnn}
%    \begin{macrocode}
\cs_new_protected:Npn \pdfmeta_xmp_add_declaration:nnnnn #1#2#3#4#5
 %#1=conformsTo uri, #2 claimBy, #3 claimDate #4 claimCredentials #4 claimReport
 {
   \@@_xmp_schema_enable_pdfd:
   \tl_set:Nn \l_@@_tmpa_tl
     {
       \@@_xmp_add_packet_open_attr:nnn{rdf}{li}{rdf:parseType="Resource"}
       \@@_xmp_add_packet_line:nnn{pdfd}{claimBy}{#2}
       \@@_xmp_add_packet_line:nnn{pdfd}{claimDate}{#3}
       \@@_xmp_add_packet_line:nnn{pdfd}{claimCredentials}{#4}
       \@@_xmp_add_packet_line:nnn{pdfd}{claimReport}{#5}
       \@@_xmp_add_packet_close:nn{rdf}{li}
     }
   \prop_get:NnNT \g_@@_xmp_pdfd_data_prop {#1}\l_@@_tmpb_tl
    {
      \tl_concat:NNN \l_@@_tmpa_tl \l_@@_tmpa_tl \l_@@_tmpb_tl
    }
   \prop_gput:Nno\g_@@_xmp_pdfd_data_prop{#1}
       {
         \l_@@_tmpa_tl
       }
 }
\cs_generate_variant:Nn\pdfmeta_xmp_add_declaration:nnnnn {e,eee}
%    \end{macrocode}
% \end{macro}
%
% \subsection{Default declarations}
% The two declarations will be required quite often with ua-2, so we
% provide some interface.

% \begin{macro}{\@@_xmp_wtpdf_reuse_declaration:,\@@_xmp_wtpdf_accessibility_declaration:}
%    \begin{macrocode}
\cs_new:Npn \@@_xmp_iso_today:
  {
    \int_use:N\c_sys_year_int-
    \int_compare:nNnT {\c_sys_month_int} < {10}{0} \int_use:N\c_sys_month_int -
    \int_compare:nNnT {\c_sys_day_int}   < {10}{0} \int_use:N\c_sys_day_int
  }
\cs_new_protected:Npn \@@_xmp_wtpdf_reuse_declaration:
 {
   \pdfmeta_xmp_add_declaration:eeenn
      {http://pdfa.org/declarations/wtpdf\c_hash_str reuse1.0}
      {LaTeX~Project}
      {\@@_xmp_iso_today:}{}{}
 }
\cs_new_protected:Npn \@@_xmp_wtpdf_accessibility_declaration:
 {
   \pdfmeta_xmp_add_declaration:ennnn
     {http://pdfa.org/declarations/wtpdf\c_hash_str accessibility1.0}
     {LaTeX~Project}
     {\@@_xmp_iso_today:}{}{}
 }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \end{implementation}
%
% \PrintIndex
