%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% ocgbase.sty
%
% low-level macros for OCG creation and for managment of global (document-wide)
% OCG related lists;
% (automatic) OCG configuration in the PDF catalog
%
% Copyright 2015--\today, Alexander Grahn
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Support package for ocgx2.sty, media9.sty, animate.sty
%
% Supported workflows:
%
%   pdflatex, lualatex
%   latex-->dvips-->ps2pdf or Distiller
%   latex-->dvipdfmx
%   xelatex
%
%
% Package options:
%
%   xetex,
%   dvipdfmx
%
%
% Commands defined:
%
%   \ocgbase_new_ocg:xxx
%   \ocgbase@new@ocg (LaTeX2e version)
%     #1: name (as shown in the Layers Tab of the Reader GUI)
%     #2: usage dict (may be empty), see PDF reference:
%         http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/
%                pdf_reference_1-7.pdf#G9.3858276
%     #3: initial visibility (1|0|true|false|on|off)
%
%   \ocgbase_last_ocg:
%   \ocgbase@last@ocg (LaTeX2e version)
%     inserts ID of PDF object created during most recent call of
%     \ocgbase_new_ocg:xxx
%
%   --------
%
%   \ocgbase_add_to_order_list:n
%   \ocgbase@add@to@order@list (LaTeX2e version)
%     #1: PDF object ID of OCG
%     Only OCGs added to the global `Order' list using this command will show
%     up in the Layers Tab
%
%   --------
%
%   \ocgbase_add_to_off_list:n
%   \ocgbase@add@to@off@list (LaTeX2e version)
%     #1: PDF object ID of OCG
%     macro for belated setting of initial non-visibility of an OCG
%
%   --------
%
%   \ocgbase_add_ocg_to_radiobtn_grp:xxx
%   \ocgbase@add@ocg@to@radiobtn@grp
%     add OCG #2 (obj ref) to radio button group `#1' (string),
%     #3: (0|1|false|true) list OCG as part of group `#1' in the Layers Tab)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3
% of this license or (at your option) any later version.
% The latest version of this license is in
%   http://mirrors.ctan.org/help/Catalogue/licenses.lppl.html
% and version 1.3 or later is part of all distributions of LaTeX
% version 2005/12/01 or later.
%
% This work has the LPPL maintenance status `maintained'.
%
% The Current Maintainer of this work is A. Grahn.

\RequirePackage{expl3}
\RequirePackage{atenddvi}

\def\g@ocgbase@date@tl{2015/10/28}
\def\g@ocgbase@version@tl{0.1}

\ProvidesExplPackage{ocgbase}{\g@ocgbase@date@tl}{\g@ocgbase@version@tl}
{support package for ocgx2.sty}

%unknown package option error message
\msg_set:nnnn{ocgbase}{unknown~package~option}{Unknown~package~option~`#1'.}{
  Package option~'#1'~is~unknown;\\
  perhaps~it~is~spelled~incorrectly.
}

%package opts
\keys_define:nn{ocgbase}{
  xetex .code:n = {\PassOptionsToPackage{xetex}{pdfbase}},
  dvipdfmx .code:n = {\PassOptionsToPackage{dvipdfmx}{pdfbase}}
}

%process package options
\ProcessKeysOptions{ocgbase}

\AtBeginDocument{
  \@ifpackageloaded{pdfbase}{}{\RequirePackage{pdfbase}}
}

\tl_new:N\g_ocgbase_ocgs_tl %takes ocg object refs
\tl_new:N\g_ocgbase_offocgs_tl
\seq_new:N\g_ocgbase_ocgorder_seq

