% \iffalse meta-comment
%
%% File: tagpdf-struct.dtx
%
% Copyright (C) 2019-2025 Ulrike Fischer
%
% 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
%
%    https://www.latex-project.org/lppl.txt
%
% This file is part of the "tagpdf 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/tagpdf
%
% for those people who are interested.
%<*driver>
\DocumentMetadata{}
\documentclass{l3doc}
\usepackage{array,booktabs,caption}
\hypersetup{pdfauthor=Ulrike Fischer,
 pdftitle=tagpdf-tree module (tagpdf)}
\input{tagpdf-docelements}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
% \title{^^A
%   The \pkg{tagpdf-struct} module\\ Commands to create the structure   ^^A
%   \\ Part of the tagpdf package
% }
%
% \author{^^A
%  Ulrike Fischer\thanks
%    {^^A
%      E-mail:
%        \href{mailto:fischer@troubleshooting-tex.de}
%          {fischer@troubleshooting-tex.de}^^A
%    }^^A
% }
%
% \date{Version 0.99u, released 2025-07-16}
% \maketitle
% \begin{documentation}
% \section{Public Commands}
% \begin{function}{\tag_struct_begin:n,\tag_struct_end:,\tag_struct_end:n}
%   \begin{syntax}
%     \cs{tag_struct_begin:n} \Arg{key-values}\\
%     \cs{tag_struct_end:}\\
%     \cs{tag_struct_end:n} \Arg{tag}
%   \end{syntax}
%  These commands start and end a new structure.
%  They don't start a group. They set all their values globally.
%  \cs{tag_struct_end:n} does nothing special normally (apart from 
%  swallowing its argument, but if \texttt{tagpdf-debug} is loaded,
%  it will check if the \Arg{tag} (after expansion)
%  is identical to the current structure on the stack. The tag is not role mapped!
% \end{function}
%  \begin{function}{\tag_struct_use:n,\tag_struct_use_num:n}
%   \begin{syntax}
%     \cs{tag_struct_use:n} \Arg{label}\\
%     \cs{tag_struct_use_num:n} \Arg{structure number}
%   \end{syntax}
% These commands insert a structure previously stashed away as kid
% into the currently active structure.
% A structure should be used only once,
% if the structure already has a parent a warning is issued.
% \end{function}
%  \begin{function}{\tag_struct_object_ref:n,\tag_struct_object_ref:e}
%   \begin{syntax}
%     \cs{tag_struct_object_ref:n} \Arg{structure number}
%   \end{syntax}
%   This is a small wrapper around |\pdf_object_ref:n| to retrieve the
%   object reference of the structure with the number \meta{struct number}.
%   This number can be retrieved and stored for the current structure
%   for example with \cs{tag_get:n}\Arg{struct_num}. Be aware that it can only
%   be used if the structure has already been created and that it doesn't check
%   if the object actually exists!
%  \end{function}
%
% The following two functions are used to add annotations. They must be used
% together and with care to get the same numbers. Perhaps some improvements are needed
% here.
%  \begin{function}{\tag_struct_insert_annot:nn}
%   \begin{syntax}
%     \cs{tag_struct_insert_annot:nn} \Arg{object reference} \Arg{struct parent number}
%   \end{syntax}
% This inserts an annotation in the structure. \meta{object reference}
% is there reference to the annotation. \meta{struct parent number}
% should be the same number as had been inserted with \cs{tag_struct_parent_int:}
% as |StructParent| value to the dictionary of the annotation.
% The command will increase the value of the counter
% used by \cs{tag_struct_parent_int:}.
% \end{function}
% \begin{function}{\tag_struct_parent_int:}
%   \begin{syntax}
%     \cs{tag_struct_parent_int:}
%   \end{syntax}
% This gives back the next free /StructParent number (assuming that it is
% together with \cs{tag_struct_insert_annot:nn} which will increase the number.
% \end{function}
% 
% \begin{function}{\tag_struct_gput:nnn}
% \begin{syntax}
%  \cs{tag_struct_gput:nnn} \Arg{structure number} \Arg{keyword} \Arg{value}
%  \end{syntax}
% This is a command that allows to update the data of a structure.
% This often can't done simply by replacing the value, as we have to 
% preserve and extend existing content. We use therefore dedicated functions
% adjusted to the key in question. 
% The first argument is the number of the structure, 
% the second a keyword referring to a function,
% the third the value. Currently the only keyword is \texttt{ref} which updates
% the Ref key (an array)
% \end{function}
%
% \begin{function}{\tag_struct_gput_ref:nnn}
% \begin{syntax}
%  \cs{tag_struct_gput_ref:nnn} \Arg{structure number} \Arg{keyword} \Arg{value}
%  \end{syntax}
% This is an user interface to add a Ref key to an
% existing structure. The target structure doesn't have to exist yet
% but can be addressed by label, destname or even num.
% \meta{keyword} is currently either \texttt{label}, \texttt{dest}
% or \texttt{num}. The value is then either a label name, the name of a destination
% or a structure number.
% \end{function}
% 
% \section{Public keys}
% \subsection{Keys for the structure commands}
% \begin{structkeydecl}{tag}
% This is required. The value of the key is normally one of the
% standard types listed in the main tagpdf documentation.
% It is possible to setup new tags/types.
% The value can also be of the form |type/NS|, where |NS| is the
% shorthand of a declared name space.
% Currently the names spaces |pdf|, |pdf2|, |mathml| and |user| are defined.
% This allows to use a different name space than
% the one connected by default to the tag. But normally this should not be needed.
% \end{structkeydecl}
% \begin{structkeydecl}{stash}
%  Normally a new structure inserts itself as a kid
%  into the currently active structure. This key prohibits this.
%  The structure is nevertheless from now on
%  \enquote{the current active structure}
%  and parent for following  marked content and structures.
% \end{structkeydecl}
% \begin{structkeydecl}{label}
% This key sets a label by which
% one can refer to the structure. It is e.g.
% used by \cs{tag_struct_use:n} (where a real label is actually not
% needed as you can only use structures already defined), and by the
% |ref| key (which can refer to future structures).
% Internally the label name will start with \texttt{tagpdfstruct-} and it stores
% the two attributes |tagstruct| (the structure number) and |tagstructobj| (the
% object reference).
% \end{structkeydecl}
% \begin{structkeydecl}{parent}
% By default a structure is added as kid to the currently active structure.
% With the parent key one can choose another parent. The value is a structure number which
% must refer to an already existing, previously created structure. Such a structure
% number can for example be have been stored with \cs{tag_get:n}, but one can also use
% a label on the parent structure and then use
% \cs{property_ref:nn}|{tagpdfstruct-label}{tagstruct}| to retrieve it.
% \end{structkeydecl}
% \begin{structkeydecl}{firstkid}
% If this key is used the structure is added at the left of the kids of 
% the parent structure (if the structure is not stashed). 
% This means that it will be the first kid of the structure 
% (unless some later structure uses the key too).
% \end{structkeydecl}
% \begin{structkeydecl}{title,title-o}
% This keys allows to set the dictionary entry
% \texttt{/Title} in the structure object.
% The value is handled as verbatim string and hex encoded.
% Commands are not expanded. |title-o| will expand the value once.
% \end{structkeydecl}
%
% \begin{structkeydecl}{alt}
% This key inserts an \texttt{/Alt} value in the dictionary of structure object.
% The value is handled as verbatim string and hex encoded.
% The value will be expanded first once. If it is empty, nothing will happen.
% \end{structkeydecl}
% \begin{structkeydecl}{actualtext}
% This key inserts an \texttt{/ActualText} value in the dictionary of structure object.
% The value is handled as verbatim string and hex encoded.
% The value will be expanded first once. If it is empty, nothing will happen.
% \end{structkeydecl}
% \begin{structkeydecl}{lang}
% This key allows to set the language for a structure element. The value should be a bcp-identifier,
% e.g. |de-De|.
% \end{structkeydecl}
% \begin{structkeydecl}{ref}
%  This key allows to add references to other structure elements,
%  it adds the |/Ref| array to the structure.
% The value should be a comma separated list of structure labels
% set with the |label| key. e.g. |ref={label1,label2}|.
% \end{structkeydecl}
% \begin{structkeydecl}{E}
%  This key sets the |/E| key, the expanded form of an
%  abbreviation or an acronym
%  (I couldn't think of a better name, so I sticked to E).
% \end{structkeydecl}
% \begin{structkeydecl}{AF,AFref, 
%  AFinline,AFinline-o,texsource,mathml}
% These keys handle associated files in the structure element. 
% 
% \begin{syntax}
% AF = \meta{object name}\\
% AFref = \meta{object reference}\\
% AF-inline = \meta{text content}\\
% \end{syntax}
% 
% The value \meta{object name} should be the name of an object pointing
% to the \texttt{/Filespec} dictionary as expected by
% |\pdf_object_ref:n| from a current \texttt{l3kernel}.
%
% The value |AF-inline| is some text,
% which is embedded in the PDF as a text file with mime type text/plain.
% |AF-inline-o| is like |AF-inline| but expands the value once.
%
% Future versions will perhaps extend this to more mime types, but it is
% still a research task to find out what is really needed.
% 
% |texsource| is a special variant of |AF-inline-o| which embeds the content 
% as |.tex| source with the |/AFrelationship| key set to |/Source|. 
% It also sets the |/Desc| key to a (currently) fix text. 
% 
% |mathml| is a special variant of |AF-inline-o| which embeds the content
% as |.xml| file with the |/AFrelationship| key set to |/Supplement|. 
% It also sets the |/Desc| key to a (currently) fix text. 
%
% The argument of |AF| is an object name referring an embedded file as declared 
% for example with 
% \cs{pdf_object_new:n} or with the l3pdffile module. |AF| expands its argument 
% (this allows e.g. to use some variable for automatic numbering) 
% and can be used more than once, to associate more than one file. 
% 
% The argument of |AFref| is an object reference to an embedded file
% or a variable expanding to such a object reference in the format 
% as you would get e.g. from \cs{pdf_object_ref_last:} or \cs{pdf_object_ref:n} 
% (and which is different for the various engines!). The key allows to make
% use of anonymous objects. Like |AF| the |AFref| key expands its argument 
% and can be used more than once, to associate more than one file. \emph{It
% does not check if the reference is valid!}
% 
% The inline keys can be used only once per structure. Additional calls are ignored.
% \end{structkeydecl}
%
% \begin{structkeydecl}{attribute}
%  This key takes as argument a comma list of attribute names
%  (use braces to protect the commas from the external key-val parser)
%  and allows to add one or more attribute dictionary entries in
%  the structure object. As an example
%     \begin{verbatim}
%      \tagstructbegin{tag=TH,attribute= TH-row}
%      \end{verbatim}
%  Attribute names and their content must be declared first in \cs{tagpdfsetup}.
%
%  \end{structkeydecl}
%
% \begin{structkeydecl}{attribute-class}
%  This key takes as argument a comma list of attribute class names
%  (use braces to protect the commas from the external key-val parser)
%  and allows to add one or more attribute classes to the structure object.
%
%  Attribute class names and their content
%  must be declared first in \cs{tagpdfsetup}.
% \end{structkeydecl}
% 
% \subsection{Setup keys}
% \begin{function}{role/new-attribute (setup-key), newattribute (deprecated)}
% \begin{syntax}
% role/new-attribute = \Arg{name}\Arg{Content}
% \end{syntax}
% This key can be used in the setup command \cs{tagpdfsetup} and allow to declare a
% new attribute, which can be used as attribute or attribute class.
% The value are two brace groups, the first contains the name, the second the content.
% \begin{verbatim}
% \tagpdfsetup
%  {
%   role/new-attribute =
%    {TH-col}{/O /Table /Scope /Column},
%   role/new-attribute =
%    {TH-row}{/O /Table /Scope /Row},
%   }
% \end{verbatim}
%
% \end{function}
% \begin{setupkeydecl}{root-AF}
% \begin{syntax}
% root-AF = \meta{object name}
% \end{syntax}
% This key can be used in the setup command \cs{tagpdfsetup} and allows
% to add associated files to the root structure. Like |AF| it can be used more than
% once to add more than one file.
% \end{setupkeydecl}
% \end{documentation}
% \begin{implementation}
%    \begin{macrocode}
%<@@=tag>
%<*header>
\ProvidesExplPackage {tagpdf-struct-code} {2025-07-16} {0.99u}
 {part of tagpdf - code related to storing structure}
%</header>
%    \end{macrocode}
% \section{Variables}
% \begin{variable}{\c@g_@@_struct_abs_int}
% Every structure will have a unique, absolute number.
%
%    \begin{macrocode}
%<base>\int_new:N  \c@g_@@_struct_abs_int 
%<base>\int_gset:Nn \c@g_@@_struct_abs_int { 1 }
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\g_@@_struct_objR_seq}
% a sequence to store mapping between the
% structure number and the object number.
% We assume that structure numbers are assign
% consecutively and so the index of the seq can be used.
% A seq allows easy mapping over the structures.
%    \begin{macrocode}
%<*package>
\@@_seq_new:N  \g_@@_struct_objR_seq
%    \end{macrocode}
% \end{variable}
% \begin{variable}{\c_@@_struct_null_tl}
% In lua mode we have to test if the kids a null
%    \begin{macrocode}
\tl_const:Nn\c_@@_struct_null_tl {null}
%    \end{macrocode}
% \end{variable}

% \begin{variable}{\g_@@_struct_cont_mc_prop}
% in generic mode it can happen after
% a page break that we have to inject into a structure
% sequence an additional mc after. We will store this additional
% info in a property. The key is the absolute mc num, the value the pdf directory.
%    \begin{macrocode}
\@@_prop_new:N  \g_@@_struct_cont_mc_prop
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\g_@@_struct_stack_seq}
% A stack sequence for the structure stack.
% When a sequence is opened it's number is put on the stack.
%    \begin{macrocode}
\seq_new:N    \g_@@_struct_stack_seq
\seq_gpush:Nn \g_@@_struct_stack_seq {1}
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\g_@@_struct_tag_stack_seq}
% We will perhaps also need the tags. While it is possible to get them from the
% numbered stack, lets build a tag stack too.
%    \begin{macrocode}
\seq_new:N    \g_@@_struct_tag_stack_seq
\seq_gpush:Nn \g_@@_struct_tag_stack_seq {{Root}{StructTreeRoot}}
%    \end{macrocode}
% \end{variable}
%
%
% \begin{variable}{\g_@@_struct_stack_current_tl,\l_@@_struct_stack_parent_tmpa_tl}
% The global variable will hold the current structure number. It is already
% defined in \texttt{tagpdf-base}.
% The local temporary variable will hold the parent when we fetch it from the stack.
%    \begin{macrocode}
%</package>
%<base>\tl_new:N  \g_@@_struct_stack_current_tl
%<base>\tl_gset:Nn \g_@@_struct_stack_current_tl {\int_use:N\c@g_@@_struct_abs_int}
%<*package>
\tl_new:N     \l_@@_struct_stack_parent_tmpa_tl
%    \end{macrocode}
% \end{variable}
%
% In luatex we will store the structure number as attribute.
%    \begin{macrocode}
\sys_if_engine_luatex:TF
 {
   \cs_new:Npn \@@_struct_set_attribute:
    {
      \lua_now:e
      {
        tex.setattribute
         (
          "global",
          luatexbase.attributes.g_@@_structnum_attr,
          \g_@@_struct_stack_current_tl 
         )
      }
    }
 }
 {
   \cs_new_eq:NN \@@_struct_set_attribute: \prg_do_nothing:
 }
