% \iffalse meta-comment
%
%% File: l3pdftools.dtx
%
% Copyright (C) 2018-2026 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}
\usepackage{array,booktabs}
\hypersetup{pdfauthor=The LaTeX Project,pdftitle=l3pdftools (LaTeX PDF management bundle)}

\providecommand\potentialclash{\noindent\llap{\dbend\ }}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \title{^^A
%   The \pkg{l3pdftools} module\\ temporary collection of pdf related commands ^^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.96z, released 2026-04-15}
%
% \maketitle
% \begin{documentation}
%
% \section{\pkg{l3pdftools} documentation}
%
% This module collects a number of candidate commands for the l3pdf module
%
% \begin{function}[added=2026-02-18]
%   {\pdf_purify:nN}
%  \begin{syntax}
%   \cs{pdf_purify:nN} \Arg{content} \Arg{tl var}
%  \end{syntax}
%
%  This command calls \cs{text_purify:n} for \meta{content} and stores the result
%  in \meta{tl var}. It performs first various setups to make the conversion suitable
%  for a PDF context, most importantly for the bookmarks. Beside other
%  it executes the hyperref command \cs{pdfstringdefPreHook}, that means that it knows the
%  exceptions added by \cs{pdfstringdefDisableCommands}. It also sets \cs{Hy@pdfstringtrue},
%  this means that if the content uses \cs{texorpdfstring} the pdf branch is used.
%
%  At the begin the command calls the hook \texttt{pdf/purify/setup} which can be used to
%  add more definitions and exceptions.
%
%  While the implementation is quite different the command has a similar purpose as
%  \cs{pdfstringdef}. The most notable difference from a user viewpoint is that it doesn't directly
%  encode the result as a PDF string, typically a call of \cs{pdf_purify:nN}
%  should be followed with a call of \cs{pdf_string_from_unicode:nnN}.
%
%
% \end{function}
%
% \begin{function}[EXP,added=2021-02-14]
%   {\pdf_name_from_unicode_e:n,\pdf_name_from_unicode_e:V}
%  \begin{syntax}
%   \cs{pdf_name_from_unicode_e:n} \Arg{content}
%  \end{syntax}
%  This converts \meta{content} to a format suitable for a PDF Name.
%  The output depends on the backend: For almost all backends
%  it will first expand the content with \cs{text_expand:n} and
%  then escape it in the way needed in a PDF Name with
%  \cs{str_convert_pdfname:e}, and at last prepend a slash before.
%  Typically such names use only ascii,
%  but non-ascii is supported, but should be utf8 encoded. For example\\
%  |\pdf_name_from_unicode_e:n {A~B\c_percent_str C\c_hash_str D€}}|\\
%   will    output |/A#20B#25C#23D#E2#82#AC|.
%
%  With dvips it will expand the content with \cs{text_expand:n} and then wrap it
%  in a |cvn| operation (\enquote{convert to name}).
%  So the example above will output |(A B%C#D€) cvn| to
%  the postscript. The content should not contain unbalanced parentheses with dvips.
%
% \end{function}
% \begin{function}[added=2020-07-04]
%   {\pdf_string_from_unicode:nnN,\pdf_string_from_unicode:nVN}
%  \begin{syntax}
%   \cs{pdf_string_from_unicode:nnN} \Arg{format} \Arg{content} \meta{tl var}
%  \end{syntax}
%  This converts \meta{content} following the rules defined by \meta{format} and stores
%  the result in \meta{tlvar}. The assignment is done locally.
%  Non-ascii input should be utf8 encoded.
%  Currently the following formats exist:
%  \begin{description}
%     \item[utf8/string-raw]
%      this converts with \cs{str_set_convert:Nnnn} into utf8/string.
%     \item[utf8/string]
%      this converts into utf8/string and adds parentheses around the result.%
%     \item[utf8/URI-raw]
%      this converts with \cs{str_set_convert:Nnnn} into utf8/url and
%      then replaces reserved and digits back from the percent encoding. Parentheses
%      are escaped.
%     \item[utf8/URI]
%      this converts into utf8/URI  and adds parentheses around the result.%
%     \item[utf16/string-raw]
%      this converts with \cs{str_set_convert:Nnnn} into utf16/string.
%     \item[utf16/string]
%      this converts into utf16/string and adds parentheses around the result.
%     \item[utf16/hex-raw]
%      this converts into utf16/hex
%     \item[utf16/hex]
%     this converts into utf16/hex and adds bracket around the result.
%   \end{description}
% \end{function}
%
% \subsection{BDC operator / Properties resource}
% \begin{NOTE}{UF}
%  we need a switch for the case that the resource should be added to
%  xform resource instead of a page resources, see pdfbase.sty
% - xdvipdfmx: looks fine, the resource is added to the xform resource automatically
% - pdftex should now work okay too
% \end{NOTE}
% Entries to the /Properties dictionary in the page resources can
% be added with dvips only through side-effects: if a BDC-mark is created
% dvips/ghostscript will automatically create the necessary objects and names.
% To get a sensible abstraction the code does for some of the following
% command the same for the other backends if the
% core management code has been activated. This means that the behaviour
% of the command is different then. The \cs{pdf_bdcobject:..} should only be used
% if the management is active.
%
%
% \begin{function}[updated = 2024-10-23]
%   {
%     \pdf_bdc:nn, \pdf_bdc:ee
%   }
%   \begin{syntax}
%     \cs{pdf_bdc:nn} \Arg{tag} \Arg{dictionary content}
%   \end{syntax}
%   This command adds a BDC marked content operator to the current page stream.
%   \meta{tag} is the tag of this operator (without the leading slash),
%   \meta{dictionary content} is the content of the second argument.
%   With the exception of the dvips backend,
%   the dictionary content is added inline in the stream. Such an inline BDC
%   is typically better for ActualText additions as some PDF reader ignore
%   entries given in properties.
% \end{function}
%
% \begin{function}[added = 2023-08-18]
%   {
%     \pdf_bdc_shipout:ee
%   }
%   \begin{syntax}
%     \cs{pdf_bdc_shipout:ee} \Arg{tag} \Arg{dictionary content}
%   \end{syntax}
%    This command adds a BDC marked content operator to the current page stream.
%   \meta{tag} is the tag of this operator (without the leading slash),
%   \meta{dictionary content} is the content of the second argument.
%
%   Differently to \cs{pdf_bdc:ee} the arguments are not expanded when the
%   command is \emph{used}, but only at \emph{shipout}.
%   This requires new engines which
%   allow to use the keyword \texttt{shipout} with the primitive
%   \cs{special} and \cs{pdfliteral}. Also similar to \cs{pdf_bdc:ee}
%   the content of \meta{dictionary content} is added inline in the stream
%   with most engines (not on the dvips + ps2pdf route).
%   This means that this command can also be used if such an inline dictionary is preferred.
%
%   The command requires current engines and
%   is not defined if an too old engine is detected!
%
% \end{function}
%
% \begin{function}[added = 2020-07-03]
%   {
%     \pdf_bdcobject:nn
%   }
%   \begin{syntax}
%     \cs{pdf_bdcobject:nn} \Arg{tag} \Arg{object name}
%   \end{syntax}
%   This command adds a BDC marked content operator to the current page stream.
%   \meta{tag} is the tag of this operator (without the leading slash),
%   \meta{object name}  is a the name of an dictionary object reserved with
%   \cs{pdf_object_new:n} and filled with \cs{pdf_object_write:nnn} with
%   the properties of the BDC. Reusing a predefined object can save space
%   but the command works correctly
%   only if the resources management has been activated and should be used only
%   if this can be ensured.
% \end{function}
% \begin{function}[updated = 2020-07-03]
%   {
%     \pdf_bdcobject:n
%   }
%   \begin{syntax}
%     \cs{pdf_bdcobject:n} \Arg{tag}
%   \end{syntax}
%    This command adds a BDC marked content operator to the current page stream.
%   \meta{tag} is the tag of this operator (without the leading slash).
%   As object this commands uses the last anonymous dictionary object created with
%   \cs{pdf_object_unnamed_write:nn}. It lies in the responsibility of the user that the last
%   object is the wanted one. Like with \cs{pdf_bdcobject:nn} the command works correctly
%   only if the resources management has been activated and should be used only
%   if this can be ensured.
% \end{function}
% \begin{function}[added = 2019-10-17]
%   {
%     \pdf_bmc:n
%   }
%   \begin{syntax}
%     \cs{pdf_bmc:n} \Arg{tag}
%   \end{syntax}
%    This command created a BMC marked content operator. The argument is the
%    tag without the leading slash. It can be e.g. used for simple artifact
%    markers.
% \end{function}
% \begin{function}[added = 2019-06-30]
%   {
%     \pdf_emc:
%   }
%   \begin{syntax}
%     \cs{pdf_emc:}
%   \end{syntax}
%    This command closes the  BDC marked content operator opened with \cs{pdf_bdc:nn}.
%    It should be on the same page as the bdc-command.
%
%   \begin{verbatim}
%   \pdf_object_new:n     {module/objA}
%   \pdf_object_write:nnn {module/objA}{dict}{/Type/Artifact}
%   \pdf_bdc:nn {Span}{module/objA}
%   text
%   \pdf_emc:
%   \end{verbatim}
% \end{function}
%
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{l3pdftools} implementation}
%
%    \begin{macrocode}
%<*header>
\ProvidesExplPackage{l3pdftools}{2026-04-15}{0.96z}
  {candidate commands for l3pdf---LaTeX PDF management bundle}