\AtEndDvi{
  \tl_if_empty:NF\g_ocgbase_ocgs_tl{
    %global OCG array
    \pbs_pdfobj:nnn{}{array}{\g_ocgbase_ocgs_tl}
    \tl_set:Nx\l_ocgbase_ocgarray_tl{\pbs_pdflastobj:}
    \tl_new:N\l_ocgbase_offocgentry_tl
    %global OFF list
    \tl_if_empty:NF\g_ocgbase_offocgs_tl{
      \pbs_pdfobj:nnn{}{array}{\g_ocgbase_offocgs_tl}
      \tl_set:Nx\l_ocgbase_offocgentry_tl{/OFF~\pbs_pdflastobj:}
    }
    %build Order list
    \tl_new:N\l_ocgbase_ocgorderentry_tl
    \seq_if_empty:NF\g_ocgbase_ocgorder_seq{
      %process radio button groups in the Order array
      \seq_map_inline:Nn\g_ocgbase_ocgorder_seq{
        \cs_if_exist:cT{g_ocgbase_#1_rbtn_order_seq}{
          \int_compare:nTF{\seq_count:c{g_ocgbase_#1_rbtn_groups_seq}>\c_one}{
            \int_compare:nT{\seq_count:c{g_ocgbase_#1_rbtn_order_seq}>\c_zero}{
              %remove all occurrences in Order array outside current rbtn group
              \seq_map_inline:cn{g_ocgbase_#1_rbtn_order_seq}{
                \seq_gremove_all:Nn\g_ocgbase_ocgorder_seq{##1}
              }
            }
          }{
            %ignore single-item radio button groups
            \seq_gremove_all:Nn\g_ocgbase_ocgorder_seq{#1}
          }
        }
      }
      %second sweep on Order array to generate the final Order entry
      \tl_new:N\l_ocgbase_ocgorder_tl
      \seq_map_inline:Nn\g_ocgbase_ocgorder_seq{
        \cs_if_exist:cTF{g_ocgbase_#1_rbtn_order_seq}{
          \int_compare:nT{\seq_count:c{g_ocgbase_#1_rbtn_order_seq}>\c_zero}{
            \tl_put_right:Nx\l_ocgbase_ocgorder_tl{
              ~[(#1)~\seq_use:cn{g_ocgbase_#1_rbtn_order_seq}{~}]
            }
          }
        }{
          %ordinary (non-radiobutton group) item (ocg obj ref, `[', `]')
          \tl_put_right:Nn\l_ocgbase_ocgorder_tl{~#1}
        }
      }
      \tl_if_empty:NF\l_ocgbase_ocgorder_tl{
        \pbs_pdfobj:nnn{}{array}{\l_ocgbase_ocgorder_tl}
        \tl_set:Nx\l_ocgbase_ocgorderentry_tl{/Order~\pbs_pdflastobj:}
      }
    }
    %generate RBGroups entry (radio button groups)
    \tl_new:N\l_ocgbase_rbgroups_tl
    \seq_map_inline:Nn\g_ocgbase_ocgorder_seq{
      \cs_if_exist:cT{g_ocgbase_#1_rbtn_groups_seq}{
        \int_compare:nT{\seq_count:c{g_ocgbase_#1_rbtn_groups_seq}>\c_one}{
          \tl_put_right:Nx\l_ocgbase_rbgroups_tl{
            ~[\seq_use:cn{g_ocgbase_#1_rbtn_groups_seq}{~}]
          }
        }
      }
    }
    \tl_new:N\l_ocgbase_rbgroupsentry_tl
    \tl_if_empty:NF\l_ocgbase_rbgroups_tl{
      \pbs_pdfobj:nnn{}{array}{\l_ocgbase_rbgroups_tl}
      \tl_set:Nx\l_ocgbase_rbgroupsentry_tl{/RBGroups~\pbs_pdflastobj:}
    }
    \pbs_pdfcatalog:n{
      /OCProperties~<<
        /OCGs~\l_ocgbase_ocgarray_tl
        /D~<<
          /AS~[
            <</Event/View  /Category[/View]  /OCGs~\l_ocgbase_ocgarray_tl>>
            <</Event/Print /Category[/Print] /OCGs~\l_ocgbase_ocgarray_tl>>
            <</Event/Export/Category[/Export]/OCGs~\l_ocgbase_ocgarray_tl>>
          ]
          /BaseState/ON~\l_ocgbase_offocgentry_tl
          \l_ocgbase_ocgorderentry_tl
          \l_ocgbase_rbgroupsentry_tl
          /ListMode/VisiblePages
        >>
      >>
    }
  }
}

%macro for inserting new OCG object
\cs_new:Nn\ocgbase_new_ocg:nnn{
  \pbs_pdfobj:nnn{}{dict}{
    /Type/OCG/Name~(#1)~\tl_if_blank:oF{#2}{/Usage<<#2>>}
  }
  \tl_gput_right:Nx\g_ocgbase_ocgs_tl{~\g_pbs_pdflastobj_tl}
  \bool_if:nT{
    \str_if_eq_p:nn{#3}{\c_zero} ||
    \str_if_eq_p:nn{#3}{off} ||
    \str_if_eq_p:nn{#3}{false}
  }{
    \tl_gput_right:Nx\g_ocgbase_offocgs_tl{~\g_pbs_pdflastobj_tl}
    \tl_new:c{off:\g_pbs_pdflastobj_tl}
  }
  \tl_gset_eq:NN\g_ocgbase_last_ocg_tl\g_pbs_pdflastobj_tl
}
\cs_generate_variant:Nn\ocgbase_new_ocg:nnn{xxx}

\cs_new:Nn\ocgbase_last_ocg:{\g_ocgbase_last_ocg_tl}

%macro for appending OCG object to global `Order' list
% in Layers Tab of the GUI)
\cs_new:Nn\ocgbase_add_to_order_list:n{
  \seq_gput_right:Nx\g_ocgbase_ocgorder_seq{#1}
}

%macro for belated appending an OCG object ref to the global `OFF' list
%(initial non-visibility)
\cs_new:Nn\ocgbase_add_to_off_list:n{
  \tl_if_exist:cF{off:#1}{
    \tl_gput_right:Nx\g_ocgbase_offocgs_tl{~#1}\tl_new:c{off:#1}
  }
}

\cs_new:Nn\ocgbase_add_ocg_to_radiobtn_grp:nnn{
  % #1: rbtn group name,
  % #2: OCG obj ref,
  % #3: visible in Layers Tab
  \seq_if_in:NnF\g_ocgbase_ocgorder_seq{#1}{
    \seq_gput_right:Nn\g_ocgbase_ocgorder_seq{#1}
    \seq_new:c{g_ocgbase_#1_rbtn_order_seq}
    \seq_new:c{g_ocgbase_#1_rbtn_groups_seq}
  }
  \seq_if_in:cnTF{g_ocgbase_#1_rbtn_order_seq}{#2}{
    \bool_if:nT{
      \str_if_eq_p:nn{#3}{\c_zero} ||
      \str_if_eq_p:nn{#3}{false}
    }{
      \seq_gremove_all:cn{g_ocgbase_#1_rbtn_order_seq}{#2}
    }
  }{
    \bool_if:nF{
      \str_if_eq_p:nn{#3}{\c_zero} ||
      \str_if_eq_p:nn{#3}{false}
    }{
      \seq_gput_right:cn{g_ocgbase_#1_rbtn_order_seq}{#2}
    }
  }
  \seq_gput_right:cn{g_ocgbase_#1_rbtn_groups_seq}{#2}
}
\cs_generate_variant:Nn\ocgbase_add_ocg_to_radiobtn_grp:nnn{xxx}

%l2e versions
\cs_gset_eq:NN\ocgbase@new@ocg\ocgbase_new_ocg:xxx
\cs_gset_eq:NN\ocgbase@last@ocg\ocgbase_last_ocg:
\cs_gset_eq:NN\ocgbase@add@to@order@list\ocgbase_add_to_order_list:n
\cs_gset_eq:NN\ocgbase@add@to@off@list\ocgbase_add_to_off_list:n
\cs_gset_eq:NN\ocgbase@add@ocg@to@radiobtn@grp\ocgbase_add_ocg_to_radiobtn_grp:xxx