%    \end{macrocode}
%
% I will need at least one structure: the StructTreeRoot
% normally it should have only one kid, e.g. the document element.

% The data of the StructTreeRoot and the StructElem are in properties:
% |\g_@@_struct_1_prop| for the root and
% |\g_@@_struct_N_prop|, $N \geq =2$ for the other.
%
% This creates quite a number of properties, so perhaps we will have to
% do this more efficiently in the future.
%
% All properties have at least the keys
% \begin{description}
%    \item[Type] StructTreeRoot or StructElem
%  \end{description}
% and the keys from the two following lists
% (the root has a special set of properties).
% the values of the prop should be already escaped properly
% when the entries are created (title,lange,alt,E,actualtext)
% \begin{variable}
%   {
%     \c_@@_struct_StructTreeRoot_entries_seq,
%     \c_@@_struct_StructElem_entries_seq
%   }
%  These seq contain the keys we support in the two object types.
%  They are currently no longer used, but are provided as documentation and
%  for potential future checks.
%  They should be adapted if there are changes in the PDF format.
%    \begin{macrocode}
\seq_const_from_clist:Nn \c_@@_struct_StructTreeRoot_entries_seq
  {%p. 857/858
    Type,              % always /StructTreeRoot
    K,                 % kid, dictionary or array of dictionaries
    IDTree,            % currently unused
    ParentTree,        % required,obj ref to the parent tree
    ParentTreeNextKey, % optional
    RoleMap,
    ClassMap,
    Namespaces,
    AF                 %pdf 2.0
  }

\seq_const_from_clist:Nn \c_@@_struct_StructElem_entries_seq
  {%p 858 f
    Type,              %always /StructElem
    S,                 %tag/type
    P,                 %parent
    ID,                %optional
    Ref,               %optional, pdf 2.0 Use?
    Pg,                %obj num of starting page, optional
    K,                 %kids
    A,                 %attributes, probably unused
    C,                 %class ""
    %R,                %attribute revision number, irrelevant for us as we
                       % don't update/change existing PDF and (probably)
                       % deprecated in PDF 2.0
    T,                 %title, value in () or <>
    Lang,              %language
    Alt,               % value in () or <>
    E,                 % abbreviation
    ActualText,
    AF,                 %pdf 2.0, array of dict, associated files
    NS,                 %pdf 2.0, dict, namespace
    PhoneticAlphabet,   %pdf 2.0
    Phoneme             %pdf 2.0
  }
%    \end{macrocode}
% \end{variable}
%
% \subsection{Variables used by the keys}
% \begin{variable}{\g_@@_struct_tag_tl,\g_@@_struct_tag_NS_tl,
%  \l_@@_struct_roletag_tl,\g_@@_struct_roletag_NS_tl,
%  \l_@@_struct_parenttag_tl,\l_@@_struct_parenttag_NS_tl}
% Use by the tag key to store the tag and the namespace.
% The \texttt{roletag} variables will hold locally rolemapping info needed
% for the parent-child checks. The \texttt{parenttag} variables allow to 
% set the target role of the parent of stashed structures. 
%    \begin{macrocode}
\tl_new:N \g_@@_struct_tag_tl
\tl_new:N \g_@@_struct_tag_NS_tl
\tl_new:N \l_@@_struct_roletag_tl
\tl_new:N \l_@@_struct_roletag_NS_tl
\tl_new:N \l_@@_struct_parenttag_tl
\tl_set:Nn \l_@@_struct_parenttag_tl {STASHED}
\tl_new:N \l_@@_struct_parenttag_NS_tl
\tl_set:Nn \l_@@_struct_parenttag_NS_tl {latex}
%    \end{macrocode}
% \end{variable}
% \begin{variable}{\g_@@_struct_label_num_prop}
% This will hold for every structure label the associated 
% structure number. The prop will allow to 
% fill the /Ref key directly at the first compilation if the ref
% key is used.
%    \begin{macrocode}
\prop_new_linked:N \g_@@_struct_label_num_prop
%    \end{macrocode}
% \end{variable}

% \begin{variable}{\l_@@_struct_elem_stash_bool}
% This will keep track of the stash status
%    \begin{macrocode}
\bool_new:N \l_@@_struct_elem_stash_bool
%    \end{macrocode}
% \end{variable}
% 
% \begin{variable}{\l_@@_struct_addkid_tl}
% This decides if a structure kid is added at the left or right of the parent.
% The default is \texttt{right}.
%    \begin{macrocode}
\tl_new:N  \l_@@_struct_addkid_tl
\tl_set:Nn \l_@@_struct_addkid_tl {right}
%    \end{macrocode}
% \end{variable}
 
