diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx | 1024 |
1 files changed, 688 insertions, 336 deletions
diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx index e385fea951d..4c342f921c9 100644 --- a/Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx @@ -21,57 +21,278 @@ % https://github.com/u-fischer/tagpdf % % for those people who are interested. -% +%<*driver> +\RequirePackage{pdfmanagement-testphase} +\DeclareDocumentMetadata{} +\makeatletter +\declare@file@substitution{doc.sty}{doc-v3beta.sty} +\makeatother +\documentclass{l3doc} +\usepackage{array,booktabs,caption} +\hypersetup{pdfauthor=Ulrike Fischer, + pdftitle=tagpdf-tree module (tagpdf)} +\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.9, released 2021-06-29} +% \maketitle +% \begin{documentation} +% \section{Public Commands} +% \begin{function}{\tag_struct_begin:n,\tag_struct_end:} +% \begin{syntax} +% \cs{tag_struct_begin:n}\Arg{key-values}\\ +% \cs{tag_struct_end:} +% \end{syntax} +% These commands start and end a new structure. +% They don't start a group. They set all their values globally. +% \end{function} +% \begin{function}{\tag_struct_use:n} +% \begin{syntax} +% \cs{tag_struct_use:n}\Arg{label} +% \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} +% 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 annotion. +% 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} +% \section{Public keys} +% \subsection{Keys for the structure commands} +% \begin{function}{tag} +% This is required. The value of the key is normally one of the +% standard types listed in section \ref{sec:new-tag}. +% 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{function} +% \begin{function}{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{function} +% \begin{function}{label} +% This key sets a label by which +% one can use the structure later in another structure. +% Internally the label name will start with \texttt{tagpdfstruct-}. +% \end{function} +% \begin{function}{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{function} +% +% \begin{function}{alttext,alttext-o} +% This key inserts an \texttt{/Alt} value in the dictionary of structure object. +% The value is handled as verbatim string and hex encoded. +% |alttext-o| will expand the value once. +% \end{function} +% \begin{function}{actualtext,actualtext-o} +% This key inserts an \texttt{/ActualText} value in the dictionary of structure object. +% The value is handled as verbatim string and hex encoded. +% |actualtext-o| will expand the value once. +% \end{function} +% \begin{function}{lang} +% This key allows to set the language for a structure element. The value should be a bcp-identifier, +% e.g. |de-De|. +% \end{function} +% \begin{function}{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{function} +% \begin{function}{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{function} +% \begin{function}{AF,AFinline,AFinline-o} +% \begin{syntax} +% AF = \meta{object name}\\ +% AF-inline = \meta{text content} +% \end{syntax} +% These keys allows to reference an associated file in the structure element. +% 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. +% +% \end{function} +% +% \begin{function}{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{function} % +% \begin{function}{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{function} +% \subsection{Setup keys} +% \begin{function}{newattribute} +% \begin{syntax} +% newattribute = \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 +% { +% newattribute = +% {TH-col}{/O /Table /Scope /Column}, +% newattribute = +% {TH-row}{/O /Table /Scope /Row}, +% } +% \end{verbatim} +% +% \end{function} +% \end{documentation} +% \begin{implementation} % \begin{macrocode} %<@@=tag> -%<*struct> -\ProvidesExplPackage {tagpdf-struct-code} {2021-06-14} {0.82} +%<*header> +\ProvidesExplPackage {tagpdf-struct-code} {2021-06-29} {0.9} {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. % I will use a latex counter for the structure count -% to have a chance to avoid double structures in align etc +% to have a chance to avoid double structures in align etc. +% % \begin{macrocode} +%<*package> \newcounter { g_@@_struct_abs_int } \int_gzero:N \c@g_@@_struct_abs_int % \end{macrocode} -% a sequence to store structnum -> the obj numbers -% to allow easy mapping over the structures +% \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} \@@_seq_new:N \g_@@_struct_objR_seq % \end{macrocode} -% a sequence for the structure stack. +% \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 {0} % \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} % \end{macrocode} -%this variables will hold the number of top entry and the parent on the stack +% \end{variable} +% +% \begin{variable}{\g_@@_struct_stack_current_tl,\l_@@_struct_stack_parent_tmpa_tl} +% The global variable will hold the current structure number. +% The local temporary variable will hold the parent when we fetch it from the stack. % \begin{macrocode} -\tl_new:N \l_@@_struct_stack_parent_tmp_tl \tl_new:N \g_@@_struct_stack_current_tl +\tl_new:N \l_@@_struct_stack_parent_tmpa_tl % \end{macrocode} -% I need at least one structure: the StructTreeRoot +% \end{variable} +% +% 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_0_prop for the root -% \g_@@_struct_N_prop, N >=1 -% they have all the keys -% objnum - number, -% Type - StructTreeRoot or StructElem -% num - number (identical to the num in the name, or 0 for the root) +% |\g_@@_struct_0_prop| for the root and +% |\g_@@_struct_N_prop|, $N \geq =1$ 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 @@ -79,7 +300,7 @@ K, % kid, dictionary or array of dictionaries IDTree, % currently unused ParentTree, % required,obj ref to the parent tree - ParentTreeNextKey, %optional + ParentTreeNextKey, % optional RoleMap, ClassMap, Namespaces @@ -96,22 +317,55 @@ K, %kids A, %attributes, probably unused C, %class "" - %R, + %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, %abreviation + E, % abreviation ActualText, AF, %pdf 2.0, array of dict, associated files NS, %pdf 2.0, dict, namespace PhoneticAlphabet, %pdf 2.0 Phoneme %pdf 2.0 } - -% I need an output handler for each prop, to get expandable output -% see https://tex.stackexchange.com/questions/424208/expandable-version-of-a-expl3-command/424213#424213 - -\cs_new:Nn \@@_struct_output_prop_aux:nn %#1 num, #2 key +% \end{macrocode} +% \end{variable} +% +% \subsection{Variables used by the keys} +% \begin{variable}{\g_@@_struct_tag_tl,\g_@@_struct_tag_NS_tl} +% Use by the tag key to store the tag and the namespace. +% \begin{macrocode} +\tl_new:N \g_@@_struct_tag_tl +\tl_new:N \g_@@_struct_tag_NS_tl +% \end{macrocode} +% \end{variable} +% \begin{variable}{\l_@@_struct_key_label_tl} +% This will hold the label value. +% \begin{macrocode} +\tl_new:N \l_@@_struct_key_label_tl +% \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} +% \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 } @@ -121,99 +375,113 @@ } } -\cs_new:Nn \@@_new_output_prop_handler:n +\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} } } +% \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/0| 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} +\tl_gset:Nn \g_@@_struct_stack_current_tl {0} +% \end{macrocode} - -% the first one, the StructTreeRoot is special, so -% created manually: +% +% \begin{variable}{g_@@_struct_0_prop,g_@@_struct_kids_0_seq} +% \begin{macrocode} \@@_prop_new:c { g_@@_struct_0_prop } \@@_new_output_prop_handler:n {0} -\tl_gset:Nn \g_@@_struct_stack_current_tl {0} - \@@_seq_new:c { g_@@_struct_kids_0_seq } -% new -\@@_prop_gput:cnx - { g_@@_struct_0_prop } - { objref} - { \pdf_object_ref:n { @@/struct/0 } } - \@@_prop_gput:cnn { g_@@_struct_0_prop } { Type } { /StructTreeRoot } -% the constants are defined in the tree code. -\@@_prop_gput:cnx - { g_@@_struct_0_prop } - { ParentTree } - { \pdf_object_ref:n { @@/tree/parenttree } } -\@@_prop_gput:cnx - { g_@@_struct_0_prop } - { RoleMap } - { \pdf_object_ref:n { @@/tree/rolemap } } - -\@@_prop_gput:cnn - { g_@@_struct_0_prop } - { entries } - { StructTreeRoot } - -\@@_prop_gput:cnn - { g_@@_struct_0_prop } - { num } - { 0 } -%Namespaces are pdf 2.0 but it doesn't harm to have an empty entry +% \end{macrocode} +% Namespaces are pdf 2.0 but it doesn't harm +% to have an empty entry. We could add a test, but if the code moves into +% the kernel, timing could get tricky. +% \begin{macrocode} \@@_prop_gput:cnx { g_@@_struct_0_prop } { Namespaces } { \pdf_object_ref:n { @@/tree/namespaces } } - - -% commands to store the kids -% I don't compare the page objects number yet, but always add the /Pg key, perhaps later - -\cs_new:Nn \@@_struct_kid_mc_gput_right:nn %#1 structure num, #2 MCID absnum% +% \end{macrocode} +% \end{variable} +% \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} +% 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. +% \begin{macrocode} +\cs_new_protected:Npn \@@_struct_kid_mc_gput_right:nn #1 #2 %#1 structure num, #2 MCID absnum% { - %\@@_store_pdfpageref:Nn \l_tmpa_tl { \@@_ref_value:enn{mcid-#2}{tagabspage}{1} } \@@_seq_gput_right:cx { g_@@_struct_kids_#1_seq } { << /Type \c_space_tl /MCR \c_space_tl - /Pg %\c_space_tl \l_tmpa_tl \c_space_tl 0 \c_space_tl R \c_space_tl + /Pg \c_space_tl \pdf_pageobject_ref:n { \@@_ref_value:enn{mcid-#2}{tagabspage}{1} } /MCID \c_space_tl \@@_ref_value:enn{mcid-#2}{tagmcid}{1} >> } } - -\cs_new:Nn\@@_struct_kid_struct_gput_right:nn %#1 num of parent struct, #2 kid struct +% \end{macrocode} +% \end{macro} +% \begin{macro} +% { +% \@@_struct_kid_struct_gput_right:nn,\@@_struct_kid_struct_gput_right:xx +% } +% This commands adds a structure as kid. We only need to record the object +% reference in the sequence. +% \begin{macrocode} +\cs_new_protected:Npn\@@_struct_kid_struct_gput_right:nn #1 #2 %#1 num of parent struct, #2 kid struct { \@@_seq_gput_right:cx { g_@@_struct_kids_#1_seq } { - \prop_item:cn - { g_@@_struct_#2_prop } - { objref } + \pdf_object_ref:n { @@/struct/#2 } } } -\cs_new:Nn\@@_struct_kid_OBJR_gput_right:nn %#1 num of parent struct, #2 obj reference +\cs_generate_variant:Nn \@@_struct_kid_struct_gput_right:nn {xx} +% \end{macrocode} +% \end{macro} +% \begin{macro} +% {\@@_struct_kid_OBJR_gput_right:nn,\@@_struct_kid_OBJR_gput_right:xx} +% 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. +% \begin{macrocode} +\cs_new_protected:Npn\@@_struct_kid_OBJR_gput_right:nn #1 #2 %#1 num of parent struct, + %#2 obj reference { - \pdf_object_unnamed_write:nx + \pdf_object_unnamed_write:nn { dict } { - /Type \c_space_tl /OBJR \c_space_tl - /Obj~#2 + /Type/OBJR/Obj~#2 } \@@_seq_gput_right:cx { g_@@_struct_kids_#1_seq } @@ -222,27 +490,43 @@ } } -\cs_generate_variant:Nn\@@_struct_kid_OBJR_gput_right:nn { nx } +\cs_generate_variant:Nn\@@_struct_kid_OBJR_gput_right:nn { xx } -\cs_new:Nn\@@_struct_exchange_kid_command:N %N= seq +% \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. +% \begin{macrocode} +\cs_new_protected:Npn\@@_struct_exchange_kid_command:N #1 %#1 = seq var { - \seq_gpop_left:NN #1 \l_tmpa_tl + \seq_gpop_left:NN #1 \l_@@_tmpa_tl \regex_replace_once:nnN { \c{\@@_mc_insert_mcid_kids:n} } { \c{\@@_mc_insert_mcid_single_kids:n} } - \l_tmpa_tl - \seq_gput_left:NV #1 \l_tmpa_tl + \l_@@_tmpa_tl + \seq_gput_left:NV #1 \l_@@_tmpa_tl } \cs_generate_variant:Nn\@@_struct_exchange_kid_command:N { c } - -\cs_new:Nn \@@_struct_fill_kid_key:n %#1 is the struct num +% \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 { \int_case:nnF { \seq_count:c { - g_@@_struct_kids_\prop_item:cn{ g_@@_struct_#1_prop }{num}_seq + g_@@_struct_kids_#1_seq } } { @@ -251,17 +535,17 @@ { 1 } % 1 kid, insert { % in this case we need a special command in - %luamode to get the array right. See issue #13 + % luamode to get the array right. See issue #13 \bool_if:NT\g_@@_mode_lua_bool { \@@_struct_exchange_kid_command:c - {g_@@_struct_kids_\prop_item:cn{ g_@@_struct_#1_prop }{num}_seq} + {g_@@_struct_kids_#1_seq} } \@@_prop_gput:cnx { g_@@_struct_#1_prop } {K} { \seq_item:cn { - g_@@_struct_kids_\prop_item:cn{ g_@@_struct_#1_prop }{num}_seq + g_@@_struct_kids_#1_seq } {1} } @@ -273,7 +557,7 @@ [ \seq_use:cn { - g_@@_struct_kids_\prop_item:cn{ g_@@_struct_#1_prop }{num}_seq + g_@@_struct_kids_#1_seq } { \c_space_tl @@ -283,22 +567,25 @@ } } -% this command can be used for roots and structure elements -% #1 is a num - -\tl_new:N \l_@@_struct_dict_content_tl - -\cs_new:Nn \@@_struct_get_dict_content:n +% \end{macrocode} +% \end{macro} +% \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. +% TODO!! this looks over-complicated. Check if it can be done with pdfdict now. +% \begin{macrocode} +\cs_new_protected:Npn \@@_struct_get_dict_content:nN #1 #2 %#1: stucture num { - %\tl_set:Nn \l_@@_struct_dict_content_tl {<<} - \tl_clear:N \l_@@_struct_dict_content_tl + \tl_clear:N #2 \seq_map_inline:cn { - c_@@_struct_\prop_item:cn{ g_@@_struct_#1_prop }{entries}_entries_seq + c_@@_struct_ + \int_compare:nNnTF{#1}={0}{StructTreeRoot}{StructElem} + _entries_seq } { \tl_put_right:Nx - \l_@@_struct_dict_content_tl + #2 { \prop_if_in:cnT { g_@@_struct_#1_prop } @@ -308,35 +595,99 @@ } } } - %\tl_put_right:Nn \l_@@_struct_dict_content_tl { >> } } - - -% #1 is the struct num -\cs_new:Nn \@@_struct_write_obj:n +% \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_in:cnTF - { g_@@_struct_#1_prop } - { objref } + \pdf_object_if_exist:nTF { @@/struct/#1 } { \@@_struct_fill_kid_key:n { #1 } - %\prop_show:c { g_@@_struct_#1_prop } - \@@_struct_get_dict_content:n { #1 } + \@@_struct_get_dict_content:nN { #1 } \l_@@_tmpa_tl \exp_args:Nx \pdf_object_write:nx { @@/struct/#1 } { - \l_@@_struct_dict_content_tl + \l_@@_tmpa_tl } } { \msg_error:nnn { tag } { struct-no-objnum } { #1} } } - -% this command allows to get the current structure tag. We will need to handle nesting -% P -\cs_new:Nn \@@_get_data_struct_tag: +% \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:nnx +% { 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 + \@@_struct_kid_OBJR_gput_right:xx + { + \l_@@_struct_stack_parent_tmpa_tl + } + { + #1 % + } + % add the parent obj number to the parent tree: + \exp_args:Nnx + \@@_parenttree_add_objr:nn + { + #2 + } + { + \pdf_object_ref:e { @@/struct/\l_@@_struct_stack_parent_tmpa_tl } + } + % increase the int: + \stepcounter{ g_@@_parenttree_obj_int } + } + } +% \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|. We will need to handle nesting +% \begin{macrocode} +\cs_new:Npn \@@_get_data_struct_tag: { \exp_args:Ne \tl_tail:n @@ -344,28 +695,33 @@ \prop_item:cn {g_@@_struct_\g__tag_struct_stack_current_tl _prop}{S} } } - -% keys for the user commands -% should I pass the values (e.g. the tag) through an escape command? +% \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 -\tl_new:N \g_@@_struct_tag_tl -\tl_new:N \g_@@_struct_tag_NS_tl -\cs_generate_variant:Nn \seq_set_split:Nnn{Nne} +% at the begin. +% \begin{macro} +% { +% label,stash,tag, +% title,title-o, +% alttext,alttext-o, +% actualtext,actualtext-o +% lang,ref,E +% } +% \begin{macrocode} \keys_define:nn { @@ / struct } { label .tl_set:N = \l_@@_struct_key_label_tl, stash .bool_set:N = \l_@@_struct_elem_stash_bool, tag .code:n = % S property - {%%????????? \pdfescapename?? slash/not slash is here the question ... - \seq_set_split:Nne \l_tmpa_seq { / } {#1/\prop_item:Nn\g__tag_role_tags_NS_prop{#1}} - \tl_gset:Nx \g_@@_struct_tag_tl { \seq_item:Nn\l_tmpa_seq {1} } - \tl_gset:Nx \g_@@_struct_tag_NS_tl { \seq_item:Nn\l_tmpa_seq {2} } - \bool_if:NT \g_@@_check_tags_bool - { - \@@_check_structure_tag:N \g_@@_struct_tag_tl - } - \@@_prop_gput:cnx + { + \seq_set_split:Nne \l_@@_tmpa_seq { / } {#1/\prop_item:Nn\g__tag_role_tags_NS_prop{#1}} + \tl_gset:Nx \g_@@_struct_tag_tl { \seq_item:Nn\l_@@_tmpa_seq {1} } + \tl_gset:Nx \g_@@_struct_tag_NS_tl { \seq_item:Nn\l_@@_tmpa_seq {2} } + \@@_check_structure_tag:N \g_@@_struct_tag_tl + \@@_prop_gput:cnx { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } { S } { \pdf_name_from_unicode_e:n{ \g_@@_struct_tag_tl} } % @@ -482,12 +838,17 @@ { <\l_@@_tmpa_str> } }, } - -% keys for AF keys, they rely on l3pdffile! +% \end{macrocode} +% \end{macro} +% \begin{macro}{AF,AFinline,AFinline-o} +% 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. +% \begin{macrocode} \keys_define:nn { @@ / struct } { - AF .code:n = % T property + AF .code:n = % AF property { \pdf_object_if_exist:nTF {#1} { @@ -503,8 +864,7 @@ ,AFinline .code:n = { \group_begin: - \exp_args:Ne - \pdf_object_if_exist:nF {@@/fileobj\int_use:N\c@g_@@_struct_abs_int} + \pdf_object_if_exist:eF {@@/fileobj\int_use:N\c@g_@@_struct_abs_int} { \pdffile_embed_stream:nxx {#1} @@ -520,8 +880,7 @@ ,AFinline-o .code:n = { \group_begin: - \exp_args:Ne - \pdf_object_if_exist:nF {@@/fileobj\int_use:N\c@g_@@_struct_abs_int} + \pdf_object_if_exist:eF {@@/fileobj\int_use:N\c@g_@@_struct_abs_int} { \pdffile_embed_stream:oxx {#1} @@ -535,81 +894,70 @@ \group_end: } } - -\cs_new_protected:Nn \tag_struct_begin:n +% \end{macrocode} +% \end{macro} +% +% \section{User commands} +% +% \begin{macro}{\tag_struct_begin:n,\tag_struct_end:} +% \begin{macrocode} +\cs_new_protected:Npn \tag_struct_begin:n #1 %#1 key-val { - \group_begin: - \int_gincr:N \c@g_@@_struct_abs_int - \@@_prop_new:c { g_@@_struct_\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} - %\@@_pdfreserveobjnum:N \l_tmpa_tl - \exp_args:Ne - \pdf_object_new:nn - { @@/struct/\int_eval:n { \c@g_@@_struct_abs_int } } - { dict } - \@@_prop_gput:cnx - { g_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_prop } - { objref} + \@@_check_if_active_struct:T { + \group_begin: + \int_gincr:N \c@g_@@_struct_abs_int + \@@_prop_new:c { g_@@_struct_\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} \exp_args:Ne - \pdf_object_ref:n - {@@/struct/\int_eval:n { \c@g_@@_struct_abs_int }} - } -% \prop_show:c { g_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_prop } - \@@_prop_gput:cnx - { g_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_prop } - { num} - { \int_eval:n { \c@g_@@_struct_abs_int } } - \@@_prop_gput:cno - { g_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_prop } - { Type } - { /StructElem } - \@@_prop_gput:cno - { g_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_prop } - { entries } - { StructElem } - \keys_set:nn { @@ / struct} { #1 } - \@@_check_structure_has_tag:n { \int_eval:n {\c@g_@@_struct_abs_int} } - \tl_if_empty:NF - \l_@@_struct_key_label_tl - { - \@@_ref_label:en{tagpdfstruct-\l_@@_struct_key_label_tl}{struct} - } - %get the potential parent from the stack: - \seq_get:NNF - \g_@@_struct_stack_seq - \l_@@_struct_stack_parent_tmp_tl - { - \msg_error:nn { tag } { struct-faulty-nesting } - } - \seq_gpush:NV \g_@@_struct_stack_seq \c@g_@@_struct_abs_int - \seq_gpush:NV \g_@@_struct_tag_stack_seq \g_@@_struct_tag_tl - \tl_gset:NV \g_@@_struct_stack_current_tl \c@g_@@_struct_abs_int - %\seq_show:N \g_@@_struct_stack_seq - \bool_if:NF - \l_@@_struct_elem_stash_bool - {%set the parent - \@@_prop_gput:cnx - { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } - { P } + \pdf_object_new:nn + { @@/struct/\int_eval:n { \c@g_@@_struct_abs_int } } + { dict } + \@@_prop_gput:cno + { g_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_prop } + { Type } + { /StructElem } + \keys_set:nn { @@ / struct} { #1 } + \@@_check_structure_has_tag:n { \int_eval:n {\c@g_@@_struct_abs_int} } + \tl_if_empty:NF + \l_@@_struct_key_label_tl + { + \@@_ref_label:en{tagpdfstruct-\l_@@_struct_key_label_tl}{struct} + } + %get the potential parent from the stack: + \seq_get:NNF + \g_@@_struct_stack_seq + \l_@@_struct_stack_parent_tmpa_tl { - \prop_item:cn - { g_@@_struct_\l_@@_struct_stack_parent_tmp_tl _prop} - { objref } + \msg_error:nn { tag } { struct-faulty-nesting } + } + \seq_gpush:NV \g_@@_struct_stack_seq \c@g_@@_struct_abs_int + \seq_gpush:NV \g_@@_struct_tag_stack_seq \g_@@_struct_tag_tl + \tl_gset:NV \g_@@_struct_stack_current_tl \c@g_@@_struct_abs_int + %\seq_show:N \g_@@_struct_stack_seq + \bool_if:NF + \l_@@_struct_elem_stash_bool + {%set the parent + \@@_prop_gput:cnx + { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } + { P } + { + \pdf_object_ref:e { @@/struct/\l_@@_struct_stack_parent_tmpa_tl } + } + %record this structure as kid: + %\tl_show:N \g_@@_struct_stack_current_tl + %\tl_show:N \l_@@_struct_stack_parent_tmpa_tl + \@@_struct_kid_struct_gput_right:xx + { \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} } - %record this structure as kid: - %\tl_show:N \g_@@_struct_stack_current_tl - %\tl_show:N \l_@@_struct_stack_parent_tmp_tl - \@@_struct_kid_struct_gput_right:nn - { \l_@@_struct_stack_parent_tmp_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_tmp_tl _seq} - } - %\prop_show:c { g_@@_struct_\g_@@_struct_stack_current_tl _prop } - %\seq_show:c {g_@@_struct_kids_\l_@@_struct_stack_parent_tmp_tl _seq} - \group_end: + %\seq_show:c {g_@@_struct_kids_\l_@@_struct_stack_parent_tmpa_tl _seq} + \group_end: + } } @@ -617,149 +965,145 @@ { %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 - \seq_gpop:NN \g_@@_struct_tag_stack_seq \l_tmpa_tl - \seq_gpop:NNTF \g_@@_struct_stack_seq \l_tmpa_tl + \@@_check_if_active_struct:T { - \int_compare:nNnT {\l_@@_loglevel_int} > { 0 } + \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_struck: } - % 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:NV \g_@@_struct_stack_current_tl \l_tmpa_tl - } - { - \@@_check_no_open_struck: - } - \seq_get:NNT \g_@@_struct_tag_stack_seq \l_tmpa_tl - { - \tl_gset:NV \g_@@_struct_tag_tl \l_tmpa_tl - } - } - - - -\cs_new_protected:Nn \tag_struct_use:n %#1 is the label - { - \prop_if_exist:cTF - { g_@@_struct_\@@_ref_value:enn{tagpdfstruct-#1}{tagstruct}{unknown}_prop } %?????????? - { - \@@_check_struct_used:n {#1} - %add the label structure as kid to the current structure (can be the root) - \@@_struct_kid_struct_gput_right:nn - { \g_@@_struct_stack_current_tl } - { \@@_ref_value:enn{tagpdfstruct-#1}{tagstruct}{0} } - %add the current structure to the labeled one as parents - \@@_prop_gput:cnx - { g_@@_struct_\@@_ref_value:enn{tagpdfstruct-#1}{tagstruct}{0}_prop } - { P } + { \@@_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 { - \prop_item:cn - { g_@@_struct_\g_@@_struct_stack_current_tl _prop} - { objref } + \tl_gset:NV \g_@@_struct_stack_current_tl \l_@@_tmpa_tl } - } - { - \msg_warning:nnn{ tag }{struct-label-unknown}{#1} - } - } - - -%%%% Code to tag annotations like links. -% Annotations used as structure content must -% (1) add a StructParent integer to their dictionary -% (2) push the object reference as OBJR object in the structure -% (3) Add a Stuctparent/obj-nr reference to the parent three. -% For a link this looks like this -% \tag_struct_begin:n { tag=Link } -% \tag_mc_begin:n { tag=Link } -% (1) \pdfannot_dict_put:nnx -% { 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: -% -%%%% this works for url, see exp-link.pdf. -%%%% it must be checked for other links - - -% this will probably be used for xform too, but then we can rename it. -\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 link structure: - \seq_get:NNF - \g_@@_struct_stack_seq - \l_@@_struct_stack_parent_tmp_tl { - \msg_error:nn { tag } { struct-faulty-nesting } + \@@_check_no_open_struct: } - %put the obj number of link annot in the kid entry: - \@@_struct_kid_OBJR_gput_right:nx + \seq_get:NNT \g_@@_struct_tag_stack_seq \l_@@_tmpa_tl { - \l_@@_struct_stack_parent_tmp_tl + \tl_gset:NV \g_@@_struct_tag_tl \l_@@_tmpa_tl } + } + } +% \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} +\cs_new_protected:Nn \tag_struct_use:n %#1 is the label + { + \@@_check_if_active_struct:T + { + \prop_if_exist:cTF + { g_@@_struct_\@@_ref_value:enn{tagpdfstruct-#1}{tagstruct}{unknown}_prop } % { - #1 % - } - % add the parent obj number to the parent tree: - \exp_args:Nnx - \@@_parenttree_add_objr:nn - { - #2 + \@@_check_struct_used:n {#1} + %add the label structure as kid to the current structure (can be the root) + \@@_struct_kid_struct_gput_right:xx + { \g_@@_struct_stack_current_tl } + { \@@_ref_value:enn{tagpdfstruct-#1}{tagstruct}{0} } + %add the current structure to the labeled one as parents + \@@_prop_gput:cnx + { g_@@_struct_\@@_ref_value:enn{tagpdfstruct-#1}{tagstruct}{0}_prop } + { P } + { + \pdf_object_ref:e { @@/struct/\g_@@_struct_stack_current_tl } + } } { - \prop_item:cn - { g_@@_struct_\l_@@_struct_stack_parent_tmp_tl _prop } - { objref } + \msg_warning:nnn{ tag }{struct-label-unknown}{#1} } - % increase the int: - \stepcounter{ g_@@_parenttree_obj_int } } } - - - -% Code to add an annot to the structure. -% This is for example needed for widget annotations and form field -% It reuses the command used by links +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \tag_struct_insert_annot:nn, +% \tag_struct_insert_annot:xx +% } +% \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 { - \@@_struct_insert_annot:nn {#1}{#2} + \@@_check_if_active_struct:T + { + \@@_struct_insert_annot:nn {#1}{#2} + } } \cs_generate_variant:Nn \tag_struct_insert_annot:nn {xx} \cs_new:Npn \tag_struct_parent_int: {\int_use:c { c@g_@@_parenttree_obj_int }} -%</struct> +%</package> % \end{macrocode} +% \end{macro} +% \end{macro} +% \section{Attributes and attribute classes} % \begin{macrocode} -%<*attr> -\ProvidesExplPackage {tagpdf-attr-code} {2021-06-14} {0.82} +%<*header> +\ProvidesExplPackage {tagpdf-attr-code} {2021-06-29} {0.9} {part of tagpdf - code related to attributes and attribute classes} - -% the obj is written in tagpdf-tree-code. - -\seq_new:N \g_@@_attr_class_used_seq -\prop_new:N \g_@@_attr_objref_prop %will contain obj num of used attributes - +%</header> +% \end{macrocode} +% \subsection{Variables} +% \begin{variable} +% { +% ,\g_@@_attr_entries_prop +% ,\g_@@_attr_class_used_seq +% ,\g_@@_attr_objref_prop +% ,\l_@@_attr_value_tl +% } +% |\g_@@_attr_entries_prop| will store attribute names and their dictionary content.\\ +% |\g_@@_attr_class_used_seq| will hold the attributes which have been used as +% class name. +% |\l_@@_attr_value_tl| is used to build the attribute array or key. +% Everytime 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 -\tl_new:N \g_@@_attr_class_content_tl -\tl_new:N \l_@@_attr_objtmp_tl +\seq_new:N \g_@@_attr_class_used_seq \tl_new:N \l_@@_attr_value_tl - - -\cs_new_protected:Nn \@@_attr_new_entry:nn %#1:name, #2: content +\prop_new:N \g_@@_attr_objref_prop %will contain obj num of used attributes +% \end{macrocode} +% \end{variable} +% \subsection{Commands and keys} +% \begin{macro}{\@@_attr_new_entry:nn,newattribute} +% This allows to define attributes. Defined attributes +% are stored in a global property. |newattribute| expects +% two brace group, the name and the content. The content typically +% needs an |/O| key for the owner. An example look like +% this. +% \begin{verbatim} +% \tagpdfsetup +% { +% newattribute = +% {TH-col}{/O /Table /Scope /Column}, +% newattribute = +% {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:Nnn \g_@@_attr_entries_prop {#1}{#2} @@ -772,16 +1116,19 @@ \@@_attr_new_entry:nn #1 } } - - -% the key for the structure: +% \end{macrocode} +% \end{macro} +% \begin{macro}{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:No \l_tmpa_clist { #1 } - \seq_set_from_clist:NN \l_tmpa_seq \l_tmpa_clist - \seq_map_inline:Nn \l_tmpa_seq + \clist_set:No \l_@@_tmpa_clist { #1 } + \seq_set_from_clist:NN \l_@@_tmpa_seq \l_@@_tmpa_clist + \seq_map_inline:Nn \l_@@_tmpa_seq { \prop_if_in:NnF \g_@@_attr_entries_prop {##1} { @@ -789,38 +1136,41 @@ } \seq_gput_left:Nn\g_@@_attr_class_used_seq { ##1} } - \seq_set_map:NNn \l_tmpb_seq \l_tmpa_seq + \seq_set_map:NNn \l_@@_tmpb_seq \l_@@_tmpa_seq { /##1 } - \tl_set:Nx \l_tmpa_tl + \tl_set:Nx \l_@@_tmpa_tl { - \int_compare:nT { \seq_count:N \l_tmpa_seq > 1 }{[} - \seq_use:Nn \l_tmpb_seq { \c_space_tl } - \int_compare:nT { \seq_count:N \l_tmpa_seq > 1 }{]} + \int_compare:nT { \seq_count:N \l_@@_tmpa_seq > 1 }{[} + \seq_use:Nn \l_@@_tmpb_seq { \c_space_tl } + \int_compare:nT { \seq_count:N \l_@@_tmpa_seq > 1 }{]} } - \int_compare:nT { \seq_count:N \l_tmpa_seq > 0 } + \int_compare:nT { \seq_count:N \l_@@_tmpa_seq > 0 } { \@@_prop_gput:cnx { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } { C } - { \l_tmpa_tl } + { \l_@@_tmpa_tl } %\prop_show:c { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } } - } + } } - +% \end{macrocode} +% \end{macro} +% \begin{macro}{attribute} +% \begin{macrocode} \keys_define:nn { @@ / struct } { attribute .code:n = % A property (attribute, value currently a dictionary) { - \clist_set:No \l_tmpa_clist { #1 } - \seq_set_from_clist:NN \l_tmpa_seq \l_tmpa_clist + \clist_set:No \l_@@_tmpa_clist { #1 } + \seq_set_from_clist:NN \l_@@_tmpa_seq \l_@@_tmpa_clist \tl_set:Nx \l_@@_attr_value_tl { - \int_compare:nT { \seq_count:N \l_tmpa_seq > 1 }{[}%] + \int_compare:nT { \seq_count:N \l_@@_tmpa_seq > 1 }{[}%] } - \seq_map_inline:Nn \l_tmpa_seq + \seq_map_inline:Nn \l_@@_tmpa_seq { \prop_if_in:NnF \g_@@_attr_entries_prop {##1} { @@ -844,7 +1194,7 @@ } \tl_put_right:Nx \l_@@_attr_value_tl { %[ - \int_compare:nT { \seq_count:N \l_tmpa_seq > 1 }{]}% + \int_compare:nT { \seq_count:N \l_@@_tmpa_seq > 1 }{]}% } % \tl_show:N \l_@@_attr_value_tl \@@_prop_gput:cnx @@ -853,5 +1203,7 @@ { \l_@@_attr_value_tl } }, } -%</attr> +%</package> % \end{macrocode} +% \end{macro} +% \end{implementation} |