%</header>
%    \end{macrocode}
%
%    \begin{macrocode}
%<@@=pdf>
%<*package>
%    \end{macrocode}
% \subsection{Conversions and export functions}
% \begin{macro}{\pdf_name_from_unicode_e:n,\pdf_name_from_unicode_e:V}
%    \begin{macrocode}
\cs_generate_variant:Nn \str_convert_pdfname:n { e }

\cs_new:Npn \pdf_name_from_unicode_e:n #1
  {
    \__kernel_pdf_name_from_unicode_e:n { #1 }
  }

\cs_generate_variant:Nn \pdf_name_from_unicode_e:n {V}
%    \end{macrocode}
% \end{macro}
%
% The convert command must use a different value the source encoding
% depending on the engines. Until the PR in str-convert is active we add the alias here
% too
%    \begin{macrocode}
\bool_lazy_any:nTF
  {
    \sys_if_engine_luatex_p:
    \sys_if_engine_xetex_p:
  }
  {
    \prop_gput:Nnn \g__str_alias_prop { default } {  }
  }
  {
    \prop_gput:Nnn \g__str_alias_prop { default } { utf8 }
  }
%    \end{macrocode}
% \begin{macro}{\pdf_string_from_unicode:nnN}
%    \begin{macrocode}
\msg_new:nnn {pdfmanagement}{ unknown-convert}
 {
   Unknown~string~conversion~method~'#1'!
 }
\cs_new:Npn \pdf_string_from_unicode:nnN #1 #2 #3
  {
    \cs_if_exist_use:cF { @@_string_from_unicode_#1:nN }
      {
        \msg_error:nnn { pdf } { unknown-convert } {#1}
        \use_none:nn
      }
    { #2 } #3
  }

\cs_generate_variant:Nn \pdf_string_from_unicode:nnN {nVN}
%    \end{macrocode}
% \end{macro}
% Most converter are simply wrapper around the str-convert commands and so
% use the same names, with the addition raw if no delimiters are added.
% The exception is the one for url's: it reverts most of the percent encodings
% and escapes the parentheses.
% That's why its name is URI instead of url. The current code is probably quite
% slow and will need a replacement.
% \begin{macro}{ @@_string_from_unicode_utf8/string-raw:nN }
% \begin{macro}{ @@_string_from_unicode_utf8/string:nN }
% \begin{macro}{ @@_string_from_unicode_utf8/URI-raw:nN }
% \begin{macro}{ @@_string_from_unicode_utf8/URI:nN }
% \begin{macro}{ @@_string_from_unicode_utf16/string-raw:nN }
% \begin{macro}{ @@_string_from_unicode_utf16/string:nN }
% \begin{macro}{ @@_string_from_unicode_utf16/hex-raw:nN }
% \begin{macro}{ @@_string_from_unicode_utf16/hex:nN }
%    \begin{macrocode}
%% TODO Names need a review when it is clear which converters
%% are actually needed
%% string conversions and printing
%% we assume here that the text purify step has been done. The input is
%% a list of (utf8) chars.
%% str convert, not expandable.
%  filespec (attachment view)  tests:
%  utf8:  gr\303\274\303\237e.txt
%    %doesn't work, umlaut wrong,
%  utf8 with BOM \357\273\277gr\303\274\303\237e.txt
%    %doesn't work, umlaut wrong, bom visible
%  utf16 with BE: (FEFF)
%   \376\377\000g\000r\000\374\000\337\000e\000.\000t\000x\000t %works
%                xetex converts to <feff0067007200fc00df0065002e007400780074>
%  utf16 with BE / HEX: <FEFF0067007200FC00DF0065002E007400780074> works

% bookmarks: as pdfoutline uses () currently only utf16 with BE is usable.
% check if one can use HEX too when directly writing the object
% ==========
% uri: utf16BE/string seems not to work, hex neither
%      utf8/string works but not on macos,
%      so a specific utf8/url variant is needed
% ==========
% "input" is utf8 for pdftex, empty (native) for unicode engine
% commands to output literal strings  (...)

\cs_new_protected:cpn { @@_string_from_unicode_utf8/string-raw:nN }  #1 #2
  {
     \str_set_convert:Nnnn #2
        { #1 }
        { default }
        {utf8/string}
  }

\cs_new_protected:cpn { @@_string_from_unicode_utf8/string:nN } #1 #2
  {
    \use:c { @@_string_from_unicode_utf8/string-raw:nN } { #1 } #2
    \str_put_left:Nn #2  {(}
    \str_put_right:Nn #2 {)}
  }
% special url command:
\cs_new_protected:cpx { @@_string_from_unicode_utf8/URI-raw:nN }  #1 #2
  {
     \exp_not:N \str_set_convert:Nnnn #2
       { #1 }
       { default }
       {utf8/url}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 3A} {:}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 2F} {/}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 23} {\c_hash_str}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 5B} {[}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 5D} {]}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 40} {\c_atsign_str}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 21} {!}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 24} {\c_dollar_str}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 26} {\c_ampersand_str}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 27} {'}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 2A} {*}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 2B} {+}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 2C} {,}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 3B} {;}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 3D} {=}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 3F} {?}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 30} {0}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 31} {1}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 32} {2}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 33} {3}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 34} {4}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 35} {5}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 36} {6}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 37} {7}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 38} {8}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 39} {9}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 28} {\c_backslash_str(}
     \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 29} {\c_backslash_str)}
  }

