% \iffalse meta-comment
%
%% File: tagpdf.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>
%<*package>
\ProvidesExplPackage {tagpdf} {2021/02/23} {0.80}
  { A package to experiment with pdf tagging }

\bool_if:nF
  {
    \bool_lazy_and_p:nn
      {\cs_if_exist_p:N \pdfmanagement_if_active_p:}
      { \pdfmanagement_if_active_p: }
  }
  {  %error for now, perhaps warning later.
    \PackageError{tagpdf}
     {
       PDF~resource~management~is~no~active!\MessageBreak
       tagpdf~will~no~work.
     }
     {
       Activate~it~with \MessageBreak
       \string\RequirePackage{pdfmanagement-testphase}\MessageBreak
       \string\DeclareDocumentMetadata{<options>}\MessageBreak
       before~\string\documentclass
     }
  }

%\debug_on:n{check-declarations,check-expressions,deprecation}

%map internal tag to package name
\prop_if_exist:NT \g_msg_module_name_prop
  {
    \prop_gput:Nnn \g_msg_module_name_prop { tag }{ tagpdf }
  }

% storing internal names to my name space:
\cs_set_eq:NN \@@_tex_global:D              \tex_global:D

%%% package options
\bool_new:N\g_@@_mode_lua_bool

\DeclareOption {luamode}    { \sys_if_engine_luatex:T { \bool_gset_true:N \g_@@_mode_lua_bool } }
\DeclareOption {genericmode}{ \bool_gset_false:N\g_@@_mode_lua_bool }
\ExecuteOptions{luamode}
\ProcessOptions

