% \iffalse meta-comment % %% File: tagpdf-struct.dtx % % Copyright (C) 2019-2020 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/u-fischer/tagpdf % % for those people who are interested. % % \fi % % \begin{macrocode} %<@@=tag> %<*struct> \ProvidesExplPackage {tagpdf-struct-code} {2021/05/14} {0.81} {part of tagpdf - code related to storing structure} % \end{macrocode} % I will use a latex counter for the structure count % to have a chance to avoid double structures in align etc % \begin{macrocode} \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 % \begin{macrocode} \@@_seq_new:N \g_@@_struct_objR_seq % \end{macrocode} % a 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} % 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 % \begin{macrocode} \tl_new:N \l_@@_struct_stack_parent_tmp_tl \tl_new:N \g_@@_struct_stack_current_tl % \end{macrocode} % I 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) % 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{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 } \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, T, %title, value in () or <> Lang, %language Alt, % value in () or <> 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 { \prop_if_in:cnT { g_@@_struct_#1_prop } { #2 } { \c_space_tl/#2~ \prop_item:cn{ g_@@_struct_#1_prop } { #2 } } } \cs_new:Nn \@@_new_output_prop_handler:n { \cs_new:cn { @@_struct_output_prop_#1:n } { \@@_struct_output_prop_aux:nn {#1}{##1} } } % the first one, the StructTreeRoot is special, so % created manually: \@@_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 \@@_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% { %\@@_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 \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 { \@@_seq_gput_right:cx { g_@@_struct_kids_#1_seq } { \prop_item:cn { g_@@_struct_#2_prop } { objref } } } \cs_new:Nn\@@_struct_kid_link_gput_right:nn %#1 num of parent struct, #2 obj reference { \pdf_object_unnamed_write:nx { dict } { /Type \c_space_tl /OBJR \c_space_tl /Obj~#2 } \@@_seq_gput_right:cx { g_@@_struct_kids_#1_seq } { \pdf_object_ref_last: } } \cs_generate_variant:Nn\@@_struct_kid_link_gput_right:nn { nx } \cs_new:Nn\@@_struct_exchange_kid_command:N %N= seq { \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 } \cs_generate_variant:Nn\@@_struct_exchange_kid_command:N { c } \cs_new:Nn \@@_struct_fill_kid_key:n %#1 is the struct num { \int_case:nnF { \seq_count:c { g_@@_struct_kids_\prop_item:cn{ g_@@_struct_#1_prop }{num}_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 \bool_if:NT\g_@@_mode_lua_bool { \@@_struct_exchange_kid_command:c {g_@@_struct_kids_\prop_item:cn{ g_@@_struct_#1_prop }{num}_seq} } \@@_prop_gput:cnx { g_@@_struct_#1_prop } {K} { \seq_item:cn { g_@@_struct_kids_\prop_item:cn{ g_@@_struct_#1_prop }{num}_seq } {1} } } % } { %many kids, use an array \@@_prop_gput:cnx { g_@@_struct_#1_prop } {K} { [ \seq_use:cn { g_@@_struct_kids_\prop_item:cn{ g_@@_struct_#1_prop }{num}_seq } { \c_space_tl } ] } } } % 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 { %\tl_set:Nn \l_@@_struct_dict_content_tl {<<} \tl_clear:N \l_@@_struct_dict_content_tl \seq_map_inline:cn { c_@@_struct_\prop_item:cn{ g_@@_struct_#1_prop }{entries}_entries_seq } { \tl_put_right:Nx \l_@@_struct_dict_content_tl { \prop_if_in:cnT { g_@@_struct_#1_prop } { ##1 } { \c_space_tl/##1~\prop_item:cn{ g_@@_struct_#1_prop } { ##1 } } } } %\tl_put_right:Nn \l_@@_struct_dict_content_tl { >> } } % #1 is the struct num \cs_new:Nn \@@_struct_write_obj:n { \prop_if_in:cnTF { g_@@_struct_#1_prop } { objref } { \@@_struct_fill_kid_key:n { #1 } %\prop_show:c { g_@@_struct_#1_prop } \@@_struct_get_dict_content:n { #1 } \exp_args:Nx \pdf_object_write:nx { @@/struct/#1 } { \l_@@_struct_dict_content_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: { \exp_args:Ne \tl_tail:n { \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? % 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} \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 { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } { S } { \pdf_name_from_unicode_e:n{ \g_@@_struct_tag_tl} } % \prop_get:NVNT \g_@@_role_NS_prop\g_@@_struct_tag_NS_tl\l_@@_tmpa_tl { \@@_prop_gput:cnx { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } { NS } { \l_@@_tmpa_tl } % } }, title .code:n = % T property { \str_set_convert:Nnon \l_@@_tmpa_str { #1 } { default } { utf16/hex } \@@_prop_gput:cnx { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } { T } { <\l_@@_tmpa_str> } }, title-o .code:n = % T property { \str_set_convert:Nnon \l_@@_tmpa_str { #1 } { default } { utf16/hex } \@@_prop_gput:cnx { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } { T } { <\l_@@_tmpa_str> } }, alttext .code:n = % Alt property { \str_set_convert:Nnon \l_@@_tmpa_str { #1 } { default } { utf16/hex } \@@_prop_gput:cnx { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } { Alt } { <\l_@@_tmpa_str> } }, alttext-o .code:n = % Alt property { \str_set_convert:Noon \l_@@_tmpa_str { #1 } { default } { utf16/hex } \@@_prop_gput:cnx { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } { Alt } { <\l_@@_tmpa_str> } }, actualtext .code:n = % ActualText property { \str_set_convert:Nnon \l_@@_tmpa_str { #1 } { default } { utf16/hex } \@@_prop_gput:cnx { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } { ActualText } { <\l_@@_tmpa_str>} }, actualtext-o .code:n = % ActualText property { \str_set_convert:Noon \l_@@_tmpa_str { #1 } { default } { utf16/hex } \@@_prop_gput:cnx { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } { ActualText } { <\l_@@_tmpa_str>} }, lang .code:n = % Lang property { \@@_prop_gput:cnx { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } { Lang } { (#1) } }, ref .code:n = % Lang property { \tl_clear:N\l_@@_tmpa_tl \clist_map_inline:nn {#1} { \tl_put_right:Nx \l_@@_tmpa_tl {~\ref_value:nn{tagpdfstruct-##1}{tagstructobj} } } \@@_prop_gput:cnx { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } { Ref } { [\l_@@_tmpa_tl] } }, E .code:n = % E property { \str_set_convert:Nnon \l_@@_tmpa_str { #1 } { default } { utf16/hex } \@@_prop_gput:cnx { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } { E } { <\l_@@_tmpa_str> } }, } % keys for AF keys, they rely on l3pdffile! % The stream variants use txt as extension to get the mimetype. \keys_define:nn { @@ / struct } { AF .code:n = % T property { \pdf_object_if_exist:nTF {#1} { \@@_prop_gput:cnx { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } { AF } { \pdf_object_ref:n {#1} } } { } }, ,AFinline .code:n = { \group_begin: \exp_args:Ne \pdf_object_if_exist:nF {@@/fileobj\int_use:N\c@g_@@_struct_abs_int} { \pdffile_embed_stream:nxx {#1} {tag-AFfile\int_use:N\c@g_@@_struct_abs_int.txt} {@@/fileobj\int_use:N\c@g_@@_struct_abs_int} } \@@_prop_gput:cnx { g_@@_struct_\int_use:N\c@g_@@_struct_abs_int _prop } { AF } { \pdf_object_ref:e {@@/fileobj\int_use:N\c@g_@@_struct_abs_int } } \group_end: } ,AFinline-o .code:n = { \group_begin: \exp_args:Ne \pdf_object_if_exist:nF {@@/fileobj\int_use:N\c@g_@@_struct_abs_int} { \pdffile_embed_stream:oxx {#1} {tag-AFfile\int_use:N\c@g_@@_struct_abs_int.txt} {@@/fileobj\int_use:N\c@g_@@_struct_abs_int} } \@@_prop_gput:cnx { g_@@_struct_\int_use:N\c@g_@@_struct_abs_int _prop } { AF } { \pdf_object_ref:e {@@/fileobj\int_use:N\c@g_@@_struct_abs_int } } \group_end: } } \cs_new_protected:Nn \tag_struct_begin:n { \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} { \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 } { \prop_item:cn { g_@@_struct_\l_@@_struct_stack_parent_tmp_tl _prop} { objref } } %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: } \cs_new_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 \seq_gpop:NN \g_@@_struct_tag_stack_seq \l_tmpa_tl \seq_gpop:NNTF \g_@@_struct_stack_seq \l_tmpa_tl { \int_compare:nNnT {\l_@@_loglevel_int} > { 0 } { \@@_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 } { \prop_item:cn { g_@@_struct_\g_@@_struct_stack_current_tl _prop} { objref } } } { \msg_warning:nnn{ tag }{struct-label-unknown}{#1} } } %%%% Code to tag links %%%% this works for url, see exp-link.pdf. %%%% it must be checked for other links \cs_new_protected:Nn \@@_struct_finish_link: { \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 } } %put the obj number of link annot in the kid entry: \@@_struct_kid_link_gput_right:nx { \l_@@_struct_stack_parent_tmp_tl } { \pdfannot_link_ref_last: } % add the parent obj number to the parent tree: \@@_parenttree_add_objr:nn { \int_use:N\c@g_@@_parenttree_obj_int } { \prop_item:cn { g_@@_struct_\l_@@_struct_stack_parent_tmp_tl _prop } { objref } } % increase the int: \stepcounter{ g_@@_parenttree_obj_int } } } % % \end{macrocode} % \begin{macrocode} %<*attr> \ProvidesExplPackage {tagpdf-attr-code} {2021/05/14} {0.81} {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 \prop_new:N \g_@@_attr_entries_prop \tl_new:N \g_@@_attr_class_content_tl \tl_new:N \l_@@_attr_objtmp_tl \tl_new:N \l_@@_attr_value_tl \cs_new_protected:Nn \@@_attr_new_entry:nn %#1:name, #2: content { \prop_gput:Nnn \g_@@_attr_entries_prop {#1}{#2} } \keys_define:nn { @@ / setup } { newattribute .code:n = { \@@_attr_new_entry:nn #1 } } % the key for the structure: \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 { \prop_if_in:NnF \g_@@_attr_entries_prop {##1} { \msg_error:nnn { tag } { attr-unknown } { ##1 } } \seq_gput_left:Nn\g_@@_attr_class_used_seq { ##1} } \seq_set_map:NNn \l_tmpb_seq \l_tmpa_seq { /##1 } \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 > 0 } { \@@_prop_gput:cnx { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } { C } { \l_tmpa_tl } %\prop_show:c { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } } } } \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 \tl_set:Nx \l_@@_attr_value_tl { \int_compare:nT { \seq_count:N \l_tmpa_seq > 1 }{[}%] } \seq_map_inline:Nn \l_tmpa_seq { \prop_if_in:NnF \g_@@_attr_entries_prop {##1} { \msg_error:nnn { tag } { attr-unknown } { ##1 } } \prop_if_in:NnF \g_@@_attr_objref_prop {##1} {%\prop_show:N \g_@@_attr_entries_prop \pdf_object_unnamed_write:nx { dict } { \prop_item:Nn\g_@@_attr_entries_prop {##1} } \prop_gput:Nnx \g_@@_attr_objref_prop {##1} {\pdf_object_ref_last:} } \tl_put_right:Nx \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:Nx \l_@@_attr_value_tl { %[ \int_compare:nT { \seq_count:N \l_tmpa_seq > 1 }{]}% } % \tl_show:N \l_@@_attr_value_tl \@@_prop_gput:cnx { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } { A } { \l_@@_attr_value_tl } }, } % % \end{macrocode}