\cs_new_protected:cpn { @@_string_from_unicode_utf8/URI:nN } #1 #2
  {
     \use:c { @@_string_from_unicode_utf8/URI-raw:nN } {#1} #2
     \str_put_left:Nn #2  {(}
     \str_put_right:Nn #2 {)}
  }
% with utf16 with BE marker
\cs_new_protected:cpn { @@_string_from_unicode_utf16/string-raw:nN }  #1 #2
  {
    \str_set_convert:Nnnn #2
      { #1 }
      { default }
      {utf16/string}
  }

\cs_new_protected:cpn { @@_string_from_unicode_utf16/string:nN } #1 #2
  {
    \use:c { @@_string_from_unicode_utf16/string-raw:nN } {#1} #2
    \str_put_left:Nn #2  {(}
    \str_put_right:Nn #2 {)}
  }

\cs_new_protected:cpn { @@_string_from_unicode_utf16/hex-raw:nN } #1 #2
  {
     \str_set_convert:Nnnn #2
       { #1 }
       { default }
       {utf16/hex}
  }

\cs_new_protected:cpn { @@_string_from_unicode_utf16/hex:nN } #1 #2
  {
    \use:c { @@_string_from_unicode_utf16/hex-raw:nN } {#1} #2
    \str_put_left:Nn #2  {<}
    \str_put_right:Nn #2 {>}
  }

%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \subsection{A \cs{pdfstringdef} replacement}
%
% The following definitions are intended to replace the \cs{pdfstringdef} command of hyperref.
% Like \cs{pdfstringdef} they take an input which can contain various \LaTeX-commands,
% sets up a number of sensible replacements and then outputs into a variable something
% that can be used in a PDF string.
%
% \subsubsection{TeX or PDF?}
% We need a \enquote{TeX  or PDF} command, we use for now the conditional and definition
% of hyperref but this should perhaps be changed.
%    \begin{macrocode}
\newif\ifHy@pdfstring
\def\texorpdfstring{%
   \ifHy@pdfstring
     \expandafter\@secondoftwo
   \else
     \expandafter\@firstoftwo
   \fi
}
%    \end{macrocode}
%
% \subsubsection{The main conversion command}
%
% \subsubsection{Converting text}
% The title text of the bookmark and other strings must be converted to strings
% suitable for a PDF. With hyperref one can use |\pdfstringdef| but
% we also provide a native version. This follows the implementation
% in the generic hyperref driver, the hyperref driver should in the end use this
% implementation too.
%
% The conversion has to do two things: purifying the input and then converting it
% into a PDF string. |\pdfstringdef| does both together but we split up the task.
% This allows to post process the purified text, it also allows to use more than one
% conversion command.
%
% \begin{variable}{\l_@@_purify_tmpa_tl,\g_@@_purify_tmpa_str}
% Temporary variables
%    \begin{macrocode}
\tl_new:N\l_@@_purify_tmpa_tl
\str_new:N\g_@@_purify_tmpa_str
%    \end{macrocode}
% \end{variable}
%
% A socket which should contain the setup definitions.
% A different plug should at the end call the hook \texttt{pdf/purify/setup}
% (if user settings should be used too).
%    \begin{macrocode}
\socket_new:nn{pdf/purify/setup}{0}
\socket_new_plug:nnn { pdf/purify/setup } { kernel}
 {
   \@@_purify_equivalents:
   \hook_use:n {pdf/purify/setup}
 }
\socket_assign_plug:nn { pdf/purify/setup } { kernel}
%    \end{macrocode}
% The hook inside the setup socket. By default it contains the hyperref
% hook:
%    \begin{macrocode}
\hook_new:n{pdf/purify/setup}
\providecommand\pdfstringdefPreHook{}
\hook_gput_code:nnn{pdf/purify/setup}{pdf}{\pdfstringdefPreHook}
%    \end{macrocode}
%
% A socket which should contain the code to expand/purify the content.
% The arguments are the content and a string variable for the result.
%    \begin{macrocode}
\socket_new:nn {pdf/purify} {2}
\socket_new_plug:nnn {pdf/purify} {kernel}
 {
%    \end{macrocode}
% Note: currently \cs{text_expand:n} does not remove a \cs{protect} command
% and does not expand a following token even if it has a declared replacement.
% But in PDF strings we want to expand as much as possible, and
% e.g. should be able to remove/format the |\numberline| command in a bookmark.
%    \begin{macrocode}
   \str_set:Ne #2 {\text_purify:n { #1 } }
 }
\socket_assign_plug:nn{pdf/purify}{kernel}
%    \end{macrocode}
% A hook for final cleanup, currently it contains nothing.
% It gets as argument a string variable and so can adapt that.
%    \begin{macrocode}
\hook_new_with_args:nn {pdf/purify/after}{1}
%    \end{macrocode}
%
% \begin{macro}{\pdf_purify:nN,\@@_purify:nN}
% \cs{pdf_purify:nN} runs all the purifying steps but does
% not convert to a PDF string. It is meant to produce input for
% commands doing the conversion later.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_purify:nN #1 #2 %#1 content #2 str-var
 {
   \group_begin:
   \socket_use:n {pdf/purify/setup}
   \socket_use:nnn {pdf/purify} {#1} {#2}
   \hook_use:nnw {pdf/purify/after} {1} {#2}
   \str_gset_eq:NN \g_@@_purify_tmpa_str #2
   \group_end:
   \str_set_eq:NN #2 \g_@@_purify_tmpa_str
 }
\cs_set_eq:NN \pdf_purify:nN\@@_purify:nN
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Setup: replacement definition for pdf strings}
% Similar to |\pdfstringdef| we need to declare various equivalents
%    \begin{macrocode}
\cs_new_protected:Npn \@@_purify_equivalents:
 {
   \Hy@pdfstringtrue
   \text_declare_expand_equivalent:Nn\MakeUppercase{\@@_purify_makeuppercase:n}
   \text_declare_expand_equivalent:Nn\MakeLowercase{\@@_purify_makelowercase:n}
   \cs_set:Npn\dots{…} %U+2026
   \cs_set:Npn\ldots{…}
   \cs_set:Npn\P{¶}
   \cs_set:Npn\TeX{TeX}
   \cs_set:Npn\LaTeX{LaTeX}
   \cs_set:Npn\LaTeXe{LaTeX2ε}
   \cs_set:Npn\SliTeX{Sli\TeX}%
   \cs_set:Npn\MF{Metafont}%
   \cs_set:Npn\MP{Metapost}%
   \cs_set_eq:NN\phantom\use_none:n
   \cs_set_eq:NN\vphantom\use_none:n
   \cs_set_eq:NN\hphantom\use_none:n
   \cs_set_eq:NN\mathversion\use_none:n
   % \ding ?
   % \Cube ?
   % \begin ?
   % \end ?
%    \end{macrocode}
% Font commands.
%    \begin{macrocode}
   \cs_set_eq:NN\emph\use_i:n
   \cs_set_eq:NN\em\prg_do_nothing:
   \cs_set_eq:NN\textnormal\use_i:n
   \cs_set_eq:NN\textrm\use_i:n
   \cs_set_eq:NN\textsf\use_i:n
   \cs_set_eq:NN\texttt\use_i:n
   \cs_set_eq:NN\textbf\use_i:n
   \cs_set_eq:NN\textmd\use_i:n
   \cs_set_eq:NN\textit\use_i:n
   \cs_set_eq:NN\textsc\use_i:n
   \cs_set_eq:NN\textsl\use_i:n
   \cs_set_eq:NN\textup\use_i:n
   \cs_set_eq:NN\normalfont\prg_do_nothing:
   \cs_set_eq:NN\rmfamily\prg_do_nothing:
   \cs_set_eq:NN\sffamily\prg_do_nothing:
   \cs_set_eq:NN\ttfamily\prg_do_nothing:
   \cs_set_eq:NN\bfseries\prg_do_nothing:
   \cs_set_eq:NN\mdseries\prg_do_nothing:
   \cs_set_eq:NN\itshape\prg_do_nothing:
   \cs_set_eq:NN\scshape\prg_do_nothing:
   \cs_set_eq:NN\slshape\prg_do_nothing:
   \cs_set_eq:NN\upshape\prg_do_nothing:
   \cs_set_eq:NN\Huge\prg_do_nothing:
   \cs_set_eq:NN\LARGE\prg_do_nothing:
   \cs_set_eq:NN\Large\prg_do_nothing:
   \cs_set_eq:NN\footnotesize\prg_do_nothing:
   \cs_set_eq:NN\huge\prg_do_nothing:
   \cs_set_eq:NN\large\prg_do_nothing:
   \cs_set_eq:NN\normalsize\prg_do_nothing:
   \cs_set_eq:NN\scriptsize\prg_do_nothing:
   \cs_set_eq:NN\small\prg_do_nothing:
   \cs_set_eq:NN\tiny\prg_do_nothing:
%    \end{macrocode}
%  Spaces
%    \begin{macrocode}
   \cs_set_eq:NN\quad\space
   \cs_set_eq:NN\qquad\space
   \cs_set_eq:NN\hspace\@@_purify_hspace:w
%    \end{macrocode}
% Colors. This handles only a few commands.
% But pdfstringdef ignores color completely, so probably not
% a practical problem.
%    \begin{macrocode}
   \cs_set_eq:NN\textcolor\use_ii:nn
   \cs_set_eq:NN\color \@gobble@om
   \cs_set_eq:NN\label \@gobble@om
   \cs_set_eq:NN\index \@gobble@som
   \cs_set_eq:NN\glossary \@gobble@om
   \cs_set_eq:NN\ref\@@_purify_ref:w
   \cs_set_eq:NN\pageref\@@_purify_pageref:w
   \cs_set_eq:NN\nameref\@@_purify_nameref:w
   \cs_set_eq:NN\autoref\HyPsd@autoref
   \let\@mkboth\@gobbletwo
   % what to do with all the \textxxx commands?
   \text_declare_expand_equivalent:Nn\textparagraph{¶}
   \text_declare_expand_equivalent:Nn\textdollar{\$}
 }
%    \end{macrocode}
%
% Replacements for reference commands
%    \begin{macrocode}
\cs_new:Npn\@@_purify_nameref:n #1
  {\cs_if_exist:cTF {r@#1}{\tl_item:cn{r@#1}{3}}{0}}
\DeclareExpandableDocumentCommand\@@_purify_ref:w{sm}{\@kernel@ref@exp{#2}}
\DeclareExpandableDocumentCommand\@@_purify_pageref:w{sm}{\@kernel@pageref@exp{#2}}
\DeclareExpandableDocumentCommand\@@_purify_nameref:w{sm}{\@@_purify_nameref:n{#2}}
%
%    \end{macrocode}
% Replacements for uppercase and lowercase command
%    \begin{macrocode}
\cs_new:Npn \@@_purify_makeuppercase:n #1
  {\use:e
    {\exp_args:Ne
       \text_uppercase:nn{\BCPdata { casing }}{#1}
    }
  }
\cs_new:Npn \@@_purify_makelowercase:n #1
  {\use:e
    {\exp_args:Ne
       \text_lowercase:nn{\BCPdata { casing }}{#1}
    }
  }
%    \end{macrocode}
% Replacement for hspace:
%    \begin{macrocode}
\NewExpandableDocumentCommand\@@_purify_hspace:w{sm}
  {
    \dim_compare:nNnT{#2}>{0pt}{\space}
  }
%    \end{macrocode}
%
% \subsection{BDC  operator commands}
% \begin{macro}{\pdf_bdc:nn,\pdf_bdc:ee}
% \begin{macro}{\pdf_bdc_property:nn}
% \begin{macro}{\pdf_bdc_shipout:ee}
% \begin{macro}{\pdf_bdcobject:nn}
% \begin{macro}{\pdf_bdcobject:n}
% \begin{macro}{\pdf_bmc:n}
% \begin{macro}{\pdf_emc:}
%    \begin{macrocode}
\cs_new_protected:Npn  \pdf_bdc:nn #1 #2 { \@@_backend_bdc:nn { #1 }{ #2 } }
\cs_generate_variant:Nn \pdf_bdc:nn {ee}

\cs_new_protected:Npn  \pdf_bdc_property:nn #1 #2
  { \@@_backend_bdc_contobj:nn { #1 }{ #2 } }
\cs_new_protected:Npn  \pdf_bdc_shipout:ee #1 #2
  {
    \@@_backend_bdc_shipout:ee { #1 }{ #2 }
    \cs_gset_eq:NN \pdf_bdc_shipout:ee \@@_backend_bdc_shipout:ee
  }
\cs_new_protected:Npn  \pdf_bdcobject:nn #1 #2 { \@@_backend_bdcobject:nn { #1 }{ #2 } }
\cs_new_protected:Npn  \pdf_bdcobject:n #1     { \@@_backend_bdcobject:n  { #1 } }
\cs_new_protected:Npn  \pdf_bmc:n #1     { \@@_backend_bmc:n  { #1 } }
\cs_new_protected:Npn  \pdf_emc:         { \@@_backend_emc: }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \end{implementation}
%
% \PrintIndex