%%% some packages
%references use l3ref-tmp
\RequirePackage{l3ref-tmp}
 %================
 % temporary code
 % ===============
 % faking the LastPage label:
 \cs_new_protected:Npn \@@_lastpagelabel:
   {
     \legacy_if:nT { @filesw }
       {
         \exp_args:NNnx \exp_args:NNx\iow_now:Nn \@auxout
            {
              \token_to_str:N \newlabeldata
                {@@_LastPage}
                {
                  {abspage} { \int_use:N \g_shipout_readonly_int}
                  {tagmcabs}{ \int_use:N \c@g_@@_MCID_abs_int }
                }
            }
       }
   }

 \AddToHook{enddocument/afterlastpage}
  {\@@_lastpagelabel:}

 % a command to override the general default. See issue in ref
 \cs_if_exist:NF \ref_value:nnn
   {
     \cs_new:Npn \ref_value:nnn #1#2#3
       {
         \exp_args:Nee
           \__ref_value:nnn
            { \tl_to_str:n {#1} } { \tl_to_str:n {#2} } {#3}
       }
     \cs_new:Npn \__ref_value:nnn #1#2#3
       {
         \tl_if_exist:cTF { g__ref_label_ #1 _ #2 _tl }
           { \tl_use:c { g__ref_label_ #1 _ #2 _tl } }
           {
             % test if attribute exist at all?
             #3
           }
       }
   }

 %=============
 % attributes
 %=============
\ref_attribute_gset:nnnn {tagstruct}    {0}{now}    { \int_use:N \c@g_@@_struct_abs_int }
\ref_attribute_gset:nnnn {tagstructobj} {}{now}
 {
  \exp_args:Ne
   \pdf_object_if_exist:nT {c__tag_struct_ \int_use:N \c@g__tag_struct_abs_int _obj}
    {
      \pdf_object_ref:e{c__tag_struct_ \int_use:N \c@g_@@_struct_abs_int _obj}
    }
 }
 % replace by abspage??
\ref_attribute_gset:nnnn {tagabspage}{0}{shipout}{ \int_use:N \g_shipout_readonly_int }
\ref_attribute_gset:nnnn {tagmcabs } {0}{now}    { \int_use:N \c@g_@@_MCID_abs_int }
\ref_attribute_gset:nnnn {tagmcid }  {0}{now}    { \int_use:N \g_@@_MCID_tmp_bypage_int }
%============
% lists
%============
\clist_const:Nn \c_@@_refmc_clist     {tagabspage,tagmcabs,tagmcid} %mc is new!!
\clist_const:Nn \c_@@_refstruct_clist {tagstruct,tagstructobj}
%=================
% command to set a label
%================
\cs_generate_variant:Nn \ref_label:nn { nv }
\cs_new_protected:Npn \@@_ref_label:nn #1 #2 %#1 label, #2 name of list mc or struct
  {
    \@bsphack
    \ref_label:nv {#1}{c_@@_ref#2_clist}
    \@esphack
  }
%==============
% command to retrieve the value
%===============
\cs_new:Npn \@@_ref_value:nnn #1 #2 #3 %#1 label, #2 attribute, #3 default
  {
    \ref_value:nnn {#1}{#2}{#3}
  }
%===============
% the LastPage label has a different name, so we need a special command for now
% =============
\cs_new:Npn \@@_ref_value_lastpage:nn #1 #2
  {
    \ref_value:nnn {@@_LastPage}{#1}{#2}
  }

% as ref doesn't expand label names we need variants:
\cs_generate_variant:Nn \@@_ref_label:nn {en}
\cs_generate_variant:Nn \@@_ref_value:nnn {enn}

\RequirePackage{etoolbox}
%\RequirePackage{l3pdf}
%\RequirePackage{pdfescape} %check if needed
%\RequirePackage{pdftexcmds}%check if needed (expandable commands!)

%\RequirePackage{l3str-convert}
\cs_generate_variant:Nn \str_set_convert:Nnnn {Nonn, Noon, Nnon }




%%% tagpdfsetup,
%%% TODO: checks need to be improved
\int_new:N  \l_@@_loglevel_int
\tl_new:N   \l_@@_tree_tabs_order_tl

\keys_define:nn { @@ / setup }
  {
    activate-mc     .bool_gset:N = \g_@@_active_mc_bool,
    activate-tree   .bool_gset:N = \g_@@_active_tree_bool,
    activate-struct .bool_gset:N = \g_@@_active_struct_bool,
    activate-all    .meta:n ={activate-mc,activate-tree,activate-struct},
    check-tags      .bool_gset:N = \g_@@_check_tags_bool,
    check-tags      .initial:n  = true,
    log             .choice:,
    log / none      .code:n = {\int_set:Nn \l_@@_loglevel_int { 0 }},
    log / v         .code:n = {\int_set:Nn \l_@@_loglevel_int { 1 }},
    log / vv        .code:n = {\int_set:Nn \l_@@_loglevel_int { 2 }},
    log / vvv       .code:n = {\int_set:Nn \l_@@_loglevel_int { 3 }},
    log / all       .code:n = {\int_set:Nn \l_@@_loglevel_int { 10 }},
    tagunmarked     .bool_gset:N = \g_@@_tagunmarked_bool,
    tagunmarked     .initial:n  = true,
    tabsorder       .choice:,
    tabsorder / row       .code:n = %{\tl_set:Nn \l_@@_tree_tabs_order_tl {/Tabs/R}},
      \pdfmanagement_add:nnn { Page } {Tabs}{/R},
    tabsorder / column    .code:n = %{\tl_set:Nn \l_@@_tree_tabs_order_tl {/Tabs/C}},
      \pdfmanagement_add:nnn { Page } {Tabs}{/C},
    tabsorder / structure .code:n = %{\tl_set:Nn \l_@@_tree_tabs_order_tl {/Tabs/S}},
      \pdfmanagement_add:nnn { Page } {Tabs}{/S},
    tabsorder / none      .code:n = %{\tl_set:Nn \l_@@_tree_tabs_order_tl {}},
      \pdfmanagement_remove:nn {Page} {Tabs},
    tabsorder       .initial:n = structure,
    uncompress      .code:n = {\pdf_uncompress:  },
  }
%need to think about the right place ...
%\AtBeginDocument
\hook_gput_code:nnn{begindocument}{tagpdf}
  {
    \bool_if:NT \g_@@_active_struct_bool
      {
         \pdfmanagement_add:nnn { Catalog / MarkInfo } { Marked } { true }
      }
  }
% escape commands are a bit engine specific so have been moved to the backend files.
% commands to escape strings so that they can be safely used in pdf
% currently not much used. But will be needed later, when alt and actualtext are added.
% we probably need the equivalent \pdfescapestring, \pdfescapename \pdfescapehex
% the commands of pdfescape adds an additional layout to allow for babel shorthands
%\cs_set_eq:NN \@@_pdf_escape_string:Nn \EdefEscapeString
%\cs_set_eq:NN \@@_pdf_escape_name:Nn   \EdefEscapeName
%\cs_set_eq:NN \@@_pdf_escape_hex:Nn    \EdefEscapeHex

%\cs_set_eq:NN \@@_pdf_escape_string:n \EdefEscapeString
%\cs_set_eq:NN \@@_pdf_escape_name:Nn   \EdefEscapeName

%escape command. They are backend specific as \pdf@escapehex would ignore most chars
%outside the "pdflatex" range





% a hook for later code and an absolute page counter
% should be executed before counters are resetted.
% is it used?
\cs_new:Nn   \@@_finish_page_hook: { }

%\AtBeginShipout

\hook_gput_code:nnn
  { shipout/before }
  { tagpdf/cnt }
  {
    \@@_finish_page_hook:
    %\int_gincr:N \g_@@_abspage_int
  }


%testing the engines and loading the backend files
\sys_if_engine_xetex:T
  { %lets try with xelatex ...
    %\PackageError { tagpdf } { xelatex~is~not~supported~-~aborting } {}
    %\tex_endinput:D
    \file_input:n {tagpdf-pdftex.def}
  }

\sys_if_engine_luatex:T
  {
    \file_input:n {tagpdf-luatex.def}
  }

\sys_if_engine_pdftex:T
  {
    \file_input:n {tagpdf-pdftex.def}
  }

\sys_if_output_dvi:T
  {
    %\PackageError { tagpdf } { dvi~output~is~not~supported~-~aborting }{}
    %\tex_endinput:D
  }

\cs_generate_variant:Nn \@@_prop_gput:Nnn      { Nxn , Nxx, Nnx , cnn, cxn, cnx, cno}
\cs_generate_variant:Nn \@@_seq_gput_right:Nn  { Nx  , No, cn, cx }
\cs_generate_variant:Nn \@@_prop_new:N   { c }
\cs_generate_variant:Nn \@@_seq_new:N    { c }
\cs_generate_variant:Nn \@@_seq_show:N   { c }
\cs_generate_variant:Nn \@@_prop_show:N  { c }
\cs_generate_variant:Nn \prop_gput:Nnn {Nxx}
\cs_generate_variant:Nn \prop_put:Nnn  {Nxx}

\cs_generate_variant:Nn \pdffile_embed_stream:nnn {nxx,oxx}
\cs_generate_variant:Nn \pdf_object_ref:n {e}


% few temp tl
\tl_new:N \g_@@_tmpa_tl
\tl_new:N \l_@@_tmpa_tl
\tl_new:N \l_@@_tmpb_tl
\tl_new:N \l_@@_tmpc_tl
\tl_new:N \l_@@_tmpd_tl
\tl_new:N \l_@@_tmpe_tl
\str_new:N \l_@@_tmpa_str

% helper function to get the propcount.
% use as \prop_map_function:NN PROP { \@@_prop_count:nn -1 }
\cs_new:Nn\@@_prop_count:nn { + 1 }

%% Loading the tagpdf sub packages
\RequirePackage { tagpdf-checks-code }
\RequirePackage { tagpdf-user }
\RequirePackage { tagpdf-tree-code }
\RequirePackage { tagpdf-roles-code }
\RequirePackage { tagpdf-attr-code }
% mc-code is split:
\RequirePackage { tagpdf-mc-code-shared }
\bool_if:NTF \g_@@_mode_lua_bool
  {
   \RequirePackage {tagpdf-mc-code-lua}
  }
  {
   \RequirePackage { tagpdf-mc-code-generic } %
  }

\RequirePackage { tagpdf-struct-code }
\RequirePackage { tagpdf-space-code }
%</package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<*user>
\ProvidesExplPackage {tagpdf-user} {2021/02/23} {0.80}
  {tagpdf - user commands}

\NewDocumentCommand \tagpdfsetup { m }
  {
    \keys_set:nn { @@ / setup } { #1 }
  }

\cs_set_eq:NN\tagpdfifluatexTF \sys_if_engine_luatex:TF
\cs_set_eq:NN\tagpdfifluatexT  \sys_if_engine_luatex:T
\cs_set_eq:NN\tagpdfifpdftexT  \sys_if_engine_pdftex:T

%%% stop tagging
\bool_new:N \l_@@_stop_bool

\prg_new_conditional:Nnn \@@_if_stop: {p,T,F,TF}
  {
    \bool_if:NTF \l_@@_stop_bool
      { \prg_return_true:  }
      { \prg_return_false: }
  }

\prg_new_eq_conditional:NNn \tag_if_stop: \@@_if_stop: {p,T,F,TF}

\cs_new_protected:Npn \@@_stop:
  {
    \bool_set_true:N \l_@@_stop_bool
    \cs_set_eq:NN \tag_mc_begin:n     \use_none:n
    \cs_set_eq:NN \tag_mc_end:        \prg_do_nothing:
    \cs_set_eq:NN \tag_mc_use:n       \use_none:n
    \cs_set_eq:NN \tag_struct_begin:n \use_none:n
    \cs_set_eq:NN \tag_struct_end:    \prg_do_nothing:
    \cs_set_eq:NN \tag_struct_use:n   \use_none:n
    %deprecated
    \cs_set_eq:NN \uftag_mc_begin:n \tag_mc_begin:n
    \cs_set_eq:NN \uftag_mc_end:    \tag_mc_end:
    \cs_set_eq:NN \uftag_mc_use:n \tag_mc_use:n
    \cs_set_eq:NN \uftag_struct_begin:n \tag_struct_begin:n
    \cs_set_eq:NN \uftag_struct_end: \tag_struct_end:
    \cs_set_eq:NN  \uftag_struct_use:n \tag_struct_use:n
  }

\cs_new_protected:Npn \tag_stop_group_begin:
  {
    \group_begin:
    \@@_stop:
  }

\cs_set_eq:NN \tag_stop_group_end: \group_end:


%%% a generic command to retrieve data

\cs_new:Npn \tagpdfget #1   { \use:c {@@_get_data_#1: } }
\cs_new:Npn \tag_get:n #1   { \use:c {@@_get_data_#1: } }
%deprecated:
\cs_new:Npn \uftag_get:n #1 { \use:c {@@_get_data_#1: } }


%%%% mc related user commands
\NewDocumentCommand \tagmcifinTF { m m }
  {
    \@@_mc_if_in:TF { #1 } { #2 }
  }


\NewDocumentEnvironment{tagmcartifact}{m}
 {
  \tag_mc_artifact_group_begin:n { #1}\ignorespaces
 }
 {
  \tag_mc_artifact_group_end:
 }

\NewDocumentCommand \tagmcbegin { m }
  {
    \tag_mc_begin:n {#1}\ignorespaces
  }


\NewDocumentCommand \tagmcend {  }
  {
    \unskip % this unskip appears to mess up some spacing; can we do this in another way?
    \tag_mc_end:
  }

\NewDocumentCommand \tagmcuse { m }
  {
    \tag_mc_use:n {#1}
  }


%%%% structure related commands

\NewDocumentCommand \tagstructbegin { m }
  {
    \tag_struct_begin:n {#1}
  }

\NewDocumentCommand \tagstructend {  }
  {
   \tag_struct_end:
  }

\NewDocumentCommand \tagstructuse { m }
  {
    \tag_struct_use:n {#1}
  }



%%%% debug/show commands
\NewDocumentCommand\showtagpdfmcdata { O {\@@_tag_get_mc_abs_cnt:} }
  {
    \bool_if:NT \g_@@_mode_lua_bool
      {
        \sys_if_engine_luatex:T
          {
            \directlua{ltx.@@.trace.show_all_mc_data(#1)}
          }
      }
  }

\NewDocumentCommand\showtagpdfattributes { }
  {
    \bool_if:NT \g_@@_mode_lua_bool
      {
        \sys_if_engine_luatex:T
          {
            \directlua
              {
                ltx.@@.trace.log
                 (
                   "showtagpdfattributes:
                    MC=>abscnt=\@@_get_mc_abs_cnt:
                    =>attr=\the\l_@@_mc_cnt_attr=>tag="
                    ..
                    tostring(ltx.@@.func.get_tag_from (\the\l_@@_mc_type_attr))
                    ..
                    "=\the\l_@@_mc_type_attr",0
                 )
             }
            \ignorespaces
          }
      }
   }

\sys_if_engine_luatex:T
  {
    \NewDocumentCommand\pdffakespace { }
      {
        \@@_fakespace:
      }
  }

%</user>
%    \end{macrocode}
%
%    \begin{macrocode}
%<*deprecated>
% \uftag_get:n
% \uftag_mc_if_in:
% \uftag_mc_begin:n
% \uftag_mc_end:
% \uftag_mc_use:n
% \uftag_struct_begin:n
% \uftag_struct_end:
% \uftag_finish_structure:
%</deprecated>
%    \end{macrocode}
