%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ocgbase.sty % % low-level macros for OCG creation, /OC-marked content 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) % % -------- % % \ocgbase_oc_bdc:n % \ocgbase@oc@bdc % #1: OCG obj ref % mark begin of optional content belonging to OCG #1 in the current % content stream % % \ocgbase_oc_emc: % \ocgbase@oc@emc % mark end of optional content in the current content stream % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % 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{l3keys2e} \RequirePackage{atenddvi} \def\g@ocgbase@date@tl{2015/11/23} \def\g@ocgbase@version@tl{0.3} \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} \AtBeginDvi{ \@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:} } %global 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 \tl_set:Nn\l_ocgbase_order_item_count_tl{\c_zero} \seq_map_inline:Nn\g_ocgbase_ocgorder_seq{ \tl_set:Nx\l_ocgbase_order_item_count_tl{ \int_eval:n{\l_ocgbase_order_item_count_tl+\c_one} } \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, `[' or `]'), \tl_set:Nx\l_ocgbase_current_order_item_tl{#1} \tl_set:Nx\l_ocgbase_next_order_item_tl{ \seq_item:Nn\g_ocgbase_ocgorder_seq{ \l_ocgbase_order_item_count_tl+\c_one } } \tl_set:Nx\l_ocgbase_prev_order_item_tl{ \seq_item:Nn\g_ocgbase_ocgorder_seq{ \l_ocgbase_order_item_count_tl-\c_one } } \bool_if:nF{ %avoid empty nesting groups \str_if_eq_p:Vn\l_ocgbase_current_order_item_tl{[} && \str_if_eq_p:Vn\l_ocgbase_next_order_item_tl{]} || \str_if_eq_p:Vn\l_ocgbase_current_order_item_tl{]} && \str_if_eq_p:Vn\l_ocgbase_prev_order_item_tl{[} }{\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~[ <> <> <> ] /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} % OC-marked content \cs_new:Nn\ocgbase_oc_bdc:n{\pbs_pdfbdc:nn{/OC}{#1}} \cs_new:Nn\ocgbase_oc_emc:{\pbs_pdfemc:} %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 \cs_gset_eq:NN\ocgbase@oc@bdc\ocgbase_oc_bdc:n \cs_gset_eq:NN\ocgbase@oc@emc\ocgbase_oc_emc: