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 | 789 |
1 files changed, 789 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx new file mode 100644 index 00000000000..09ea3511882 --- /dev/null +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx @@ -0,0 +1,789 @@ +% \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/02/23} {0.80} + {part of tagpdf - code related to storing structure} + +% I will use a latex counter for the structure count +% to have a chance to avoid double structures in align etc + +\newcounter { g_@@_struct_abs_int } +\int_gzero:N \c@g_@@_struct_abs_int + +% ref code is in tagpdf.dtx + +% a sequence stores structnum -> the obj numbers +% to allow easy mapping over the structures + +\@@_seq_new:N \g_@@_struct_objR_seq + +% a sequence for the structure stack. When a sequence is opened it's number is put on the stack. +\seq_new:N \g_@@_struct_stack_seq +\seq_gpush:Nn \g_@@_struct_stack_seq {0} + +%this variables will hold the top entry and the parent on the stack +\tl_new:N \l_@@_struct_stack_parent_tmp_tl +\tl_new:N \g_@@_struct_stack_current_tl + +% 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) + + +\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 + } + +\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 } + +% deprecated +%\@@_prop_gput:cno +% { g_@@_struct_0_prop } +% { objnum} +% { \c_@@_tree_obj_structtreeroot_tl } + +% new +\@@_prop_gput:cno + { g_@@_struct_0_prop } + { objref} + { \pdf_object_ref:n { c_@@_struct_0_obj } } + +\@@_prop_gput:cno + { 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 { c_@@_tree_parenttree_obj } } + +\@@_prop_gput:cnx + { g_@@_struct_0_prop } + { RoleMap } + { \pdf_object_ref:n { c_@@_tree_rolemap_obj } } + +\@@_prop_gput:cno + { g_@@_struct_0_prop } + { entries } + { StructTreeRoot } + +\@@_prop_gput:cno + { g_@@_struct_0_prop } + { num } + { 0 } + +% 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 + { c_@@_struct_#1_obj } + { + \l_@@_struct_dict_content_tl + } + } + { + \msg_error:nnn { tag } { struct-no-objnum } { #1} + } + } + +% keys for the user commands +% should I pass the values (e.g. the tag) through an escape command? +\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?? + \tl_set:Nx \l_@@_tmpa_tl { #1 } + \bool_if:NT \g_@@_check_tags_bool + { + \@@_check_structure_tag:N \l_@@_tmpa_tl + } + \@@_prop_gput:cnx + { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } + { S } + { /\exp_not:V\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 + { c_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_obj } + { dict } + \@@_prop_gput:cnx + { g_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_prop } + { objref} + { + \exp_args:Ne + \pdf_object_ref:n + {c_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_obj} + } +% \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 + \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: + } + +%deprecated +\cs_set_eq:NN \uftag_struct_begin:n \tag_struct_begin:n + +\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: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: + } + } + +%deprecated +\cs_set_eq:NN \uftag_struct_end: \tag_struct_end: + +\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} + } + } + +\cs_set_eq:NN \uftag_struct_use:n \tag_struct_use:n + + +%%%% 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 } + } + } + +%</struct> + +% \end{macrocode} +% \begin{macrocode} +%<*attr> +\ProvidesExplPackage {tagpdf-attr-code} {2021/02/23} {0.80} + {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 } + }, + } +%</attr> +% \end{macrocode} |