% \subsection{Variables used by tagging code of basic elements}
%
% \begin{variable}{\g_@@_struct_dest_num_prop}
% This variable records for (some or all, not clear yet)
% destination names the related structure number to allow
% to reference them in a Ref. The key is the destination.
% It is currently used by the toc-tagging and sec-tagging code.
%    \begin{macrocode}
%</package>
%<base>\prop_new_linked:N \g_@@_struct_dest_num_prop
%<*package>
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\g_@@_struct_ref_by_dest_prop}
% This variable contains structures whose Ref key should be updated
% at the end to point to structured related with this destination.
% As this is probably need in other places too, it is not only a toc-variable.
% TODO: remove after 11/2024 release. 
%    \begin{macrocode}
\prop_new_linked:N \g_@@_struct_ref_by_dest_prop
%    \end{macrocode}
% \end{variable} 
% 
% \section{Commands}
%
% The properties must be in some places handled expandably.
% So I need an output handler for each prop, to get expandable output
% see \url{https://tex.stackexchange.com/questions/424208}.
% There is probably room here for a more efficient implementation.
% TODO check if this can now be implemented with the pdfdict commands.
% The property contains currently non pdf keys, but e.g. object numbers are
% perhaps no longer needed as we have named object anyway.
%
% \begin{macro}{\@@_struct_output_prop_aux:nn,\@@_new_output_prop_handler:n}
%    \begin{macrocode}
\cs_new:Npn \@@_struct_output_prop_aux:nn #1 #2 %#1 num, #2 key
  {
    \prop_if_in:cnT
      { g_@@_struct_#1_prop }
      { #2 }
      {
        \c_space_tl/#2~ \prop_item:cn{ g_@@_struct_#1_prop } { #2 }
      }
  }

\cs_new_protected:Npn \@@_new_output_prop_handler:n #1
  {
    \cs_new:cn { @@_struct_output_prop_#1:n }
      {
        \@@_struct_output_prop_aux:nn {#1}{##1}
      }
  }
%</package>  
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_struct_prop_gput:nnn}
% The structure props must be filled in various places.
% For this we use a common command which also takes care of the debug package: 
%    \begin{macrocode}
%<*package|debug>
%<package>\cs_new_protected:Npn \@@_struct_prop_gput:nnn #1 #2 #3
%<debug>\cs_set_protected:Npn \@@_struct_prop_gput:nnn #1 #2 #3
 {
   \@@_prop_gput:cnn
        { g_@@_struct_#1_prop }{#2}{#3}
%<debug>\prop_gput:cnn { g_@@_struct_debug_#1_prop } {#2} {#3}        
 }
\cs_generate_variant:Nn \@@_struct_prop_gput:nnn {onn,nne,nee,nno} 
%</package|debug>
%    \end{macrocode}
% \end{macro}

% \subsection{Initialization of the StructTreeRoot}
% The first structure element, the StructTreeRoot is special, so
% created manually. The underlying object is |@@/struct/1| which is currently
% created in the tree code (TODO move it here).
% The |ParentTree| and |RoleMap| entries are added at begin document
% in the tree code as they refer to object which are setup in other parts of the
% code. This avoid timing issues.
%
%    \begin{macrocode}
%<*package>
\tl_gset:Nn \g_@@_struct_stack_current_tl {1}
%    \end{macrocode}
% \begin{macro}{\@@_pdf_name_e:n}
%    \begin{macrocode}
\cs_new:Npn \@@_pdf_name_e:n #1{\pdf_name_from_unicode_e:n{#1}}
%</package>
%    \end{macrocode}
% \end{macro}
%
% \begin{variable}{g_@@_struct_1_prop,g_@@_struct_kids_1_seq}
%    \begin{macrocode}
%<*package>
\@@_prop_new:c { g_@@_struct_1_prop }
\@@_new_output_prop_handler:n {1}
\@@_seq_new:c  { g_@@_struct_kids_1_seq }

\@@_struct_prop_gput:nne
  { 1 }            
  { Type }
  { \pdf_name_from_unicode_e:n {StructTreeRoot} }

\@@_struct_prop_gput:nne
  { 1 }            
  { S }
  { \pdf_name_from_unicode_e:n {StructTreeRoot} }

\@@_struct_prop_gput:nne
  { 1 }            
  { tag }
  { {StructTreeRoot}{pdf} }

\@@_struct_prop_gput:nne
  { 1 }            
  { rolemap }
  { {StructTreeRoot}{pdf} }

\@@_struct_prop_gput:nne
  { 1 }            
  { parentrole }
  { {StructTreeRoot}{pdf} }
  
%    \end{macrocode}
% Namespaces are pdf 2.0.
% If the code moves into the kernel, the setting must be probably delayed.
%    \begin{macrocode}
\pdf_version_compare:NnF < {2.0}
 {
   \@@_struct_prop_gput:nne
    { 1 }            
    { Namespaces }
    { \pdf_object_ref:n { @@/tree/namespaces } }
 } 
%</package>
%    \end{macrocode}
% In debug mode we have to copy the root manually as it is already setup:
%    \begin{macrocode}
%<debug>\prop_new:c { g_@@_struct_debug_1_prop }
%<debug>\seq_new:c  { g_@@_struct_debug_kids_1_seq }
%<debug>\prop_gset_eq:cc { g_@@_struct_debug_1_prop }{ g_@@_struct_1_prop }
%<debug>\prop_gremove:cn { g_@@_struct_debug_1_prop }{Namespaces}   
%    \end{macrocode}
% \end{variable}
% 
% \subsection{Adding the /ID key}
% Every structure gets automatically an ID which is currently
% simply calculated from the structure number.
% \begin{macro}{\@@_struct_get_id:n}
%    \begin{macrocode}
%<*package>
\cs_new:Npn \@@_struct_get_id:n #1 %#1=struct num
  {
    (
     ID.
     \prg_replicate:nn 
      { \int_abs:n{\g_@@_tree_id_pad_int - \tl_count:e { \int_to_arabic:n { #1 } }} } 
      { 0 }
     \int_to_arabic:n { #1 }
    )
  }
%    \end{macrocode}
% \end{macro}
% 
% \subsection{Filling in the tag info}

% \begin{macro}{\@@_struct_set_tag_info:nnn }
% This adds or updates the tag info to a structure given by a number. 
% We need also the original data, so we store both.
%    \begin{macrocode}
\pdf_version_compare:NnTF < {2.0}
 {
   \cs_new_protected:Npn \@@_struct_set_tag_info:nnn #1 #2 #3
     %#1 structure number, #2 tag, #3 NS
     { 
       \@@_struct_prop_gput:nne
         { #1 }            
         { S }
         { \pdf_name_from_unicode_e:n {#2}  } %
       \@@_struct_prop_gput:nnn
         { #1 }            
         { tag }
         { {#2} {} }  
     }
 }     
 {     
   \cs_new_protected:Npn \@@_struct_set_tag_info:nnn #1 #2 #3
     {       
       \@@_struct_prop_gput:nne
         { #1 }            
         { S }
         { \pdf_name_from_unicode_e:n {#2} } %
       \prop_get:NnNT \g_@@_role_NS_prop {#3} \l_@@_get_tmpc_tl
         {
           \@@_struct_prop_gput:nne
             { #1 }            
             { NS }
             { \l_@@_get_tmpc_tl } %
         }
       \@@_struct_prop_gput:nnn
         { #1 }            
         { tag }
         { {#2} {#3} }           
     } 
 }
\cs_generate_variant:Nn \@@_struct_set_tag_info:nnn {eoo}
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_struct_get_role:nnNN}
%  We also need a way to get the tag info needed for parent child
%  check from parent structures.
%  The tag info is stored as the value of the rolemap key, but for
%  \enquote{transparent} structures we also have to look into parentrole key.
%  
%    \begin{macrocode}
\cs_new_protected:Npn \@@_struct_get_role:nnNN #1 #2 #3 #4
   %#1 :struct num, 
   %#2 :rolemap or parentrole 
   %#3 :tlvar for tag (rolemapped)
   %#4 :tlvar for NS  (rolemapped, so standard or empty or UNKNOWN)
    {        
       \prop_get:cnNTF 
         { g_@@_struct_#1_prop } 
         { #2 }
         \l_@@_get_tmpc_tl
         {
           \tl_set:Ne #3{\exp_last_unbraced:No\use_i:nn  { \l_@@_get_tmpc_tl }}
           \tl_set:Ne #4{\exp_last_unbraced:No\use_ii:nn { \l_@@_get_tmpc_tl }}
         }         
         {
           \tl_clear:N#3
           \tl_clear:N#4
         }
    }
\cs_generate_variant:Nn\@@_struct_get_role:nnNN {enNN}    
%    \end{macrocode}
% \end{macro}
% \subsection{Handlings kids}
% Commands to store the kids. Kids in a structure can be a reference to a mc-chunk,
% an object reference to another structure element, or a object reference to an
% annotation (through an OBJR object).
% \begin{macro}{\@@_struct_kid_mc_gput_right:nn,\@@_struct_kid_mc_gput_right:ne}
% The command to store an mc-chunk, this is a dictionary of type MCR.
% It would be possible to write out the content directly as unnamed object
% and to store only the object reference, but probably this would be slower,
% and the PDF is more readable like this.
% The code doesn't try to avoid the use of the /Pg key by checking page numbers.
% That imho only slows down without much gain.
% In generic mode the page break code will perhaps to have to insert
% an additional mcid after an existing one. For this we use a property list
% At first an auxiliary to write the MCID dict. This should normally be expanded!
%    \begin{macrocode}
\cs_new:Npn \@@_struct_mcid_dict:n #1 %#1 MCID absnum
  {
     <<
      /Type \c_space_tl /MCR \c_space_tl
      /Pg
        \c_space_tl
      \pdf_pageobject_ref:n { \property_ref:enn{mcid-#1}{tagabspage}{1} }
       /MCID \c_space_tl \property_ref:enn{mcid-#1}{tagmcid}{1}
     >>
  }
%</package>  
%    \end{macrocode}
%    \begin{macrocode}
%<*package|debug>
%<package>\cs_new_protected:Npn \@@_struct_kid_mc_gput_right:nn #1 #2 
%<debug>\cs_set_protected:Npn \@@_struct_kid_mc_gput_right:nn #1 #2 
%#1 structure num, #2 MCID absnum%
  {
    \@@_seq_gput_right:ce
      { g_@@_struct_kids_#1_seq }
      {
        \@@_struct_mcid_dict:n {#2}
      }
%<debug>    \seq_gput_right:cn
%<debug>      { g_@@_struct_debug_kids_#1_seq }
%<debug>      {
%<debug>        MC~#2
%<debug>      }
    \@@_seq_gput_right:cn
      { g_@@_struct_kids_#1_seq }
      {
        \prop_item:Nn \g_@@_struct_cont_mc_prop {#2}
      }
  }
%<package>\cs_generate_variant:Nn \@@_struct_kid_mc_gput_right:nn {ne}
%    \end{macrocode}
% \end{macro}
%  \begin{macro}
%    {
%      \@@_struct_kid_struct_gput_right:nn,\@@_struct_kid_struct_gput_right:ee
%    }
%  This commands adds a structure as kid. We only need to record the object
%  reference in the sequence.
%    \begin{macrocode}
%<package>\cs_new_protected:Npn\@@_struct_kid_struct_gput_right:nn #1 #2
%<debug>\cs_set_protected:Npn\@@_struct_kid_struct_gput_right:nn #1 #2 
%%#1 num of parent struct, #2 kid struct
  {
    \@@_seq_gput_right:ce
      { g_@@_struct_kids_#1_seq }
      {
        \pdf_object_ref_indexed:nn { @@/struct }{ #2 }
      }
%<debug>    \seq_gput_right:cn
%<debug>      { g_@@_struct_debug_kids_#1_seq }
%<debug>      {
%<debug>        Struct~#2
%<debug>      }            
  }
%<package>\cs_generate_variant:Nn \@@_struct_kid_struct_gput_right:nn {ee}
%    \end{macrocode}
% \end{macro}
%  \begin{macro}
%    {
%      \@@_struct_kid_struct_gput_left:nn,\@@_struct_kid_struct_gput_left:ee
%    }
%  This commands adds a structure as kid one the left, so as first
%  kid. We only need to record the object reference in the sequence.
%    \begin{macrocode}
%<package>\cs_new_protected:Npn\@@_struct_kid_struct_gput_left:nn #1 #2
%<debug>\cs_set_protected:Npn\@@_struct_kid_struct_gput_left:nn #1 #2 
%%#1 num of parent struct, #2 kid struct
  {
    \@@_seq_gput_left:ce
      { g_@@_struct_kids_#1_seq }
      {
        \pdf_object_ref_indexed:nn { @@/struct }{ #2 }
      }
%<debug>    \seq_gput_left:cn
%<debug>      { g_@@_struct_debug_kids_#1_seq }
%<debug>      {
%<debug>        Struct~#2
%<debug>      }            
  }
%<package>\cs_generate_variant:Nn \@@_struct_kid_struct_gput_left:nn {ee}
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}
%  {\@@_struct_kid_OBJR_gput_right:nnn,\@@_struct_kid_OBJR_gput_right:eee}
% At last the command to add an OBJR object. This has to write an object first.
% The first argument is the number of the parent structure, the second the
% (expanded) object reference of the annotation. The last argument is the page
% object reference
%
%    \begin{macrocode}
%<package>\cs_new_protected:Npn\@@_struct_kid_OBJR_gput_right:nnn #1 #2 #3 
%<package>                                                             
%<package>                                                             
%<debug>\cs_set_protected:Npn\@@_struct_kid_OBJR_gput_right:nnn #1 #2 #3
%%#1 num of parent struct,#2 obj reference,#3 page object reference
  {
    \pdf_object_unnamed_write:nn
      { dict }
      {
        /Type/OBJR/Obj~#2/Pg~#3
      }
    \@@_seq_gput_right:ce
      { g_@@_struct_kids_#1_seq }
      {
        \pdf_object_ref_last:
      }
%<debug>    \seq_gput_right:ce
%<debug>      { g_@@_struct_debug_kids_#1_seq }
%<debug>      {
%<debug>        OBJR~reference
%<debug>      }      
  }
%</package|debug>  
%<*package>
\cs_generate_variant:Nn\@@_struct_kid_OBJR_gput_right:nnn { eee }
%    \end{macrocode}
% \end{macro}
% \begin{macro}
%   {\@@_struct_exchange_kid_command:N, \@@_struct_exchange_kid_command:c}
%  In luamode it can happen that a single kid in a structure is split at a page
%  break into two or more mcid. In this case the lua code has to convert
%  put the dictionary of the kid into an array. See issue 13 at tagpdf repo.
%  We exchange the dummy command for the kids to mark this case.
%  Change 2024-03-19: don't use a regex - that is slow.
%    \begin{macrocode}
\cs_new_protected:Npn\@@_struct_exchange_kid_command:N #1 %#1 = seq var
  {
    \seq_gpop_left:NN #1 \l_@@_tmpa_tl
    \tl_replace_once:Nnn \l_@@_tmpa_tl
     {\@@_mc_insert_mcid_kids:n}
     {\@@_mc_insert_mcid_single_kids:n}    
    \seq_gput_left:No #1 { \l_@@_tmpa_tl }
  }

\cs_generate_variant:Nn\@@_struct_exchange_kid_command:N { c }
%    \end{macrocode}
% \end{macro}
% \begin{macro}{ \@@_struct_fill_kid_key:n }
% This command adds the kid info to the K entry. In lua mode the
% content contains commands which are expanded later. The argument is the structure
% number.
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_struct_fill_kid_key:n #1 %#1 is the struct num
  {
    \bool_if:NF \g_@@_mode_lua_bool
     {
        \seq_clear:N \l_@@_tmpa_seq
        \seq_map_inline:cn { g_@@_struct_kids_#1_seq }
         { \seq_put_right:Ne \l_@@_tmpa_seq { ##1 } }
        %\seq_show:c { g_@@_struct_kids_#1_seq }
        %\seq_show:N \l_@@_tmpa_seq
        \seq_remove_all:Nn \l_@@_tmpa_seq {}
        %\seq_show:N \l_@@_tmpa_seq
        \seq_gset_eq:cN { g_@@_struct_kids_#1_seq } \l_@@_tmpa_seq
     }

    \int_case:nnF
      {
        \seq_count:c
          {
            g_@@_struct_kids_#1_seq
          }
      }
      {
        { 0 }
         { } %no kids, do nothing
        { 1 } % 1 kid, insert
         {
           % in this case we need a special command in
           % luamode to get the array right. See issue #13
           \sys_if_engine_luatex:TF
             {
               \@@_struct_exchange_kid_command:c
                {g_@@_struct_kids_#1_seq}
%    \end{macrocode}
% check if we get null
%    \begin{macrocode}
                \tl_set:Ne\l_@@_tmpa_tl
                  {\use:e{\seq_item:cn {g__tag_struct_kids_#1_seq} {1}}}
                \tl_if_eq:NNF\l__tag_tmpa_tl \c_@@_struct_null_tl 
                  {   
                    \@@_struct_prop_gput:nne
                      {#1}            
                      {K}
                      {
                        \seq_item:cn
                          {
                            g_@@_struct_kids_#1_seq
                          }
                          {1}
                      } 
                  }                 
             }
             {
               \@@_struct_prop_gput:nne
                 {#1}            
                 {K}
                 {
                   \seq_item:cn
                     {
                       g_@@_struct_kids_#1_seq
                     }
                     {1}
                 }
             }
         } %
      }
      { %many kids, use an array
        \@@_struct_prop_gput:nne
          {#1} 
          {K}
          {
            [
              \seq_use:cn
                {
                  g_@@_struct_kids_#1_seq
                }
                {
                  \c_space_tl
                }
            ]
          }
      }
  }

%    \end{macrocode}
% \end{macro}
%  \subsection{Output of the object}
% \begin{macro}{\@@_struct_get_dict_content:nN}
% This maps the dictionary content of a structure into a tl-var.
% Basically it does what |\pdfdict_use:n| does.
% This is used a lot so should be rather fast.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_struct_get_dict_content:nN #1 #2 %#1: structure num
  {
    \tl_clear:N #2
    \prop_map_inline:cn { g_@@_struct_#1_prop }
      {
%    \end{macrocode}
% Some keys needs the option to format the value, e.g. add brackets for an
% array, we also need the option to ignore some entries in the properties.
%    \begin{macrocode}
        \cs_if_exist_use:cTF {@@_struct_format_##1:nnN}
          {
            {##1}{##2}#2
          }
          {
            \tl_put_right:Ne #2 { \c_space_tl/##1~##2 } 
          }
      }    
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_struct_format_rolemap:nnN,\@@_struct_format_parentrole:nnN,
% \@@_struct_format_P:nnN,\@@_struct_format_tag:nnN}
% This three entries should not end in the PDF.
% Todo: check if the S/NS keys can be dropped and replaced by a processing of the tag key.
%    \begin{macrocode}
\cs_new:Nn\@@_struct_format_rolemap:nnN{}
\cs_new:Nn\@@_struct_format_parentrole:nnN{}
\cs_new:Nn\@@_struct_format_tag:nnN{}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_struct_format_parentnum:nnN}
% parent is a structure number and
% should expand to the object reference.
%    \begin{macrocode}
\cs_new_protected:Nn\@@_struct_format_parentnum:nnN
  {
    \tl_put_right:Ne #3 { ~/P~\pdf_object_ref_indexed:nn { @@/struct} { #2 } }
  }
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_struct_format_Ref:nnN}
% Ref is an array, we store values as a clist of commands that must
% be executed here, the formatting has to add also brackets. 
% 
%    \begin{macrocode}
\cs_new_protected:Nn\@@_struct_format_Ref:nnN
  {
    \tl_put_right:Nn #3 { ~/#1~[ } %]
    \clist_map_inline:nn{ #2 }
     {
       ##1 #3
     }
    \tl_put_right:Nn #3 
     { %[
      \c_space_tl]
     }  
  }
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_struct_write_obj:n}
% This writes out the structure object.
% This is done in the finish code, in the tree module and
% guarded by the tree boolean.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_struct_write_obj:n #1 % #1 is the struct num
  {
    \prop_if_exist:cTF { g_@@_struct_#1_prop } 
      {
%    \end{macrocode}
% It can happen that a structure is not used and so has not parent.
% Simply ignoring it is problematic as it is also recorded in 
% the IDTree, so we make an artifact out of it.
%    \begin{macrocode}
        \prop_get:cnNF { g_@@_struct_#1_prop } {parentnum}\l_@@_tmpb_tl
          { 
%            \prop_gput:cne { g_@@_struct_#1_prop } {P}
%              {\pdf_object_ref_indexed:nn { @@/struct }{1}}
            \prop_gput:cne { g_@@_struct_#1_prop } {parentnum}{1}  
            \prop_gput:cne { g_@@_struct_#1_prop } {S}{/Artifact}
            \seq_if_empty:cF {g_@@_struct_kids_#1_seq}
              {
                \msg_warning:nnee 
                  {tag} 
                  {struct-orphan} 
                  { #1 }
                  {\seq_count:c{g_@@_struct_kids_#1_seq}}
              }
          }              
        \@@_struct_fill_kid_key:n { #1 }
        \@@_struct_get_dict_content:nN { #1 } \l_@@_tmpa_tl
        \pdf_object_write_indexed:nnne
           { @@/struct }{ #1 }
           {dict}
           {
             \l_@@_tmpa_tl\c_space_tl                            
             /ID~\@@_struct_get_id:n{#1}              
           }
           
      }
      {
        \msg_error:nnn { tag } { struct-no-objnum } { #1}
      }
  }
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_struct_insert_annot:nn}
% This is the command to insert an annotation into the structure.
% It can probably be used for xform too.
%
% Annotations used as structure content must
% \begin{enumerate}
% \item add a StructParent integer to their dictionary
% \item push the object reference as OBJR object in the structure
% \item Add a Structparent/obj-nr reference to the parent tree.
% \end{enumerate}
% For a link this looks like this
% \begin{verbatim}
%         \tag_struct_begin:n { tag=Link }
%         \tag_mc_begin:n { tag=Link }
% (1)     \pdfannot_dict_put:nne
%           { link/URI }
%           { StructParent }
%           { \int_use:N\c@g_@@_parenttree_obj_int }
%    <start link> link text <stop link>
% (2+3)   \@@_struct_insert_annot:nn {obj ref}{parent num}
%         \tag_mc_end:
%         \tag_struct_end:
% \end{verbatim}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_struct_insert_annot:nn #1 #2 
 %#1 object reference to the annotation/xform
 %#2 structparent number
  {
    \bool_if:NT \g_@@_active_struct_bool
      {
        %get the number of the parent structure:
        \seq_get:NNF
          \g_@@_struct_stack_seq
          \l_@@_struct_stack_parent_tmpa_tl
          {
            \msg_error:nn { tag } { struct-faulty-nesting }
          }
        %put the obj number of the annot in the kid entry, this also creates
        %the OBJR object
        \@@_property_record:nn {@tag@objr@page@#2 }{ tagabspage }
        \@@_struct_kid_OBJR_gput_right:eee
          {
            \l_@@_struct_stack_parent_tmpa_tl
          }
          {
            #1 %
          }
          {
            \pdf_pageobject_ref:n 
              { \property_ref:nnn {@tag@objr@page@#2 }{ tagabspage }{1} }
          }
        % add the parent obj number to the parent tree:
        % the command always expands its arguments!   
        \@@_parenttree_add_objr:nn
          {
            #2
          }
          {
            \pdf_object_ref_indexed:nn 
              { @@/struct }{ \l_@@_struct_stack_parent_tmpa_tl }
          }
        % increase the int:
        \int_gincr:N \c@g_@@_parenttree_obj_int 
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_struct_insert_annot_shipout:nnn}
% This command is similar to the previous one but is meant to be used at shipout 
% (currently only sensible for luatex). 
% To move the OBJR into the right structure it has to get the structure number
% additionally as argument. But as it is used at shipout it doesn't need a label
% to get the page reference but can use \cs{g_shipout_readonly_int}.
% It does \emph{not} increase the parenttree integer (timing is wrong in 
% lua), instead code using the command has to do it. See the lua code. 
%    \begin{macrocode}
\cs_new_protected:Npn\@@_struct_insert_annot_shipout:nnn #1#2#3
% #1 structnum, #2 object reference, #3 StructParentNum
 {
   \@@_struct_kid_OBJR_gput_right:eee
     {
       #1
     }
     {
       #2 
     }
     {
       \pdf_pageobject_ref:n 
         { \int_use:N \g_shipout_readonly_int } %
     }
   % add the parent obj number to the parent tree:
   % the command always expands its arguments!   
   \@@_parenttree_add_objr:nn
     {
       #3
     }
     {
       \pdf_object_ref_indexed:nn 
         { @@/struct }{ #1 }
     }   
  }   
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_get_data_struct_tag:}
% this command allows \cs{tag_get:n} to get the current
% structure tag with the keyword |struct_tag|. 
%    \begin{macrocode}
\cs_new:Npn \@@_get_data_struct_tag:
  {
    \exp_args:Ne
    \tl_tail:n
     {
       \prop_item:cn {g_@@_struct_\g_@@_struct_stack_current_tl _prop}{S}
     }
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_get_data_struct_id:}
% this command allows \cs{tag_get:n} to get the current
% structure id with the keyword |struct_id|. 
%    \begin{macrocode}
\cs_new:Npn \@@_get_data_struct_id:
  {
    \@@_struct_get_id:n {\g_@@_struct_stack_current_tl}
  }
%</package>
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_get_data_struct_num:}
% this command allows \cs{tag_get:n} to get the current
% structure number with the keyword |struct_num|. We will need to handle nesting
%    \begin{macrocode}
%<*base>
\cs_new:Npn \@@_get_data_struct_num:
  {
    \g_@@_struct_stack_current_tl
  }
%</base>
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_get_data_struct_counter:}
% this command allows \cs{tag_get:n} to get the current
% state of the structure counter with the keyword |struct_counter|. 
% By comparing the numbers it can be used to check the number of 
% structure commands in a piece of code.
%    \begin{macrocode}
%<*base>
\cs_new:Npn \@@_get_data_struct_counter:
  {
    \int_use:N \c@g_@@_struct_abs_int
  }
%</base>
%    \end{macrocode}
% \end{macro}
% 
% \subsection{Commands for the parent-child checks}
%
% \begin{macro}{\@@_struct_check_parent_child_aux:nnnnN}
%    \begin{macrocode}
%<*package>
\cs_new_protected:Npn \@@_struct_check_parent_child_aux:nnnnN #1#2#3#4#5
  {
% #1 structure number of parent
% #2 key to use to retrieve role of parent (either rolemap or parentrole field)
% #3 structure number of parent
% #4 key to use to retrieve role of child (either rolemap or parentrole field)
% #5 tl for return value
%    \end{macrocode}
% get parent rolemap
%    \begin{macrocode}
    \@@_struct_get_role:nnNN  
      {#1} 
      {#2}
      \l_@@_get_parent_tmpa_tl
      \l_@@_get_parent_tmpb_tl
%    \end{macrocode}
% get child rolemap
%    \begin{macrocode}
    \@@_struct_get_role:nnNN  
      {#3} 
      {#4}
      \l_@@_get_child_tmpa_tl
      \l_@@_get_child_tmpb_tl
%    \end{macrocode}
% check
%    \begin{macrocode}
    \@@_role_check_parent_child:ooooN  
      { \l_@@_get_parent_tmpa_tl } % rolemapped from above
      { \l_@@_get_parent_tmpb_tl } % rolemapped from above
      { \l_@@_get_child_tmpa_tl } % 
      { \l_@@_get_child_tmpb_tl } %   
     #5
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_struct_check_parent_child:nn}
% When comparing the relation between structures we use the structure 
% numbers.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_struct_check_parent_child:nn #1 #2 
% #1 structure number of parent
% #2 structure number of child. %
% This assumes that the fields rolemap/parentrole has already been filled.
  {
%    \end{macrocode}
% This records if logging is on
%    \begin{macrocode}
    \int_compare:nNnT {\l_@@_loglevel_int} > { 0 }
      {
        \prop_get:cnN{g_@@_struct_#1_prop}{tag}\l_@@_get_parent_tmpa_tl
        \prop_get:cnN{g_@@_struct_#2_prop}{tag}\l_@@_get_parent_tmpb_tl
        \msg_note:nnee
         { tag }
         { role-parent-child-check }
         {
           \quark_if_no_value:NTF \l__tag_get_parent_tmpa_tl
            {??}
            {
              \exp_last_unbraced:No\use_ii:nn 
               { \l_@@_get_parent_tmpa_tl }
              :
             \exp_last_unbraced:No\use_i:nn 
               { \l_@@_get_parent_tmpa_tl }             
            }   
         }
         {
           \quark_if_no_value:NTF \l__tag_get_parent_tmpb_tl
            {??}
            {
             \exp_last_unbraced:No\use_ii:nn 
              { \l_@@_get_parent_tmpb_tl }
             :
             \exp_last_unbraced:No\use_i:nn 
              { \l_@@_get_parent_tmpb_tl }    
            }  
         }           
      }      
    \@@_struct_check_parent_child_aux:nnnnN
      {#1}
      {rolemap}
      {#2}
      {rolemap}
      \l_@@_parent_child_check_tl 
%    \end{macrocode}
% if the return value is 7 we have to check against the parentrole field.
%    \begin{macrocode}
    \int_compare:nNnT {\l_@@_parent_child_check_tl} = { \c_@@_role_rule_checkparent_tl }                            
      {
        \@@_struct_check_parent_child_aux:nnnnN
          {#1}
          {parentrole}
          {#2}
          {rolemap}
          \l_@@_parent_child_check_tl 
      }               
    \@@_check_struct_forbidden_parent_child:onn 
     {\l_@@_parent_child_check_tl}  
     {#1}
     {#2}
  } 
\cs_generate_variant:Nn \@@_struct_check_parent_child:nn {oo}             
%    \end{macrocode}
% \end{macro}
%  
% \begin{macro}{\@@_struct_use_check_parent_child:nn}
% A similar command is needed if a structure is stashed and used.
% The child can be 
%  - a normal tag (e.g. H1)
%    then rolemap = parentrole = {H1}{pdf2} 
%    and we should test 
%    rolemap (parent) and rolemap (child)
%    if = 7 
%    parentrole (parent) and rolemap (child)  
%    That is the normal check above. 
%    
%  - Part/Div/Nonstruct
%    then rolemap = {Part}{pdf2}
%    and parentrole = {STASHED}{latex} or {target parent}{NS}
%    
%    If parentrole =STASHED we can't test if the child fits here.
%    If parentrole is not STASHED, then would should test
%    if target parent= rolemap (parent) or parentrole (parent)
%    and if yet then test
%    rolemap (child) against rolemap (parent) 
%    and if =7 rolemap(child) against parentrole(parent). 
%    that is again the normal check. 
%    \begin{macrocode}
\cs_new_protected:Npn \@@_struct_use_check_parent_child:nn #1 #2 
% #1 structure number of parent
% #2 structure number of child. %
  {
    \@@_struct_get_role:enNN  
      {#2}
      {rolemap} 
      \l_@@_get_child_tmpa_tl
      \l_@@_get_child_tmpb_tl
    \str_case:onTF { \l_@@_get_child_tmpa_tl }
      {
        {Part} {}
        {Div}  {}
        {NonStruct} {}
      } 
      { %child=Part etc
        \@@_struct_get_role:enNN  
         {#2}
         {parentrole} 
         \l_@@_get_child_tmpa_tl
         \l_@@_get_child_tmpb_tl
        \str_if_eq:noTF
         {STASHED}{\l_@@_get_child_tmpa_tl}
         {
           % warn about unknown relationship
         } 
         {
           % test if 
           \@@_struct_get_role:enNN  
            {#1}
            {parentrole} 
            \l_@@_get_parent_tmpa_tl
            \l_@@_get_parent_tmpb_tl
           \tl_if_eq:NNTF\l_@@_get_parent_tmpa_tl \l_@@_get_child_tmpa_tl
            {
              \@@_struct_check_parent_child:nn {#1}{#2}
            }
            {
              %warn that parent-tag was misused.
            }   
         }
      }  
      {
        %child not Part etc, normal parent child test.
        \@@_struct_check_parent_child:nn {#1}{#2}
      }         
  }          
\cs_generate_variant:Nn  { \@@_struct_use_check_parent_child:nn }{oo}      
%    \end{macrocode}
% \end{macro}
% \section{Keys}
% This are the keys for the user commands.
% we store the tag in a variable. But we should be careful, it is only reliable
% at the begin.
% 
% This socket is used by the tag key. It allows to switch between
% the latex-tabs and the standard tags.
%    \begin{macrocode}
\socket_new:nn { tag/struct/tag }{1}
\socket_new_plug:nnn { tag/struct/tag }{ latex-tags }
 {
   \prop_get:NeNTF \g__tag_role_tags_NS_prop {#1} \l_@@_tmp_unused_tl
    {
      \seq_set_split:Nne \l_@@_tmpa_seq { / } 
        {#1/\l_@@_tmp_unused_tl}
    }
    {
      \seq_set_split:Nne \l_@@_tmpa_seq { / } 
        {#1/}
    }    
   \tl_gset:Ne \g_@@_struct_tag_tl   { \seq_item:Nn\l_@@_tmpa_seq {1} }
   \tl_gset:Ne \g_@@_struct_tag_NS_tl{ \seq_item:Nn\l_@@_tmpa_seq {2} }  
   \@@_check_structure_tag:N \g_@@_struct_tag_tl
 }
 
\socket_new_plug:nnn { tag/struct/tag }{ pdf-tags }
 {
   \prop_get:NeNTF \g__tag_role_tags_NS_prop {#1} \l_@@_tmp_unused_tl
     {
      \seq_set_split:Nne \l_@@_tmpa_seq { / } 
        {#1/\l_@@_tmp_unused_tl}
     }
     {
       \seq_set_split:Nne \l_@@_tmpa_seq { / } 
         {#1/}
     }        
   \tl_gset:Ne \g_@@_struct_tag_tl   { \seq_item:Nn\l_@@_tmpa_seq {1} }
   \tl_gset:Ne \g_@@_struct_tag_NS_tl{ \seq_item:Nn\l_@@_tmpa_seq {2} }
   \@@_role_get:ooNN 
     { \g_@@_struct_tag_tl }
     { \g_@@_struct_tag_NS_tl}
     \l_@@_tmpa_tl
     \l_@@_tmpb_tl
   \tl_gset:Ne \g_@@_struct_tag_tl {\l_@@_tmpa_tl}
   \tl_gset:Ne \g_@@_struct_tag_NS_tl{\l_@@_tmpb_tl}
   \@@_check_structure_tag:N \g_@@_struct_tag_tl
 }
\socket_assign_plug:nn { tag/struct/tag } {latex-tags} 
%    \end{macrocode}

% \begin{structkeydecl}
%  {
%    label,
%    stash,
%    parent,
%    firstkid,
%    tag,
%    title,
%    title-o,
%    alt,
%    actualtext,
%    lang,
%    ref,
%    E,
%    phoneme
%  }
%    \begin{macrocode}
\keys_define:nn { @@ / struct }
  {
    label .code:n        = 
      {
        \prop_gput:Nee\g_@@_struct_label_num_prop 
          {#1}{\int_use:N \c@g_@@_struct_abs_int}
        \@@_property_record:eo
          {tagpdfstruct-#1}
          { \c_@@_property_struct_clist } 
      },
    stash .bool_set:N    = \l_@@_struct_elem_stash_bool,
    parent .code:n       =
      {
        \bool_lazy_and:nnTF
          {
            \prop_if_exist_p:c { g_@@_struct_\int_eval:n {#1}_prop }
          }
          {
            \int_compare_p:nNn {#1}<{\c@g_@@_struct_abs_int}
          }
          { \tl_set:Ne \l_@@_struct_stack_parent_tmpa_tl { \int_eval:n {#1} } }
          {
            \msg_warning:nnee { tag } { struct-unknown }
              { \int_eval:n {#1} }
              { parent~key~ignored }
          }
      },
    parent .default:n    = {-1},
    parent-tag .code:n =
     {
        \prop_get:NeNTF \g__tag_role_tags_NS_prop {#1} \l_@@_tmp_unused_tl
          {
           \seq_set_split:Nne \l_@@_tmpa_seq { / } 
             {#1/\l_@@_tmp_unused_tl}
          }
          {
            \seq_set_split:Nne \l_@@_tmpa_seq { / } 
              {#1/}
          }        
        \tl_set:Ne \l_@@_struct_parenttag_tl   { \seq_item:Nn\l_@@_tmpa_seq {1} }
        \tl_set:Ne \l_@@_struct_parenttag_NS_tl{ \seq_item:Nn\l_@@_tmpa_seq {2} }
        \@@_role_get:ooNN 
          { \l_@@_struct_parenttag_tl }
          { \l_@@_struct_parenttag_NS_tl}
          \l_@@_tmpa_tl
          \l_@@_tmpb_tl
        \tl_set:No \l_@@_struct_parenttag_tl   {\l_@@_tmpa_tl}
        \tl_set:No \l_@@_struct_parenttag_NS_tl{\l_@@_tmpb_tl}
        \@@_check_structure_tag:N \l_@@_struct_parenttag_tl
     },    
	firstkid .code:n = { \tl_set:Nn \l_@@_struct_addkid_tl {left} },   
    tag   .code:n        = % S property
      {
        \socket_use:nn { tag/struct/tag }{#1}
      },
    title .code:n        = % T property
      {
        \str_set_convert:Nnnn
          \l_@@_tmpa_str
          { #1 }
          { default }
          { utf16/hex }
        \@@_struct_prop_gput:nne
          { \int_use:N \c@g_@@_struct_abs_int }
          { T }
          { <\l_@@_tmpa_str> }
      },
    title-o .code:n        = % T property
      {
        \str_set_convert:Nonn
          \l_@@_tmpa_str
          { #1 }
          { default }
          { utf16/hex }
        \@@_struct_prop_gput:nne
          { \int_use:N \c@g_@@_struct_abs_int }
          { T }
          { <\l_@@_tmpa_str> }
      },
    alt .code:n      = % Alt property
      {
       \tl_if_empty:oF{#1}
         {
          \str_set_convert:Noon
            \l_@@_tmpa_str
            { #1 }
            { default }
            { utf16/hex }
          \@@_struct_prop_gput:nne
            { \int_use:N \c@g_@@_struct_abs_int }
            { Alt }
            { <\l_@@_tmpa_str> }
         }   
      },
    alttext .meta:n = {alt=#1},
    actualtext .code:n  = % ActualText property
      {
        \tl_if_empty:oF{#1}
          {
           \str_set_convert:Noon
             \l_@@_tmpa_str
             { #1 }
             { default }
             { utf16/hex }
           \@@_struct_prop_gput:nne
             { \int_use:N \c@g_@@_struct_abs_int }
             { ActualText }
             { <\l_@@_tmpa_str>}
          }  
      },
     phoneme .code:n  = % Phoneme property
      {
        \tl_if_empty:oF{#1}
          {
           \str_set_convert:Noon
             \l_@@_tmpa_str
             { #1 }
             { default }
             { utf16/hex }
           \@@_struct_prop_gput:nne
             { \int_use:N \c@g_@@_struct_abs_int }
             { Phoneme }
             { <\l_@@_tmpa_str>}
          }  
      },     
    lang .code:n        = % Lang property
      {
        \@@_struct_prop_gput:nne
          { \int_use:N \c@g_@@_struct_abs_int }
          { Lang }
          { (#1) }
      },
    }  
%    \end{macrocode}
% \end{structkeydecl}
% Ref is rather special as it values are often
% known only at the end of the document.
% It therefore stores it values as
% clist of commands which are executed at the end of the document,
% when the structure elements are written.
% \begin{macro}{\@@_struct_Ref_obj:nN,\@@_struct_Ref_label:nN,
%  \@@_struct_Ref_dest:nN,\@@_struct_Ref_num:nN}
% this commands are helper commands that are stored as clist
% in the Ref key of a structure. They are executed when the structure
% elements are written in  \cs{@@_struct_write_obj}. They are used
% in \cs{@@_struct_format_Ref}. They allow to add a Ref by object reference,
% label, destname and structure number
%    \begin{macrocode}
\cs_new_protected:Npn \@@_struct_Ref_obj:nN #1 #2 %#1 a object reference
  {
    \tl_put_right:Ne#2
      {
        \c_space_tl#1
      }
  }
  
\cs_new_protected:Npn \@@_struct_Ref_label:nN #1 #2 %#1 a label
  {
    \prop_get:NnNTF \g_@@_struct_label_num_prop {#1} \l_@@_tmpb_tl
      {
        \tl_put_right:Ne#2
          {
            \c_space_tl\tag_struct_object_ref:e{ \l_@@_tmpb_tl }
          }
      }    
      {
        \msg_warning:nnn {tag}{struct-Ref-unknown}{Label~'#1'}
      }    
  }
\cs_new_protected:Npn \@@_struct_Ref_dest:nN #1 #2 %#1 a dest name
  { 
    \prop_get:NnNTF \g_@@_struct_dest_num_prop {#1} \l_@@_tmpb_tl
      {
        \tl_put_right:Ne#2
          {
            \c_space_tl\tag_struct_object_ref:e{ \l_@@_tmpb_tl }
          }
      }    
      {
        \msg_warning:nnn {tag}{struct-Ref-unknown}{Destination~'#1'}
      }
  }
\cs_new_protected:Npn \@@_struct_Ref_num:nN #1 #2 %#1 a structure number
  {
    \tl_put_right:Ne#2
      {
        \c_space_tl\tag_struct_object_ref:e{ #1 }
      }  
  }
   
%    \end{macrocode}
% \end{macro}
% \begin{structkeydecl}{ref,E,}
%    \begin{macrocode}
\keys_define:nn { @@ / struct }
  {
    ref .code:n        = % ref property
      {
        \clist_map_inline:on {#1}
          {
            \tag_struct_gput:nne
              {\int_use:N \c@g_@@_struct_abs_int}{ref_label}{ ##1 }
          }
      },
    E .code:n        = % E property
      {
        \str_set_convert:Nnon
          \l_@@_tmpa_str
          { #1 }
          { default }
          { utf16/hex }
        \@@_struct_prop_gput:nne
          { \int_use:N \c@g_@@_struct_abs_int }
          { E }
          { <\l_@@_tmpa_str> }
      },
  }
%    \end{macrocode}
% \end{structkeydecl}
% 
% \begin{structkeydecl}{AF, AFref,AFinline,AFinline-o,texsource,mathml}
% keys for the AF keys (associated files). They use commands from l3pdffile!
% The stream variants use txt as extension to get the mimetype.
% TODO: check if this should be configurable. For math we will perhaps need another
% extension.
% AF/AFref is an array and can be used more than once, so we store it in a tl.
% which is expanded.
% AFinline currently uses the fix extension txt. 
% texsource is a special variant which creates a tex-file, it expects a
% tl-var as value (e.g. from math grabbing)
% 
% \begin{variable}{\g_@@_struct_AFobj_int}
% This variable is used to number the AF-object names
%    \begin{macrocode}
\int_new:N\g_@@_struct_AFobj_int
%    \end{macrocode}
% \end{variable}
% 
%    \begin{macrocode}
\cs_generate_variant:Nn \pdffile_embed_stream:nnN {neN}
\cs_new_protected:Npn \@@_struct_add_inline_AF:nn #1 #2  
% #1 content, #2 extension
 {
   \tl_if_empty:nF{#1}
     {
      \group_begin:
      \int_gincr:N \g_@@_struct_AFobj_int
      \pdffile_embed_stream:neN
        {#1}
        {tag-AFfile\int_use:N\g_@@_struct_AFobj_int.#2}
        \l_@@_tmpa_tl          
        \@@_struct_add_AF:ee
          { \int_use:N \c@g_@@_struct_abs_int }
          { \l_@@_tmpa_tl }
        \@@_struct_prop_gput:nne
          { \int_use:N \c@g_@@_struct_abs_int } 
          { AF }
          {
            [
              \tl_use:c
               { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_AF_tl }
            ]
          }               
      \group_end:
     } 
 } 
     
\cs_generate_variant:Nn \@@_struct_add_inline_AF:nn {on} 
%    \end{macrocode}
% 
%    \begin{macrocode}
\cs_new_protected:Npn \@@_struct_add_AF:nn #1 #2 
% #1 struct num #2 object reference
  {
     \tl_if_exist:cTF
       {
         g_@@_struct_#1_AF_tl
       }
       {
         \tl_gput_right:ce
           { g_@@_struct_#1_AF_tl }
           {  \c_space_tl #2 }
       }
       {
          \tl_new:c
            { g_@@_struct_#1_AF_tl }
          \tl_gset:ce
            { g_@@_struct_#1_AF_tl }
            { #2 }
       }
  }
\cs_generate_variant:Nn \@@_struct_add_AF:nn {en,ee}
\keys_define:nn { @@ / struct }
 {
    AF .code:n        = % AF property
      {
        \pdf_object_if_exist:eTF {#1}
          {
            \@@_struct_add_AF:ee 
             { \int_use:N \c@g_@@_struct_abs_int }{\pdf_object_ref:e {#1}}
            \@@_struct_prop_gput:nne
             { \int_use:N \c@g_@@_struct_abs_int }            
             { AF }
             {
               [
                 \tl_use:c
                   { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_AF_tl }
               ]
             }
          }
          {
             % message?
          }
      },
    AFref .code:n        = % AF property
      {
        \tl_if_empty:eF {#1}
         {
           \@@_struct_add_AF:ee { \int_use:N \c@g_@@_struct_abs_int }{#1}
           \@@_struct_prop_gput:nne
             { \int_use:N \c@g_@@_struct_abs_int }            
             { AF }
             {
               [
                 \tl_use:c
                 { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_AF_tl }
               ]
             }
         } 
      },      
   ,AFinline .code:n =
     {
       \@@_struct_add_inline_AF:nn {#1}{txt}
     }
   ,AFinline-o .code:n =
     {
       \@@_struct_add_inline_AF:on {#1}{txt}
     }
   ,texsource .code:n =
    {
      \group_begin:
      \pdfdict_put:nnn { l_pdffile/Filespec } {Desc}{(TeX~source)}
      \pdfdict_put:nnn { l_pdffile/Filespec }{AFRelationship} { /Source }
      \@@_struct_add_inline_AF:on {#1}{tex}
      \group_end:
    }  
   ,mathml .code:n =
     {
      \group_begin:
      \pdfdict_put:nnn { l_pdffile/Filespec } {Desc}{(mathml~representation)}
      \pdfdict_put:nnn { l_pdffile/Filespec }{AFRelationship} { /Supplement }
      \pdfdict_put:nne { l_pdffile }{Subtype}
         { \pdf_name_from_unicode_e:n{application/mathml+xml} }
      \@@_struct_add_inline_AF:on {#1}{xml}
      \group_end:  
    }  
 }
%    \end{macrocode}
% \end{structkeydecl}
% \begin{setupkeydecl}{root-AF}
% The root structure can take AF keys too, so we provide a key for it.
% This key is used with |\tagpdfsetup|, not in a structure!
%    \begin{macrocode}
\keys_define:nn { @@ / setup }
  {
    root-AF .code:n =
     {
        \pdf_object_if_exist:nTF {#1}
          {
            \@@_struct_add_AF:ee { 1 }{\pdf_object_ref:n {#1}}
            \@@_struct_prop_gput:nne
             { 1 } 
             { AF }
             {
               [
                 \tl_use:c
                   { g_@@_struct_1_AF_tl }
               ]
             }
          }
          {

          }
      },
  }
%    \end{macrocode}
% \end{setupkeydecl}
% 
% \begin{setupkeydecl}{root-supplemental-file}
% This key allows to add a file as root-AF with relationship Supplement.
% This is typically need to add a css or an html
%    \begin{macrocode}
\keys_define:nn { @@ / setup }
  {
    root-supplemental-file .code:n =
     {
       \group_begin:
       \pdfdict_put:nnn {l_pdffile/Filespec} {AFRelationship}{/Supplement}
       \int_gincr:N \g_@@_unique_cnt_int
       \pdffile_embed_file:eee 
        {#1}
        {#1}
        {__tag_latex_css_\int_use:N\g_@@_unique_cnt_int}
       \keys_set:nn 
        {__tag / setup}
        {root-AF={__tag_latex_css_\int_use:N\g_@@_unique_cnt_int}}
       \group_end:                 
     }
  }
%    \end{macrocode}
% \end{setupkeydecl}
% 
% \begin{setupkeydecl}{catalog-supplemental-file}
% This key allows to add a file as AF with relationship Supplement to the Catalog.
% This is typically need to add a css or an html.
%    \begin{macrocode}
\keys_define:nn { @@ / setup }
  {
    catalog-supplemental-file .code:n =
     {
       \group_begin:
       \pdfdict_put:nnn {l_pdffile/Filespec} {AFRelationship}{/Supplement}
       \int_gincr:N \g_@@_unique_cnt_int
       \pdffile_embed_file:eee 
        {#1}
        {#1}
        {@@_latex_css_\int_use:N\g_@@_unique_cnt_int}
       \pdfmanagement_add:nne
        {Catalog}
        {AF}
        {\pdf_object_ref:e{@@_latex_css_\int_use:N\g_@@_unique_cnt_int }} 
       \group_end:                 
     }
  }
%    \end{macrocode}
% \end{setupkeydecl}
%
% \section{User commands}
% We allow to set a language by default
% \begin{macro}{\l_@@_struct_lang_tl}
%    \begin{macrocode}
\tl_new:N \l_@@_struct_lang_tl
%</package>
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\tag_struct_begin:n,\tag_struct_end:}
%    \begin{macrocode}
%<base>\cs_new_protected:Npn \tag_struct_begin:n #1 {\int_gincr:N \c@g_@@_struct_abs_int}
%<base>\cs_new_protected:Npn \tag_struct_end:{}
%<base>\cs_new_protected:Npn \tag_struct_end:n{}
%<*package|debug>
%<package>\cs_set_protected:Npn \tag_struct_begin:n #1 %#1 key-val
%<debug>\cs_set_protected:Npn \tag_struct_begin:n #1 %#1 key-val
  {
%<package>\@@_check_if_active_struct:T
%<debug>\@@_check_if_active_struct:TF
      {
        \group_begin:
        \int_gincr:N \c@g_@@_struct_abs_int
        \@@_prop_new:c  { g_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_prop }
%<debug>         \prop_new:c { g_@@_struct_debug_\int_eval:n {\c@g_@@_struct_abs_int}_prop }    
        \@@_new_output_prop_handler:n {\int_eval:n { \c@g_@@_struct_abs_int }}
        \@@_seq_new:c  { g_@@_struct_kids_\int_eval:n { \c@g_@@_struct_abs_int }_seq}
%<debug>         \seq_new:c { g_@@_struct_debug_kids_\int_eval:n {\c@g_@@_struct_abs_int}_seq }            
          \pdf_object_new_indexed:nn { @@/struct }
            { \c@g_@@_struct_abs_int }
         \@@_struct_prop_gput:nnn
            { \int_use:N \c@g_@@_struct_abs_int }          
            { Type }
            { /StructElem }
         \tl_if_empty:NF \l_@@_struct_lang_tl
           {
             \@@_struct_prop_gput:nne
              { \int_use:N \c@g_@@_struct_abs_int }
              { Lang }
              { (\l_@@_struct_lang_tl) }
           }   
         \@@_struct_prop_gput:nnn
            { \int_use:N \c@g_@@_struct_abs_int }          
            { Type }
            { /StructElem }
            
        \tl_set:Nn \l_@@_struct_stack_parent_tmpa_tl {-1}
        \keys_set:nn { @@ / struct} { #1 }
%    \end{macrocode}
%    \begin{macrocode}
        \@@_struct_set_tag_info:eoo 
          { \int_use:N \c@g_@@_struct_abs_int }
          { \g_@@_struct_tag_tl }              
          { \g_@@_struct_tag_NS_tl }   
        \@@_check_structure_has_tag:n { \int_use:N \c@g_@@_struct_abs_int }           
%    \end{macrocode}
% The structure number of the parent is either taken from the stack or
% has been set with the parent key.
%    \begin{macrocode}
        \int_compare:nNnT { \l_@@_struct_stack_parent_tmpa_tl } = { -1 }
          {
            \seq_get:NNF
              \g_@@_struct_stack_seq
              \l_@@_struct_stack_parent_tmpa_tl
              {
                \msg_error:nn { tag } { struct-faulty-nesting }
              }
           }
        \seq_gpush:NV \g_@@_struct_stack_seq  \c@g_@@_struct_abs_int
        \@@_role_get:ooNN 
          { \g_@@_struct_tag_tl }
          { \g_@@_struct_tag_NS_tl }
          \l_@@_struct_roletag_tl 
          \l_@@_struct_roletag_NS_tl 
%    \end{macrocode}
% We push the role tag on the stack:% 
%    \begin{macrocode}
        \seq_gpush:Ne \g_@@_struct_tag_stack_seq 
          {{\g_@@_struct_tag_tl}{\l_@@_struct_roletag_tl}}
        \tl_gset:NV   \g_@@_struct_stack_current_tl \c@g_@@_struct_abs_int
        \@@_struct_set_attribute:
        %\seq_show:N   \g_@@_struct_stack_seq
%    \end{macrocode}
% the rolemapped role and its NS are stored in the rolemap key.
%    \begin{macrocode}
         \@@_struct_prop_gput:nne
           { \int_use:N \c@g_@@_struct_abs_int }           
           { rolemap }
           {
             {\l_@@_struct_roletag_tl}{\l_@@_struct_roletag_NS_tl} 
           }  
%    \end{macrocode}
% If the role is one of Part, Div, NonStruct we have to (sometimes) retrieve
% the \enquote{real} parent for the parent/child test. The role of this 
% real parent is stored in the key \texttt{parentrole}. 
% If the current structure is stashed
% we use UNKNOWN as real parent if the current structure is rolemapped to
% Part, Div or NonStruct so that the children can detect that no reliable check is possible.
% For structures that are not rolemapped to Part, Div, NonStruct, 
% \texttt{parentrole} and \texttt{rolemap}  are always equal. 
%    \begin{macrocode}
        \str_case:onTF { \l_@@_struct_roletag_tl }
         {
           {Part} {}
           {Div}  {}
           {NonStruct} {}
         } 
         {
           \bool_if:NTF \l_@@_struct_elem_stash_bool
           {
             \@@_struct_prop_gput:nne
                { \int_use:N \c@g_@@_struct_abs_int }          
                { parentrole }
                {
                  {\l_@@_struct_parenttag_tl}{\l_@@_struct_parenttag_NS_tl}
                } 
           }
           {
             \prop_get:cnNT 
              { g_@@_struct_ \l_@@_struct_stack_parent_tmpa_tl _prop }
              { parentrole }
              \l_@@_get_tmpc_tl
              {
                \@@_struct_prop_gput:nno
                  { \int_use:N \c@g_@@_struct_abs_int }          
                  { parentrole }
                  {
                    \l_@@_get_tmpc_tl
                  } 
              }  
           } 
         }
         {
            \@@_struct_prop_gput:nne
              { \int_use:N \c@g_@@_struct_abs_int }            
              { parentrole }
              {
                {\l_@@_struct_roletag_tl}{\l_@@_struct_roletag_NS_tl} 
              }
         }
%    \end{macrocode}
%
%    \begin{macrocode}
        \bool_if:NF
          \l_@@_struct_elem_stash_bool
          {
%    \end{macrocode}
%  check if the tag can be used inside the parent. It only makes sense,
%  if the structure is actually used here, so it is guarded by the stash boolean.  
%    \begin{macrocode}
            \socket_use:nn{tag/check/parent-child}
             {
               \@@_struct_check_parent_child:oo
                 { \l_@@_struct_stack_parent_tmpa_tl }
                 { \int_use:N \c@g_@@_struct_abs_int }
             } 
%    \end{macrocode}
% Set the Parent structure number.
%    \begin{macrocode}
            \@@_struct_prop_gput:nne
              { \int_use:N \c@g_@@_struct_abs_int }           
              { parentnum }
              {
                \l_@@_struct_stack_parent_tmpa_tl 
              }              
%    \end{macrocode}
%    \begin{macrocode}
            %record this structure as kid:
            %\tl_show:N \g_@@_struct_stack_current_tl
            %\tl_show:N \l_@@_struct_stack_parent_tmpa_tl
            \use:c { @@_struct_kid_struct_gput_ \l_@@_struct_addkid_tl :ee }
               { \l_@@_struct_stack_parent_tmpa_tl }
               { \g_@@_struct_stack_current_tl }
            %\prop_show:c { g_@@_struct_\g_@@_struct_stack_current_tl _prop }
            %\seq_show:c {g_@@_struct_kids_\l_@@_struct_stack_parent_tmpa_tl _seq}
          }
%    \end{macrocode}
% the debug mode stores in second prop and replaces value with more suitable ones.
% (If the structure is updated later this gets perhaps lost, but well ...)
% This must be done outside of the stash boolean.
%    \begin{macrocode}
%<debug>           \prop_gset_eq:cc 
%<debug>             { g_@@_struct_debug_\int_eval:n {\c@g_@@_struct_abs_int}_prop }
%<debug>             { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop }
%<debug>           \prop_gput:cne
%<debug>             { g_@@_struct_debug_\int_eval:n {\c@g_@@_struct_abs_int}_prop }
%<debug>             { parentnum }
%<debug>             { 
%<debug>               \bool_if:NTF \l_@@_struct_elem_stash_bool
%<debug>                 {no~parent:~stashed}
%<debug>                 {
%<debug>                   \l_@@_struct_stack_parent_tmpa_tl\c_space_tl =~
%<debug>                   \prop_item:cn{ g__tag_struct_\l_@@_struct_stack_parent_tmpa_tl _prop }{S}
%<debug>                 }  
%<debug>             }
%<debug>           \prop_gput:cne
%<debug>             { g_@@_struct_debug_\int_eval:n {\c@g_@@_struct_abs_int}_prop }
%<debug>             { NS }
%<debug>             { \g_@@_struct_tag_NS_tl } 
%    \end{macrocode}
%    \begin{macrocode}
        %\prop_show:c { g_@@_struct_\g_@@_struct_stack_current_tl _prop }
        %\seq_show:c {g_@@_struct_kids_\l_@@_struct_stack_parent_tmpa_tl _seq}
%<debug> \@@_debug_struct_begin_insert:n { #1 }
        \group_end:
     }
%<debug>{ \@@_debug_struct_begin_ignore:n { #1 }}
  }
%<package>\cs_set_protected:Nn \tag_struct_end:
%<debug>\cs_set_protected:Nn \tag_struct_end:
  { %take the current structure num from the stack:
    %the objects are written later, lua mode hasn't all needed info yet
    %\seq_show:N \g_@@_struct_stack_seq
%<package>\@@_check_if_active_struct:T
%<debug>\@@_check_if_active_struct:TF
      {
        \seq_gpop:NN   \g_@@_struct_tag_stack_seq \l_@@_tmpa_tl
        \seq_gpop:NNTF \g_@@_struct_stack_seq \l_@@_tmpa_tl
          {
            \@@_check_info_closing_struct:o { \g_@@_struct_stack_current_tl }
          }
          { \@@_check_no_open_struct: }
        % get the previous one, shouldn't be empty as the root should be there
        \seq_get:NNTF \g_@@_struct_stack_seq \l_@@_tmpa_tl
          {
            \tl_gset:No  \g_@@_struct_stack_current_tl { \l_@@_tmpa_tl }
            \@@_struct_set_attribute:
          }
          {
            \@@_check_no_open_struct:
          }
       \seq_get:NNT \g_@@_struct_tag_stack_seq \l_@@_tmpa_tl
          {
            \tl_gset:Ne \g_@@_struct_tag_tl 
              { \exp_last_unbraced:No\use_i:nn { \l_@@_tmpa_tl } }
            \prop_get:NoNT\g_@@_role_tags_NS_prop { \g_@@_struct_tag_tl} \l_@@_tmpa_tl
             {
               \tl_gset:Ne \g_@@_struct_tag_NS_tl { \l_@@_tmpa_tl } 
             }  
          }
%<debug>\@@_debug_struct_end_insert:
      }
%<debug>{\@@_debug_struct_end_ignore:}
  }

\cs_set_protected:Npn \tag_struct_end:n #1
 {
%<debug>    \@@_check_if_active_struct:T{\@@_debug_struct_end_check:n{#1}}
   \tag_struct_end:
 }  
%</package|debug>
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\tag_struct_use:n}
% This command allows to use a stashed structure in another place.
% TODO: decide how it should be guarded. Probably by the struct-check.
%    \begin{macrocode}
%<base>\cs_new_protected:Npn \tag_struct_use:n #1 {}
%<*package|debug>
\cs_set_protected:Npn \tag_struct_use:n #1 %#1 is the label
  { 
    \@@_check_if_active_struct:T
      {
        \prop_if_exist:cTF
          { g_@@_struct_\property_ref:enn{tagpdfstruct-#1}{tagstruct}{unknown}_prop } %
          {
            \@@_check_struct_used:n {#1}
            \tl_set:Ne \l_@@_get_child_tmpa_tl 
              { \property_ref:enn{tagpdfstruct-#1}{tagstruct}{1} } 
%    \end{macrocode}
%  add the label structure as kid to the current structure (can be the root)
%    \begin{macrocode}           
            \@@_struct_kid_struct_gput_right:ee
              { \g_@@_struct_stack_current_tl }
              { \l_@@_get_child_tmpa_tl }
%    \end{macrocode}
% add the current structure to the labeled one as parents
%    \begin{macrocode}            
            \@@_prop_gput:cne
              { g_@@_struct_ \l_@@_get_child_tmpa_tl _prop }    
              { parentnum }
              {
                \g_@@_struct_stack_current_tl 
              }               
%    \end{macrocode}
% debug code
%    \begin{macrocode}
%<debug>           \prop_gput:cne
%<debug>             { g_@@_struct_debug_ \l_@@_get_child_tmpa_tl _prop }
%<debug>             { parentnum }
%<debug>             { 
%<debug>               \g_@@_struct_stack_current_tl\c_space_tl=~
%<debug>               \g_@@_struct_tag_tl
%<debug>             }
%    \end{macrocode}
%   check if the tag is allowed as child.
%   If the tag of the child after rolemapping is \emph{not} one of Part, Div, NonStruct,
%   then the parentrole field will be identically to the rolemap field and can be used for 
%   a check. 
%   Otherwise the parentrole will contain latex:STASHED (if not changed with the \texttt{parent-tag}
%   key when the structure was stashed) and will produce a warning. 
%    \begin{macrocode}
           \socket_use:nn{tag/check/parent-child}
            {
              \@@_struct_use_check_parent_child:oo 
                { \g_@@_struct_stack_current_tl }
                { \l_@@_get_child_tmpa_tl }
            }               
          }
          {
            \msg_warning:nnn{ tag }{struct-label-unknown}{#1}
          }
      }
  }
%</package|debug>
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\tag_struct_use_num:n}
% This command allows to use a stashed structure in another place.
% differently to the previous command it doesn't use a label but directly 
% a structure number to find the parent.
% TODO: decide how it should be guarded. Probably by the struct-check.
%    \begin{macrocode}
%<base>\cs_new_protected:Npn \tag_struct_use_num:n #1 {}
%<*package|debug>
\cs_set_protected:Npn \tag_struct_use_num:n #1 %#1 is structure number
  { 
    \@@_check_if_active_struct:T
      {
        \prop_if_exist:cTF
          { g_@@_struct_#1_prop } %
          {
            \prop_get:cnNT
              {g_@@_struct_#1_prop}
              {parentnum}
              \l_@@_tmpa_tl
              {
                \msg_warning:nnn { tag } {struct-used-twice} {#1}
              }
%    \end{macrocode}
% add the \#1 structure as kid to the current structure (can be the root)
%    \begin{macrocode}
            \@@_struct_kid_struct_gput_right:ee
              { \g_@@_struct_stack_current_tl }
              { #1 }
%    \end{macrocode}
% add the current structure to \#1 as parent
%    \begin{macrocode}
             \@@_struct_prop_gput:nne
              { #1 }            
              { parentnum }
              {
                \g_@@_struct_stack_current_tl 
              }              
%<debug>           \prop_gput:cne
%<debug>             { g_@@_struct_debug_#1_prop }
%<debug>             { parentnum }
%<debug>             { 
%<debug>               \g_@@_struct_stack_current_tl\c_space_tl=~
%<debug>               \g_@@_struct_tag_tl
%<debug>             }
%    \end{macrocode}
%   check if the tag is allowed as child.
%    \begin{macrocode}
            \socket_use:nn{tag/check/parent-child}
             {
               \@@_struct_use_check_parent_child:oo
                 {\g_@@_struct_stack_current_tl}
                 {#1}
             } 
          }
          {
            \msg_warning:nnn{ tag }{struct-label-unknown}{#1}
          }
      }
  }
%</package|debug>
%    \end{macrocode}
% \end{macro}
% \begin{macro}[EXP]{\tag_struct_object_ref:n}
% This is a command that allows to reference a structure. The argument is the
% number which can be get for the current structure with |\tag_get:n{struct_num}|
% TODO check if it should be in base too.
%    \begin{macrocode}
%<*package>
\cs_new:Npn \tag_struct_object_ref:n #1
 {
   \pdf_object_ref_indexed:nn {@@/struct}{ #1 }
 }
\cs_generate_variant:Nn \tag_struct_object_ref:n {e}
%</package>
%    \end{macrocode}
%
% \end{macro}
%
% \begin{macro}{\tag_struct_gput:nnn}
% This is a command that allows to update the data of a structure.
% This often can't done simply by replacing the value, as we have to 
% preserve and extend existing content. We use therefore dedicated functions
% adjusted to the key in question. 
% The first argument is the number of the structure, 
% the second a keyword referring to a function,
% the third the value. Currently the existing keywords are mostly related
% to the \texttt{Ref} key (an array). 
% The keyword \texttt{ref} takes as value an explicit object reference to 
% a structure. The keyword \texttt{ref_label} expects as value a label name (from
% a label set in a \cs{tagstructbegin} command). The keyword \texttt{ref_dest}
% expects a destination name set with \cs{MakeLinkTarget}. It then will refer to 
% the structure in which this \cs{MakeLinkTarget} was used. The
% keyword \texttt{ref_num} expects a structure number. 
% At last there is the keyword \texttt{attribute} which allows to add or extend the \texttt{/A}
% key of the structure. The value is the content of one attribute dictionary, so for example
% \texttt{/O /Layout /BBox [10 10 50 50]}. The content is stored in an object and the object
% reference is than added to the \texttt{/A}.
% 
%    \begin{macrocode}
%<base>\cs_new_protected:Npn \tag_struct_gput:nnn #1 #2 #3{}
%<*package>
\cs_set_protected:Npn \tag_struct_gput:nnn #1 #2 #3
 {
   \cs_if_exist_use:cF {@@_struct_gput_data_#2:nn}
    { %warning??
      \use_none:nn
    }
    {#1}{#3}
 }
\cs_generate_variant:Nn \tag_struct_gput:nnn {ene,nne}
%</package>
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_struct_gput_data_ref_aux:nnn}
%    \begin{macrocode}
%<*package>
\cs_new_protected:Npn \@@_struct_gput_data_ref_aux:nnn #1 #2 #3
  % #1 receiving struct num, #2 key word #3 value
   {
     \prop_get:cnNTF
       { g_@@_struct_#1_prop }
       {Ref}
       \l_@@_get_tmpc_tl
       {
         \tl_put_right:No \l_@@_get_tmpc_tl 
           {\cs:w @@_struct_Ref_#2:nN \cs_end: {#3},}
       }
       {
         \tl_set:No \l_@@_get_tmpc_tl 
           {\cs:w @@_struct_Ref_#2:nN \cs_end: {#3},}
       } 
       \@@_struct_prop_gput:nno
         { #1 }
         { Ref }
         { \l_@@_get_tmpc_tl }        
    }
\cs_new_protected:Npn \@@_struct_gput_data_ref:nn #1 #2 
  {
    \@@_struct_gput_data_ref_aux:nnn {#1}{obj}{#2}
  }    
\cs_new_protected:Npn \@@_struct_gput_data_ref_label:nn #1 #2 
  {
    \@@_struct_gput_data_ref_aux:nnn {#1}{label}{#2}
  }      
\cs_new_protected:Npn \@@_struct_gput_data_ref_dest:nn #1 #2 
  {
    \@@_struct_gput_data_ref_aux:nnn {#1}{dest}{#2}
  }      
\cs_new_protected:Npn \@@_struct_gput_data_ref_num:nn #1 #2 
  {
    \@@_struct_gput_data_ref_aux:nnn {#1}{num}{#2}
  }   
     
\cs_generate_variant:Nn \@@_struct_gput_data_ref:nn {ee,no}
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_struct_gput_data_attribute:nn}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_struct_gput_data_attribute:nn #1 #2 
  {
    \pdf_object_unnamed_write:nn {dict} {#2}
    \prop_get:cnNTF { g_@@_struct_#1_prop }{A} \l_@@_tmpa_tl
      {
        \tl_remove_once:Nn\l_@@_tmpa_tl{[}
        \tl_remove_once:Nn\l_@@_tmpa_tl{]}
        \@@_prop_gput:cne { g_@@_struct_#1_prop }
         { A }
         {
           [ \l_@@_tmpa_tl \c_space_tl \pdf_object_ref_last: ]
         }       
      }
      {
        \@@_prop_gput:cne { g_@@_struct_#1_prop }
         { A }
         { \pdf_object_ref_last: }
      }
  }    
%    \end{macrocode}
% \end{macro}
% \begin{macro}
%   {
%     \tag_struct_insert_annot:nn,
%     \tag_struct_insert_annot:ee,
%     \tag_struct_insert_annot:ee
%   }
% \begin{macro}[EXP]
%   {
%     \tag_struct_parent_int:
%   }
% This are the user command to insert annotations. They must be used
% together to get the numbers right. They use a counter to the
% |StructParent| and \cs{tag_struct_insert_annot:nn}  increases the
% counter given back by \cs{tag_struct_parent_int:}.
%
% It must be used together with |\tag_struct_parent_int:| to insert an
% annotation.
% TODO: decide how it should be guarded if tagging is deactivated.
%    \begin{macrocode}
\cs_new_protected:Npn \tag_struct_insert_annot:nn #1 #2 %#1 should be an object reference
                                                        %#2 struct parent num
  {
    \@@_check_if_active_struct:T
      {
        \@@_struct_insert_annot:nn {#1}{#2}
      }
  }

\cs_generate_variant:Nn \tag_struct_insert_annot:nn {xx,ee}
\cs_new:Npn \tag_struct_parent_int: {\int_use:c { c@g_@@_parenttree_obj_int }}

%</package>

%    \end{macrocode}
% \end{macro}
% \end{macro}
% \section{Attributes and attribute classes}
%    \begin{macrocode}
%<*header>
\ProvidesExplPackage {tagpdf-attr-code} {2025-07-16} {0.99u}
  {part of tagpdf - code related to attributes and attribute classes}
%</header>
%    \end{macrocode}
% \subsection{Variables}
%  \begin{variable}
%   {
%     ,\g_@@_attr_entries_prop
%     ,\g_@@_attr_class_used_prop
%     ,\g_@@_attr_objref_prop
%     ,\l_@@_attr_value_tl
%   }
% |\g_@@_attr_entries_prop| will store attribute names and their dictionary content.\\
% |\g_@@_attr_class_used_prop| will hold the attributes which have been used as
% class name.
% |\l_@@_attr_value_tl| is used to build the attribute array or key.
% Every time an attribute is used for the first time, and object is created
% with its content, the name-object reference relation is stored in
% |\g_@@_attr_objref_prop|
%    \begin{macrocode}
%<*package>
\prop_new:N \g_@@_attr_entries_prop
\prop_new_linked:N \g_@@_attr_class_used_prop
\tl_new:N   \l_@@_attr_value_tl
\prop_new:N \g_@@_attr_objref_prop %will contain obj num of used attributes
%    \end{macrocode}
% This seq is currently kept for compatibility with the table code.
%    \begin{macrocode}
\seq_new:N\g_@@_attr_class_used_seq
%    \end{macrocode}
% \end{variable}
% \subsection{Commands and keys}
% \begin{macro}{\@@_attr_new_entry:nn,role/new-attribute (setup-key), newattribute (deprecated)}
% This allows to define attributes. Defined attributes
% are stored in a global property. |role/new-attribute| expects
% two brace group, the name and the content. The content typically
% needs an |/O| key for the owner. An example look like
% this.
% 
% TODO: consider to put them directly in the ClassMap, that is perhaps
% more effective. 
% \begin{verbatim}
%  \tagpdfsetup
%   {
%    role/new-attribute =
%     {TH-col}{/O /Table /Scope /Column},
%    role/new-attribute =
%     {TH-row}{/O /Table /Scope /Row},
%    }
% \end{verbatim}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_attr_new_entry:nn #1 #2 %#1:name, #2: content
  {
    \prop_gput:Nen \g_@@_attr_entries_prop
      {\pdf_name_from_unicode_e:n{#1}}{#2}
  }
  
\cs_generate_variant:Nn \__tag_attr_new_entry:nn {ee}
\keys_define:nn { @@ / setup }
  {
    role/new-attribute .code:n =
      {
        \@@_attr_new_entry:nn #1
      }
%    \end{macrocode}
% deprecated name
%    \begin{macrocode}
   ,newattribute .code:n =
      {
        \@@_attr_new_entry:nn #1
      },   
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{structkeydecl}{attribute-class}
% attribute-class has to store the used attribute names so that
% they can be added to the ClassMap later.
%    \begin{macrocode}
\keys_define:nn { @@ / struct }
  {
    attribute-class .code:n =
     {
       \clist_set:Ne \l_@@_tmpa_clist { #1 }
       \seq_set_from_clist:NN \l_@@_tmpb_seq \l_@@_tmpa_clist
%    \end{macrocode}
%    we convert the names into pdf names with slash
%    \begin{macrocode}
       \seq_set_map_e:NNn \l_@@_tmpa_seq \l_@@_tmpb_seq
         {
           \pdf_name_from_unicode_e:n {##1}
         }
       \seq_map_inline:Nn \l_@@_tmpa_seq
         {
           \prop_get:NnNF \g_@@_attr_entries_prop {##1}\l_@@_tmpa_tl
             {
               \msg_error:nnn { tag } { attr-unknown } { ##1 }
             }
           \prop_gput:Nnn\g_@@_attr_class_used_prop { ##1} {}
         }
       \tl_set:Ne \l_@@_tmpa_tl
         {
           \int_compare:nT { \seq_count:N \l_@@_tmpa_seq > 1 }{[}
           \seq_use:Nn \l_@@_tmpa_seq  { \c_space_tl  }
           \int_compare:nT { \seq_count:N \l_@@_tmpa_seq > 1 }{]}
         }
       \int_compare:nT { \seq_count:N \l_@@_tmpa_seq > 0 }
         {
           \@@_struct_prop_gput:nne
             { \int_use:N \c@g_@@_struct_abs_int }          
             { C }
             { \l_@@_tmpa_tl }
          %\prop_show:c  { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop }
         }
     }
  }
%    \end{macrocode}
% \end{structkeydecl}
% \begin{structkeydecl}{attribute}
%    \begin{macrocode}
\keys_define:nn { @@ / struct }
  {
    attribute .code:n  = % A property (attribute, value currently a dictionary)
      {
        \clist_set:Ne          \l_@@_tmpa_clist { #1 }
        \clist_if_empty:NF \l_@@_tmpa_clist
         {
            \seq_set_from_clist:NN \l_@@_tmpb_seq \l_@@_tmpa_clist
%    \end{macrocode}
%    we convert the names into pdf names with slash
%    \begin{macrocode}
           \seq_set_map_e:NNn \l_@@_tmpa_seq \l_@@_tmpb_seq
             {
               \pdf_name_from_unicode_e:n {##1}
             }
            \tl_set:Ne \l_@@_attr_value_tl
              {
                \int_compare:nT { \seq_count:N \l_@@_tmpa_seq > 1 }{[}%]
              }
            \seq_map_inline:Nn \l_@@_tmpa_seq
              {
                \prop_get:NnNF \g_@@_attr_entries_prop {##1}\l_@@_tmp_unused_tl
                  {
                    \msg_error:nnn { tag } { attr-unknown } { ##1 }
                  }
                \prop_get:NnNF \g_@@_attr_objref_prop {##1}\l_@@_tmpa_tl
                  {%\prop_show:N \g_@@_attr_entries_prop
                    \pdf_object_unnamed_write:ne
                      { dict }
                      {
                        \prop_item:Nn\g_@@_attr_entries_prop {##1}
                      }
                    \prop_gput:Nne \g_@@_attr_objref_prop {##1} {\pdf_object_ref_last:}
                  }
                \tl_put_right:Ne \l_@@_attr_value_tl
                  {
                    \c_space_tl
                    \prop_item:Nn \g_@@_attr_objref_prop {##1}
                  }
     %     \tl_show:N \l_@@_attr_value_tl
              }
            \tl_put_right:Ne \l_@@_attr_value_tl
              { %[
                \int_compare:nT { \seq_count:N \l_@@_tmpa_seq > 1 }{]}%
              }
     %     \tl_show:N \l_@@_attr_value_tl
            \@@_struct_prop_gput:nne
              { \int_use:N \c@g_@@_struct_abs_int }           
              { A }
              { \l_@@_attr_value_tl }
         }    
    },
  }
%</package>
%    \end{macrocode}
% \end{structkeydecl}
% \end{implementation}
