%%%%%%%%%%%%%%%%%%%%%% pdfmanagement-testphase %%%%%%%%%%%%%%%%%%% \newif\if@ocgxii@testphase \ExplSyntaxOn \bool_if:nTF{ \bool_lazy_and_p:nn {\cs_if_exist_p:N \pdfmanagement_if_active_p:} { \pdfmanagement_if_active_p: } }{\@ocgxii@testphasetrue}{ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ocgx2.sty % % Copyright 2015--\today, Alexander Grahn % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % The intent of this package is to be a drop-in replacement for the already % existing CTAN package `ocgx' by Paul Gaborit, and also for `ocg-p' and `ocg'. % % It re-implements the functionality of the ocg, ocgx and ocg-p packages % and adds support for all known engines and backends including % latex+dvips+ps2pdf, xelatex, latex+dvipdfmx, lualatex. % % With ocgx2, PDF layers may extend across page breaks. % % ocgx2 implements OCMDs (optional content membership dictionaries) % % Adds some minor improvements, such as package options, remembering option. % settings of reopened ocgs, correct behaviour of ocg switching links that were % themselves placed on layers, compatibility with the animate and media9 % packages. % % Re-implements hyperref's `ocgcolorlinks' option to produce coloured links % that may wrap around line breaks and page breaks. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License. % % The latest version of this license is in % http://mirrors.ctan.org/macros/latex/base/lppl.txt % % This work has the LPPL maintenance status `maintained'. % % The Current Maintainer of this work is A. Grahn. \def\g@ocgxii@date@tl{2024/03/18} \def\g@ocgxii@version@tl{0.57} \ProvidesExplPackage{ocgx2}{\g@ocgxii@date@tl}{\g@ocgxii@version@tl} {ports `ocgx' functionality to dvips+ps2pdf, xelatex and dvipdfmx} %creating global definitions \cs_new_protected:Npn\ocgxii@newkey#1#2{\tl_gset:cx{#1}{#2}} \cs_new_protected:Npn\ocgxii@newkeynoexp#1#2{\tl_gset:cn{#1}{#2}} \AtBeginDocument{ \iow_now:Nx\@mainaux{ \token_to_str:N\providecommand\token_to_str:N\ocgxii@newkey[2]{} } \iow_now:Nx\@mainaux{ \token_to_str:N\providecommand\token_to_str:N\ocgxii@newkeynoexp[2]{} } \iow_now:Nx\@mainaux{ \token_to_str:N\providecommand\token_to_str:N\ocgxii@ocg@stack@on@page[2]{} } \iow_now:Nx\@mainaux{ \token_to_str:N\providecommand \token_to_str:N\ocgxii@lnkcol@stack@on@page[2]{} } } \msg_set:nnn{ocgx2}{generic~msg}{#1\\#2} \msg_set:nnn{ocgx2}{missing~package}{ Package~`#1'~must~be~loaded~before~ocgx2.\\\\ Put\\\\ \space\space\string\usepackage#2{#1}\\ \space\space\string\usepackage[ocgcolorlinks]{ocgx2}\\\\ to~the~preamble~of~your~document. } % package options \msg_set:nnnn{ocgx2}{unknown~package~option}{Unknown~package~option~`#1'.}{ Package option~'#1'~is~unknown;\\ perhaps~it~is~spelled~incorrectly. } \bool_new:N\g_ocgxii_dvipdfmx_bool \bool_new:N\l_ocgxii_tikz_bool \bool_new:N\l_ocgxii_ocgcolorlinks_bool \bool_new:N\g_ocgxii_showingui_bool \bool_new:N\l_ocgxii_showingui_bool \keys_define:nn{ocgx2}{ pdftex.code:n = {}, pdftex.value_forbidden:n = true, luatex.code:n = {}, luatex.value_forbidden:n = true, xetex.code:n = {}, xetex.value_forbidden:n = true, dvips.code:n = {}, dvips.value_forbidden:n = true, dvipdfmx .code:n = { \PassOptionsToPackage{dvipdfmx}{ocgbase} }, dvipdfmx .value_forbidden:n = true, viewocg .choice:, viewocg / always .code:n={ \tl_gset:Nn\g_ocgxii_view_tl{/View<>}}, viewocg / never .code:n={ \tl_gset:Nn\g_ocgxii_view_tl{/View<>}}, viewocg / ifvisible .code:n={\tl_gclear_new:N\g_ocgxii_view_tl}, viewocg .default:n={ifvisible}, printocg .choice:, printocg / always .code:n={ \tl_gset:Nn\g_ocgxii_print_tl{/Print<>}}, printocg / never .code:n={ \tl_gset:Nn\g_ocgxii_print_tl{/Print<>}}, printocg / ifvisible .code:n={\tl_gclear_new:N\g_ocgxii_print_tl}, printocg .default:n={ifvisible}, exportocg .choice:, exportocg / always .code:n={ \tl_gset:Nn\g_ocgxii_export_tl{/Export<>}}, exportocg / never .code:n={ \tl_gset:Nn\g_ocgxii_export_tl{/Export<>}}, exportocg / ifvisible .code:n={\tl_gclear_new:N\g_ocgxii_export_tl}, exportocg .default:n={ifvisible}, showingui .choices:nn = {true,false,always,never,iffirstuse}{ \bool_if:nTF{ \str_if_eq_p:ee{#1}{false} || \str_if_eq_p:ee{#1}{never} }{ \bool_gset_false:N\g_ocgxii_showingui_bool }{ \bool_gset_true:N\g_ocgxii_showingui_bool } }, showingui .default:n={true}, listintoolbar .meta:n = {showingui=#1}, listintoolbar .default:n={true}, tikz .bool_set:N = \l_ocgxii_tikz_bool, tikz .default:n = true, ocgcolorlinks .bool_set:N = \l_ocgxii_ocgcolorlinks_bool, ocgcolorlinks .default:n = true, unknown .code:n = { \msg_error:nnx{ocgx2}{unknown~package~option}{\l_keys_key_tl} } } %package options preset \keys_set:nn{ocgx2}{viewocg,printocg,exportocg,showingui,tikz=false} %process package options \ProcessKeyOptions[ocgx2] \RequirePackage{ocgbase} %also loads pdfbase.sty \bool_gset_eq:NN\g_ocgxii_dvipdfmx_bool\g_pbs_dvipdfmx_bool \bool_if:NT\g_pbs_dvisvgm_bool{ \msg_error:nnn{ocgx2}{generic~msg}{ Package~`ocgx2'~is~incompatible~with~the~`dvisvgm'~backend. } } %re-implement ocg-p's `ocg' environment \DeclareDocumentEnvironment{ocg}{O{}mmm}{ \ocgxii_begin_ocg:nnnn{#1}{#2}{#3}{#4} }{ \ocgxii_end_ocg: } \cs_new_protected_nopar:Nn\ocgxii_begin_ocg:nnnn{ \group_begin: \ocgxii_reset_cmd_opts: % ... to the user-set package options \tl_set:Nx\l_ocgxii_argiv_tl{\tl_trim_spaces:n{#4}} \tl_if_exist:cTF{ocgxii_ocg_#3}{ %re-open existing layer \tl_set:Nx\l_tempa_tl{\tl_use:c{ocgxii_ocg_#3.opts},#1} \tl_gset:cx{ocgxii_ocg_#3.opts}{\l_tempa_tl} %new options appended \keys_set:nV{ocgx2/ocgenv}\l_tempa_tl \bool_if:nTF{ %initial visibility \str_if_eq_p:ee{\l_ocgxii_argiv_tl}{1} || \str_if_eq_p:ee{\l_ocgxii_argiv_tl}{on} || \str_if_eq_p:ee{\l_ocgxii_argiv_tl}{true} }{ \ocgbase_del_from_off_list:n{\tl_use:c{ocgxii_ocg_#3}} }{ \ocgbase_add_to_off_list:n{\tl_use:c{ocgxii_ocg_#3}} } }{ \tl_set:Nx\l_tempa_tl{#1} \tl_gset:cx{ocgxii_ocg_#3.opts}{\l_tempa_tl} \keys_set:nV{ocgx2/ocgenv}\l_tempa_tl \ocgbase_new_ocg:nnn{#2}{ \l_ocgxii_view_tl\l_ocgxii_print_tl\l_ocgxii_export_tl }{\l_ocgxii_argiv_tl} \tl_gset:cx{ocgxii_ocg_#3}{\ocgbase_last_ocg:} \tl_gset:cx{ocgx2.ocg.\ocgbase_last_ocg:}{\ocgbase_last_ocg:} \tl_gset:cx{ocgx2.ocg.#3}{\ocgbase_last_ocg:} \iow_now:Nx\@mainaux{ \token_to_str:N\ocgxii@newkey{ocgx2.ocg.#3}{\ocgbase_last_ocg:} } } \bool_if:nT{ !\cs_if_exist:cTF{ocgx2.ocg.#3}{ \str_if_eq_p:ee{\tl_use:c{ocgx2.ocg.#3}}{\tl_use:c{ocgxii_ocg_#3}} }{ \c_false_bool } }{ \tl_if_exist:NF\g_ocgxii_rerunwarned_tl{ \tl_new:N\g_ocgxii_rerunwarned_tl \AtEndDocument{\msg_warning:nn{ocgx2}{rerun}} } } \seq_map_inline:Nn\l_ocgxii_rbgrps_seq{% process list of radio btn groups \ocgbase_add_ocg_to_radiobtn_grp:nn{##1}{\tl_use:c{ocgxii_ocg_#3}} } \ocgbase_open_stack_push:n{\tl_use:c{ocgxii_ocg_#3}} \ocgxii_make_oc_entry: \ocgxii_stack_shipout:NN\ocgxii@ocg@stack@on@page\g_ocgbase_open_stack_seq % insert OCG into Order tree \bool_if:NT\l_ocgxii_showingui_bool{ \ocgbase_tree_node_begin:n{\tl_use:c{ocgxii_ocg_#3}} } \group_end: \ocgbase_oc_bdc:n{\tl_use:c{ocgxii_ocg_#3}} \ignorespaces } \cs_new_protected_nopar:Nn\ocgxii_end_ocg:{ \unskip \ocgbase_oc_emc: \ocgbase_tree_node_end: \ocgbase_open_stack_pop:N\l_trash_tl \ocgxii_make_oc_entry: \ocgxii_stack_shipout:NN\ocgxii@ocg@stack@on@page\g_ocgbase_open_stack_seq } % OCMD implementation \DeclareDocumentEnvironment{ocmd}{O{}m}{ \ocgxii_begin_ocmd:on{#1}{#2} \ignorespaces }{ \unskip \ocgxii_end_ocmd: } \cs_new_protected_nopar:Nn\ocgxii_protected_dummy_cs:n{} \cs_new_protected_nopar:Nn\ocgxii_begin_ocmd:nn{ % #1: id, \bool_if:nTF{ % #2: visib. expr. or policy \tl_if_blank:oTF{#1}{ \c_false_bool }{ \tl_if_exist_p:c{ocgxii_ocmd_#1} } }{ % re-open existing ocmd \tl_set_eq:Nc\l_ocgxii_cur_ocmd_tl{ocgxii_ocmd_#1} }{ % new ocmd \group_begin: \cs_set_eq:NN\AllOn \ocgxii_protected_dummy_cs:n \cs_set_eq:NN\AnyOn \ocgxii_protected_dummy_cs:n \cs_set_eq:NN\AnyOff\ocgxii_protected_dummy_cs:n \cs_set_eq:NN\AllOff\ocgxii_protected_dummy_cs:n \cs_set_eq:NN\Not \ocgxii_protected_dummy_cs:n \cs_set_eq:NN\And \ocgxii_protected_dummy_cs:n \cs_set_eq:NN\Or \ocgxii_protected_dummy_cs:n \ocgxii_ocmd_read_visbility:xN{#2}\l_ocgxii_ocmd_visibility_tl \pbs_pdfobj:nnn{}{dict}{/Type/OCMD\l_ocgxii_ocmd_visibility_tl} %if only visb. policy is given, generate equivalent visib. expression, %needed for stack of open layers and \ocgxii_make_oc_entry: command \ocgxii_ocmd_make_equiv_ve:xN{#2}\l_ocgxii_ocmd_equiv_ve_tl \tl_gset:co{ocgx2.ocmd.\pbs_pdflastobj:}{\l_ocgxii_ocmd_equiv_ve_tl} \group_end: \tl_set:Nx\l_ocgxii_cur_ocmd_tl{\pbs_pdflastobj:} \tl_if_blank:oF{#1}{ \tl_gset:cx{ocgxii_ocmd_#1}{\pbs_pdflastobj:} \group_begin: \cs_set_eq:NN\AllOn \ocgxii_protected_dummy_cs:n \cs_set_eq:NN\AnyOn \ocgxii_protected_dummy_cs:n \cs_set_eq:NN\AnyOff\ocgxii_protected_dummy_cs:n \cs_set_eq:NN\AllOff\ocgxii_protected_dummy_cs:n \cs_set_eq:NN\Not \ocgxii_protected_dummy_cs:n \cs_set_eq:NN\And \ocgxii_protected_dummy_cs:n \cs_set_eq:NN\Or \ocgxii_protected_dummy_cs:n \iow_now:Nx\@mainaux{\exp_not:N\ocgxii@newkeynoexp{ocgx2.ocmd.#1}{#2}} \group_end: } } \ocgbase_open_stack_push:n{\l_ocgxii_cur_ocmd_tl} \ocgxii_make_oc_entry: \ocgxii_stack_shipout:NN\ocgxii@ocg@stack@on@page\g_ocgbase_open_stack_seq \ocgbase_oc_bdc:n{\l_ocgxii_cur_ocmd_tl} } \cs_generate_variant:Nn\ocgxii_begin_ocmd:nn{on} \cs_new_protected_nopar:Nn\ocgxii_end_ocmd:{ \ocgbase_oc_emc: \ocgbase_open_stack_pop:N\l_trash_tl \ocgxii_make_oc_entry: %update \ocgxii_stack_shipout:NN\ocgxii@ocg@stack@on@page\g_ocgbase_open_stack_seq } %visibility expressions \cs_new_protected_nopar:Nn\ocgxii_ocmd_read_visbility:nN{ \int_zero:N\l_ocgxii_ve_cnt_int \int_zero:N\l_ocgxii_vp_cnt_int \tl_clear_new:N#2 \clist_map_inline:nn{#1}{\ocgxii_omcd_parse_argument:nN{##1}#2} } \cs_generate_variant:Nn\ocgxii_ocmd_read_visbility:nN{xN} \cs_new_protected_nopar:Nn\ocgxii_omcd_parse_argument:nN{ \cs_set_eq:NN\AllOn \ocgxii_vp_check:n \cs_set_eq:NN\AnyOn \ocgxii_vp_check:n \cs_set_eq:NN\AnyOff\ocgxii_vp_check:n \cs_set_eq:NN\AllOff\ocgxii_vp_check:n \cs_set_eq:NN\Not\ocgxii_ve_check:n \cs_set_eq:NN\And\ocgxii_ve_check:n \cs_set_eq:NN\Or \ocgxii_ve_check:n \tl_if_exist:cTF{ocgx2.ocg.#1}{ \msg_error:nnxx{ocgx2}{generic~msg}{ OCG~ids~cannot~be~directly~used~in~the~visibility~argument~of~an~ `ocmd'~environment. }{\g_ocgxii_help_msg_tl} }{ \tl_if_exist:cTF{ocgx2.ocmd.#1}{ \msg_error:nnxx{ocgx2}{generic~msg}{ OCMD~ids~cannot~be~directly~used~in~the~visibility~argument~of~an~ `ocmd'~environment. }{\g_ocgxii_help_msg_tl} }{ \tl_if_exist:cTF{ocgxii_#1}{ \str_case_e:nn{\tl_use:c{ocgxii_#1}}{ {VisExpr}{ \int_incr:N\l_ocgxii_ve_cnt_int \int_compare:nNnTF{\l_ocgxii_ve_cnt_int}>{1}{ \msg_error:nnnn{ocgx2}{generic~msg}{ More~than~one~visibility~expression~passed~to~the~`ocmd'~ environment. }{ At~most~one~visibility~expression~is~allowed.~A~visibility~ expression~is~a~boolean~expression~built~by~nesting~any~number~of~ \And{...},~\Or{...},~\Not{...}~commands. } }{ \tl_put_right:Nx#2{/VE~} \ocgxii_ocmd_expression_parser:nN{#1}#2 } } {VisPol}{ \int_incr:N\l_ocgxii_vp_cnt_int \int_compare:nNnTF{\l_ocgxii_vp_cnt_int}>{1}{ \msg_error:nnnn{ocgx2}{generic~msg}{ More~than~one~visibility~policy~passed~to~the~`ocmd'~environment. }{ At~most~one~visibility~policy~out~of~\AllOn{...},~\AnyOn{...},~ \AnyOff{...},~\AllOff{...}~is~allowed.~Any~number~of~OCG~IDs,~ separated~by~commas,~may~be~passed~as~arguments~to~these~ commands,~but~commands~may~not~be~nested.~For~complex~visibilty~ relations,~consider~using~a~visibility~expression. } }{ \ocgxii_ocmd_expression_parser:nN{#1}#2 } } } }{ \msg_error:nnxx{ocgx2}{generic~msg}{ The~visibility~argument~of~the~`ocmd'~environment~cannot~be~parsed. }{\g_ocgxii_help_msg_tl} } } } } \cs_generate_variant:Nn\ocgxii_omcd_parse_argument:nN{xN} \cs_generate_variant:Nn\ocgxii_omcd_parse_argument:nN{oN} \cs_new_protected_nopar:Nn\ocgxii_ocmd_expression_parser:nN{ \cs_set_eq:NN\AllOn \ocgxii_vp_check:n \cs_set_eq:NN\AnyOn \ocgxii_vp_check:n \cs_set_eq:NN\AnyOff\ocgxii_vp_check:n \cs_set_eq:NN\AllOff\ocgxii_vp_check:n \cs_set_eq:NN\Not\ocgxii_ve_check:n \cs_set_eq:NN\And\ocgxii_ve_check:n \cs_set_eq:NN\Or \ocgxii_ve_check:n \tl_if_exist:cTF{ocgx2.ocg.#1}{% ocg reference \tl_put_right:Nx#2{~\tl_use:c{ocgx2.ocg.#1}} }{ \tl_if_exist:cTF{ocgx2.ocmd.#1}{% ocmd reference \ocgxii_ocmd_expression_parser:vN{ocgx2.ocmd.#1}#2 }{ \tl_if_exist:cTF{ocgxii_#1}{% visib. bool expression or policy directive \bool_if:nTF{ \str_if_eq_p:ee{\tl_use:c{ocgxii_#1}}{VisExpr} && \bool_if_p:N\l_ocgxii_vp_open_bool || \str_if_eq_p:ee{\tl_use:c{ocgxii_#1}}{VisPol} && \bool_if_p:N\l_ocgxii_ve_open_bool }{ \msg_error:nnxx{ocgx2}{generic~msg}{ Visibility~policy~and~expression~commands~cannot~be~mixed. }{\g_ocgxii_help_msg_tl} }{ \bool_if:nT{ \str_if_eq_p:ee{\tl_use:c{ocgxii_#1}}{VisPol} && \bool_if_p:N\l_ocgxii_vp_open_bool }{ \msg_error:nnxx{ocgx2}{generic~msg}{ Visibility~policy~commands~cannot~be~nested.~For~more~complex~ visibilty~relations,~consider~using~a~visibility~expression. }{\g_ocgxii_help_msg_tl} } } \cs_set_eq:NN\AllOn \ocgxii_vp_allon:nN \cs_set_eq:NN\AnyOn \ocgxii_vp_anyon:nN \cs_set_eq:NN\AnyOff\ocgxii_vp_anyoff:nN \cs_set_eq:NN\AllOff\ocgxii_vp_alloff:nN \cs_set_eq:NN\Not\ocgxii_ve_not:nN \cs_set_eq:NN\And\ocgxii_ve_and:nN \cs_set_eq:NN\Or \ocgxii_ve_or:nN #1#2 }{ \msg_warning:nnx{ocgx2}{undefined~OCG}{#1} \tl_if_exist:NF\g_ocgxii_refundefwarned_tl{ \tl_new:N\g_ocgxii_refundefwarned_tl \AtEndDocument{\msg_warning:nn{ocgx2}{undefined~OCGs}} } } } } } \cs_generate_variant:Nn\ocgxii_ocmd_expression_parser:nN{vN} % visib. policy directives \cs_new_protected_nopar:Nn\ocgxii_vp_allon:nN{ \bool_set_true:N\l_ocgxii_vp_open_bool \tl_put_right:Nx#2{/P/AllOn/OCGs~\g_ocgxii_left_bracket_tl} \clist_map_inline:nn{#1}{\ocgxii_ocmd_expression_parser:nN{##1}#2} \tl_put_right:Nx#2{\g_ocgxii_right_bracket_tl} \bool_set_false:N\l_ocgxii_vp_open_bool } \cs_new_protected_nopar:Nn\ocgxii_vp_anyon:nN{ \bool_set_true:N\l_ocgxii_vp_open_bool \tl_gput_right:Nx#2{/P/AnyOn/OCGs~\g_ocgxii_left_bracket_tl} \clist_map_inline:nn{#1}{\ocgxii_ocmd_expression_parser:nN{##1}#2} \tl_gput_right:Nx#2{\g_ocgxii_right_bracket_tl} \bool_set_false:N\l_ocgxii_vp_open_bool } \cs_new_protected_nopar:Nn\ocgxii_vp_anyoff:nN{ \bool_set_true:N\l_ocgxii_vp_open_bool \tl_gput_right:Nx#2{/P/AnyOff/OCGs~\g_ocgxii_left_bracket_tl} \clist_map_inline:nn{#1}{\ocgxii_ocmd_expression_parser:nN{##1}#2} \tl_gput_right:Nx#2{\g_ocgxii_right_bracket_tl} \bool_set_false:N\l_ocgxii_vp_open_bool } \cs_new_protected_nopar:Nn\ocgxii_vp_alloff:nN{ \bool_set_true:N\l_ocgxii_vp_open_bool \tl_gput_right:Nx#2{/P/AllOff/OCGs~\g_ocgxii_left_bracket_tl} \clist_map_inline:nn{#1}{\ocgxii_ocmd_expression_parser:nN{##1}#2} \tl_gput_right:Nx#2{\g_ocgxii_right_bracket_tl} \bool_set_false:N\l_ocgxii_vp_open_bool } % policy to expression conversion \cs_new_protected_nopar:Nn\ocgxii_ocmd_make_equiv_ve:nN{ \int_zero:N\l_ocgxii_ve_cnt_int \tl_clear_new:N#2 \clist_map_inline:nn{#1}{\ocgxii_omcd_convert_vp:nN{##1}#2} } \cs_generate_variant:Nn\ocgxii_ocmd_make_equiv_ve:nN{xN} \cs_new_protected_nopar:Nn\ocgxii_omcd_convert_vp:nN{ \cs_set_eq:NN\AllOn \ocgxii_vp_check:n \cs_set_eq:NN\AnyOn \ocgxii_vp_check:n \cs_set_eq:NN\AnyOff\ocgxii_vp_check:n \cs_set_eq:NN\AllOff\ocgxii_vp_check:n \cs_set_eq:NN\Not\ocgxii_ve_check:n \cs_set_eq:NN\And\ocgxii_ve_check:n \cs_set_eq:NN\Or \ocgxii_ve_check:n \tl_if_exist:cT{ocgxii_#1}{ \str_case_e:nn{\tl_use:c{ocgxii_#1}}{ {VisExpr}{ \int_incr:N\l_ocgxii_ve_cnt_int \tl_set:Nn#2{#1} } {VisPol}{ \int_compare:nNnT{\l_ocgxii_ve_cnt_int}={0}{ \cs_set_eq:NN\AllOn \ocgxii_allon_to_ve:n \cs_set_eq:NN\AnyOn \ocgxii_anyon_to_ve:n \cs_set_eq:NN\AnyOff\ocgxii_anyoff_to_ve:n \cs_set_eq:NN\AllOff\ocgxii_alloff_to_ve:n \tl_set:No#2{#1} } } } } } \cs_new_protected_nopar:Nn\ocgxii_allon_to_ve:n{\And{#1}} \cs_new_protected_nopar:Nn\ocgxii_anyon_to_ve:n{\Or{#1}} \cs_new_protected_nopar:Nn\ocgxii_anyoff_to_ve:n{\Not{\And{#1}}} \cs_new_protected_nopar:Nn\ocgxii_alloff_to_ve:n{\Not{\Or{#1}}} % visib. boolean expressions \cs_new_protected_nopar:Nn\ocgxii_ve_and:nN{ \bool_set_true:N\l_ocgxii_ve_open_bool \tl_put_right:Nx#2{\g_ocgxii_left_bracket_tl/And} \clist_map_inline:nn{#1}{\ocgxii_ocmd_expression_parser:nN{##1}#2} \tl_put_right:Nx#2{\g_ocgxii_right_bracket_tl} \bool_set_false:N\l_ocgxii_ve_open_bool } \cs_new_protected_nopar:Nn\ocgxii_ve_or:nN{ \bool_set_true:N\l_ocgxii_ve_open_bool \tl_put_right:Nx#2{\g_ocgxii_left_bracket_tl/Or} \clist_map_inline:nn{#1}{\ocgxii_ocmd_expression_parser:nN{##1}#2} \tl_put_right:Nx#2{\g_ocgxii_right_bracket_tl} \bool_set_false:N\l_ocgxii_ve_open_bool } \cs_new_protected_nopar:Nn\ocgxii_ve_not:nN{ \bool_set_true:N\l_ocgxii_ve_open_bool % only one item allowed in \Not{...} argument \int_compare:nNnT{\clist_count:n{#1}}>{\c_one_int}{ \msg_error:nnnn{ocgx2}{generic~msg}{ More~than~one~item~passed~to~\Not{...}. }{ Only~one~item~is~allowed. } } \int_compare:nNnT{\clist_count:n{#1}}={\c_one_int}{ \tl_put_right:Nx#2{\g_ocgxii_left_bracket_tl/Not} \clist_map_inline:nn{#1}{\ocgxii_ocmd_expression_parser:nN{##1}#2} \tl_put_right:Nx#2{\g_ocgxii_right_bracket_tl} } \bool_set_false:N\l_ocgxii_ve_open_bool } \cs_new_protected_nopar:Nn\ocgxii_ve_check:n{VisExpr} \tl_set:cn{ocgxii_VisExpr}{VisExpr} \cs_new_protected_nopar:Nn\ocgxii_vp_check:n{VisPol} \tl_set:cn{ocgxii_VisPol}{VisPol} \tl_set:Nx\g_ocgxii_left_bracket_tl{\tl_to_str:N[} \tl_set:Nx\g_ocgxii_right_bracket_tl{\tl_to_str:N]} \int_new:N\l_ocgxii_vp_cnt_int %number of visib. policies \int_new:N\l_ocgxii_ve_cnt_int %number of visib. expressions \bool_new:N\l_ocgxii_vp_open_bool %for nesting test \bool_new:N\l_ocgxii_ve_open_bool %for nesting test \tl_set:Nn\g_ocgxii_help_msg_tl{ At~most~one~visibility~policy~and,~separated~by~a~comma,~at~most~one~ visibility~expression~may~be~passed~as~the~2nd~argument~to~the~`ocmd'~ environment.~A~visibility~ policy~is~defined~by~one~of~\AllOn{...},~\AnyOn{...},~\AnyOff{...},~ \AllOff{...}.~A~visibility~expression~is~a~boolean~expression~built~by~ nesting~any~number~of~\And{...},~\Or{...},~\Not{...}~commands.~If~both~are~ provided,~the~visibility~expression~takes~precedence~over~the~policy,~but~the~ latter~may~be~used~as~fallback~by~non-conforming~PDF~viewers. } %command that builds /OC entry from open layer stack \cs_new_protected_nopar:Nn\ocgxii_make_oc_entry:{ \group_begin: \tl_gclear:N\g_ocgxii_oc_entry_tl \tl_clear:N\l_tempa_tl \seq_if_empty:NF\g_ocgbase_open_stack_seq{ \seq_clear:N\l_tempa_seq %additional level of braces around indirect PDF objects (needed for dvips) \seq_map_inline:Nn\g_ocgbase_open_stack_seq{ \seq_put_right:Nn\l_tempa_seq{{##1}} } \ocgxii_omcd_parse_argument:xN{ \exp_not:N\And{\seq_use:Nn\l_tempa_seq{,}} }\l_tempa_tl \tl_gset:Nx\g_ocgxii_oc_entry_tl{/OC~<>} } \group_end: } %programmer/author command that inserts /OC << >> entry; for use in %annotation/xobject dicts, in order to make them layer-aware \cs_new_nopar:Nn\ocgxii_insert_oc:{\g_ocgxii_oc_entry_tl} \cs_gset_eq:NN\ocgbase_insert_oc:\ocgxii_insert_oc: \cs_gset_eq:NN\ocgbase@insert@oc\ocgxii_insert_oc: \tl_new:N\g_ocgxii_oc_entry_tl \cs_new_protected_nopar:Nn\ocgxii_stack_shipout:NN{ \iow_shipout_x:Nx\@mainaux{ \token_to_str:N#1{ \exp_not:N\int_use:N\g_ocgxii_page_int }{\seq_use:Nn#2{,}} } } \cs_new_protected_nopar:Npn\ocgxii@ocg@stack@on@page#1#2{ \seq_gset_from_clist:cn{g_pending_ocgs_on_#1_seq}{#2} %re-add braces around items for dvips \bool_if:nT{\sys_if_output_dvi_p: && !\g_ocgxii_dvipdfmx_bool}{ \seq_map_inline:cn{g_pending_ocgs_on_#1_seq}{ \seq_gpop_left:cN{g_pending_ocgs_on_#1_seq}\l_trash_tl \seq_gput_right:cn{g_pending_ocgs_on_#1_seq}{{##1}} } } } \ocgxii@ocg@stack@on@page{0}{} %initialize \cs_new_protected_nopar:Npn\ocgxii@lnkcol@stack@on@page#1#2{ \seq_gset_from_clist:cn{g_pending_lnkcols_on_#1_seq}{#2} %re-add braces around items \seq_map_inline:cn{g_pending_lnkcols_on_#1_seq}{ \seq_gpop_left:cN{g_pending_lnkcols_on_#1_seq}\l_trash_tl \seq_gput_right:cn{g_pending_lnkcols_on_#1_seq}{{##1}} } } \ocgxii@lnkcol@stack@on@page{0}{} %initialize %end-of-page action \pbs_eop_action:n{ \seq_if_exist:cT{g_pending_lnkcols_on_\int_use:c{g_ocgxii_page_int}_seq}{ %check whether end-of-page link colour stack has settled \iow_shipout:Nx\@mainaux{ \token_to_str:N\ocgxii@newkey{ocgx2.oldlnkcol.\int_use:N\g_ocgxii_page_int}{ \seq_use:cn{g_pending_lnkcols_on_\int_use:c{g_ocgxii_page_int}_seq}{,} } } \bool_if:nT{ !\cs_if_exist:cTF{ocgx2.oldlnkcol.\int_use:N\g_ocgxii_page_int}{ \str_if_eq_p:ee{ \tl_use:c{ocgx2.oldlnkcol.\int_use:N\g_ocgxii_page_int} }{ \seq_use:cn{g_pending_lnkcols_on_\int_use:c{g_ocgxii_page_int}_seq}{,} } }{ \c_false_bool } }{ \tl_if_exist:NF\g_ocgxii_rerunwarned_tl{ \tl_new:N\g_ocgxii_rerunwarned_tl \AtEndDocument{\msg_warning:nn{ocgx2}{rerun}} } } % now close the colourlink opened last \seq_get:cNT{g_pending_lnkcols_on_\int_use:c{g_ocgxii_page_int}_seq}\l_tmpa_tl{ \tl_gset:Nx\g_ocgxii_lnkcol_tl{{\l_tmpa_tl}} \ocgxii_colourlink_end: } } %check whether end-of-page ocg stack has settled \iow_shipout:Nx\@mainaux{ \token_to_str:N\ocgxii@newkey{ocgx2.oldstack.\int_use:N\g_ocgxii_page_int}{ \seq_use:cn{g_pending_ocgs_on_\int_use:c{g_ocgxii_page_int}_seq}{,} } } \bool_if:nT{ !\cs_if_exist:cTF{ocgx2.oldstack.\int_use:N\g_ocgxii_page_int}{ \str_if_eq_p:ee{ \tl_use:c{ocgx2.oldstack.\int_use:N\g_ocgxii_page_int} }{ \seq_use:cn{g_pending_ocgs_on_\int_use:c{g_ocgxii_page_int}_seq}{,} } }{ \c_false_bool } }{ \tl_if_exist:NF\g_ocgxii_rerunwarned_tl{ \tl_new:N\g_ocgxii_rerunwarned_tl \AtEndDocument{\msg_warning:nn{ocgx2}{rerun}} } } %now close pending ocgs \seq_map_variable:cNn{ g_pending_ocgs_on_\int_use:c{g_ocgxii_page_int}_seq }\l_tmpb_tl{\ocgbase_oc_emc:} } %begin-of-page action \pbs_bop_action:n{ % re-open all pending ocgs in original order \seq_set_eq:Nc\l_ocgxii_pending_ocgs_seq{ g_pending_ocgs_on_\int_use:c{g_ocgxii_page_int}_seq } \seq_reverse:N\l_ocgxii_pending_ocgs_seq \seq_map_variable:NNn\l_ocgxii_pending_ocgs_seq\l_tmpa_tl{ \ocgbase_oc_bdc:n{\l_tmpa_tl} } % re-open the colourlink opened last \seq_get:cNT{g_pending_lnkcols_on_\int_use:c{g_ocgxii_page_int}_seq}\l_tmpa_tl{ \tl_gset:Nx\g_ocgxii_lnkcol_tl{{\l_tmpa_tl}} \ocgxii_colourlink_begin: } \int_gincr:N\g_ocgxii_page_int % copy pending ocg stack from previous page, if it has not been initialized % yet from aux file \seq_if_exist:cF{g_pending_ocgs_on_\int_use:c{g_ocgxii_page_int}_seq}{ \seq_gset_eq:cc{ g_pending_ocgs_on_\int_use:c{g_ocgxii_page_int}_seq }{ g_pending_ocgs_on_\int_eval:n{\g_ocgxii_page_int-\c_one_int}_seq } } %the same for link colour stack \seq_if_exist:cF{g_pending_lnkcols_on_\int_use:c{g_ocgxii_page_int}_seq}{ \seq_gset_eq:cc{ g_pending_lnkcols_on_\int_use:c{g_ocgxii_page_int}_seq }{ g_pending_lnkcols_on_\int_eval:n{\g_ocgxii_page_int-\c_one_int}_seq } } } \int_new:N\g_ocgxii_page_int %abs. page counter \cs_new_protected_nopar:Nn\ocgxii_ocglist_reset:{ \tl_clear_new:N\l_ocgxii_u_list_tl \tl_clear_new:N\l_ocgxii_d_list_tl \tl_clear_new:N\l_ocgxii_e_list_tl \tl_clear_new:N\l_ocgxii_x_list_tl } \cs_new_protected_nopar:Nn\ocgxii_ocglist_build:Nn{ \tl_set:Nx\l_ocglistarg_tl{#2}\tl_trim_spaces:N\l_ocglistarg_tl % first try splitting at commas (ocgx2 syntax) \tl_set_eq:NN\l_tmpa_tl\l_ocglistarg_tl \tl_remove_all:Nn\l_tmpa_tl{~} \seq_set_split:NnV\l_ocgxii_ocglistarg_seq{,}\l_tmpa_tl \seq_remove_all:Nn\l_ocgxii_ocglistarg_seq{} \tl_set:Nx\l_tmpa_tl{\seq_count:N\l_ocgxii_ocglistarg_seq} % now at spaces (legacy ocgx/ocg-p) \tl_set_eq:NN\l_tmpb_tl\l_ocglistarg_tl \tl_remove_all:Nn\l_tmpb_tl{,} \seq_set_split:NnV\l_tmpb_seq{~}\l_tmpb_tl \seq_remove_all:Nn\l_tmpb_seq{} \tl_set:Nx\l_tmpb_tl{\seq_count:N\l_tmpb_seq} %take the seq having more elements (guessing the separator most likely used) \int_compare:nT{\l_tmpb_tl>\l_tmpa_tl}{ \seq_set_eq:NN\l_ocgxii_ocglistarg_seq\l_tmpb_seq } \seq_map_variable:NNn\l_ocgxii_ocglistarg_seq\l_tempa_tl{ \ocgxii_process_ocgref:NN#1\l_tempa_tl } } \cs_new_protected_nopar:Nn\ocgxii_commalist_process:n{ \seq_set_split:Nnn\l_tmpa_seq{,}{#1} \ocgxii_ocglist_build:Nn\l_ocgxii_e_list_tl{\seq_item:Nn\l_tmpa_seq{1}} \ocgxii_ocglist_build:Nn\l_ocgxii_x_list_tl{\seq_item:Nn\l_tmpa_seq{2}} \ocgxii_ocglist_build:Nn\l_ocgxii_d_list_tl{\seq_item:Nn\l_tmpa_seq{3}} \ocgxii_ocglist_build:Nn\l_ocgxii_u_list_tl{\seq_item:Nn\l_tmpa_seq{4}} } \cs_new_protected_nopar:Nn\ocgxii_ocglist_process_idlist:nn{ \ocgxii_ocglist_reset: \tl_set:Nx\l_ocgxii_opt_tl{#1}\tl_remove_all:Nn\l_ocgxii_opt_tl{~} \str_case_e:nnF{\l_ocgxii_opt_tl}{ {onmouseup}{ \ocgxii_ocglist_build:Nn\l_ocgxii_u_list_tl{#2} } {onmousedown}{ \ocgxii_ocglist_build:Nn\l_ocgxii_d_list_tl{#2} } {onmouseenter}{ \ocgxii_ocglist_build:Nn\l_ocgxii_e_list_tl{#2} } {onmouseexit}{ \ocgxii_ocglist_build:Nn\l_ocgxii_x_list_tl{#2} } {onmouseall}{ \ocgxii_commalist_process:n{#2} } }{ \msg_error:nnx{ocgx2}{unknown~option}{\l_ocgxii_opt_tl} } } \int_new:N\g_ocgxii_widcount_int% widget counter %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % re-implement commands from ocgx.sty (all engines including ps2pdf [gs>=9.15]) % adding optional `*' (arg 1) -> non-breakable link instead of plain (multiline) % Link; % adding optional 2nd argument -> Button Widget (non-breakable) with one of % various mouse triggers (`troggerocgs` option from ocg-p) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \DeclareDocumentCommand\switchocg{s O{} m +m}{ \ocgxii_actionsocg:nnnnnn{#1}{#2}{#3}{}{}{#4} } \DeclareDocumentCommand\showocg{s O{} m +m}{ \ocgxii_actionsocg:nnnnnn{#1}{#2}{}{#3}{}{#4} } \DeclareDocumentCommand\hideocg{s O{} m +m}{ \ocgxii_actionsocg:nnnnnn{#1}{#2}{}{}{#3}{#4} } \DeclareDocumentCommand\actionsocg{s O{} m m m +m}{ \ocgxii_actionsocg:nnnnnn{#1}{#2}{#3}{#4}{#5}{#6} } \bool_new:N\l_ocgxii_mouse_triggers_bool \bool_new:N\l_ocgxii_nobreak_bool \cs_new_protected:Nn\ocgxii_actionsocg:nnnnnn{ \leavevmode \bool_set_false:N\l_ocgxii_mouse_triggers_bool \bool_set_false:N\l_ocgxii_nobreak_bool % explicitly non-breakable? \bool_if:nT{#1}{\bool_set_true:N\l_ocgxii_nobreak_bool} % %clear actions for various mouse triggers (e,d,x) \tl_clear:N\l_ocgxii_toswitch_e_tl \tl_clear:N\l_ocgxii_toswitch_x_tl \tl_clear:N\l_ocgxii_toswitch_d_tl \tl_clear:N\l_ocgxii_toshow_e_tl \tl_clear:N\l_ocgxii_toshow_x_tl \tl_clear:N\l_ocgxii_toshow_d_tl \tl_clear:N\l_ocgxii_tohide_e_tl \tl_clear:N\l_ocgxii_tohide_x_tl \tl_clear:N\l_ocgxii_tohide_d_tl % %process *all* mouse triggers (e,d,u,x) \ocgxii_ocglist_process_idlist:nn{ \tl_if_blank:oTF{#2}{onmouseup}{#2} }{#3} \tl_set_eq:NN\l_ocgxii_toswitch_e_tl\l_ocgxii_e_list_tl \tl_set_eq:NN\l_ocgxii_toswitch_x_tl\l_ocgxii_x_list_tl \tl_set_eq:NN\l_ocgxii_toswitch_d_tl\l_ocgxii_d_list_tl \tl_set_eq:NN\l_ocgxii_toswitch_u_tl\l_ocgxii_u_list_tl \ocgxii_ocglist_process_idlist:nn{ \tl_if_blank:oTF{#2}{onmouseup}{#2} }{#4} \tl_set_eq:NN\l_ocgxii_toshow_e_tl\l_ocgxii_e_list_tl \tl_set_eq:NN\l_ocgxii_toshow_x_tl\l_ocgxii_x_list_tl \tl_set_eq:NN\l_ocgxii_toshow_d_tl\l_ocgxii_d_list_tl \tl_set_eq:NN\l_ocgxii_toshow_u_tl\l_ocgxii_u_list_tl \ocgxii_ocglist_process_idlist:nn{ \tl_if_blank:oTF{#2}{onmouseup}{#2} }{#5} \tl_set_eq:NN\l_ocgxii_tohide_e_tl\l_ocgxii_e_list_tl \tl_set_eq:NN\l_ocgxii_tohide_x_tl\l_ocgxii_x_list_tl \tl_set_eq:NN\l_ocgxii_tohide_d_tl\l_ocgxii_d_list_tl \tl_set_eq:NN\l_ocgxii_tohide_u_tl\l_ocgxii_u_list_tl %any triggers apart from mouse-up? \str_if_eq:eeF{ \l_ocgxii_toswitch_e_tl\l_ocgxii_toswitch_x_tl\l_ocgxii_toswitch_d_tl \l_ocgxii_toshow_e_tl\l_ocgxii_toshow_x_tl\l_ocgxii_toshow_d_tl \l_ocgxii_tohide_e_tl\l_ocgxii_tohide_x_tl\l_ocgxii_tohide_d_tl }{}{ \bool_set_true:N\l_ocgxii_mouse_triggers_bool } % \bool_if:nTF{\l_ocgxii_nobreak_bool || \l_ocgxii_mouse_triggers_bool}{ \hbox_set:Nn\l_tmpa_box{#6} \pbs_pdfannot:nnnn{ \dim_use:N\box_wd:N\l_tmpa_box}{ \dim_use:N\box_ht:N\l_tmpa_box}{ \dim_use:N\box_dp:N\l_tmpa_box }{ \bool_if:NTF\l_ocgxii_mouse_triggers_bool{ % e,d,x mouse triggers require (non-breakable) /Widget annot with AA % (additional actions) dict /Subtype/Widget/Ff~65536/FT/Btn/BS<> /T~(ocgx2@\int_use:N\g_ocgxii_widcount_int) %treat mouse-up as mouse-click --> handle through /A dictionary \str_if_eq:eeF{}{ \l_ocgxii_toswitch_u_tl\l_ocgxii_toshow_u_tl\l_ocgxii_tohide_u_tl }{ /A <> } /AA << %\str_if_eq:eeF{}{ % mouse-up % \l_ocgxii_toswitch_u_tl\l_ocgxii_toshow_u_tl\l_ocgxii_tohide_u_tl %}{ % /U <> %} \str_if_eq:eeF{}{ % mouse-down \l_ocgxii_toswitch_d_tl\l_ocgxii_toshow_d_tl\l_ocgxii_tohide_d_tl }{ /D <> } \str_if_eq:eeF{}{ % mouse-enter \l_ocgxii_toswitch_e_tl\l_ocgxii_toshow_e_tl\l_ocgxii_tohide_e_tl }{ /E <> } \str_if_eq:eeF{}{ % mouse-exit \l_ocgxii_toswitch_x_tl\l_ocgxii_toshow_x_tl\l_ocgxii_tohide_x_tl }{ /X <> } >> }{ %mouse-up alone may go with simple /Link annot /Subtype/Link /A <> /Border~[0~0~0] } \cs_if_exist:NT\@pdfhighlight{ \ifx\@pdfhighlight\@empty\else/H\@pdfhighlight\fi } }\box_use_drop:N\l_tmpa_box \bool_if:NT\l_ocgxii_mouse_triggers_bool{ \pbs_appendtofields:n{\pbs_pdflastann:} \int_gincr:N\g_ocgxii_widcount_int } }{ %line-breakable annotation \pbs_pdflink:nn{ /Subtype/Link /A <> %look and feel of hyperref links, if hyperref has been loaded \cs_if_exist:NTF\Hy@setpdfborder{ \Hy@setpdfborder\g_ocgxii_patch_tl \ifx\@pdfhighlight\@empty\else/H\@pdfhighlight\fi \ifx\@linkbordercolor\relax\else/C[\@linkbordercolor]\fi \ifHy@pdfa /F~4\fi }{ /Border~[0~0~0] } }{ \cs_if_exist:NTF\Hy@colorlink{ \Hy@colorlink\@linkcolor#6\Hy@endcolorlink\Hy@VerboseLinkStop }{#6} } } } %mimic commands from ocg-p \keys_define:nn{ocgx2/ocgpcmd}{ triggerocg .choices:nn = { onmouseenter, onmouseexit, onmousedown, onmouseup, allactions }{ \str_if_eq:eeTF{\l_keys_choice_tl}{allactions}{ \tl_set:Nn\l_ocgxii_trigger_tl{onmouseall} }{ \tl_set_eq:NN\l_ocgxii_trigger_tl\l_keys_choice_tl } } } \DeclareDocumentCommand\toggleocgs{O{} m +m}{ \tl_clear_new:N\l_ocgxii_trigger_tl \keys_set:nn{ocgx2/ocgpcmd}{#1} \switchocg*[\l_ocgxii_trigger_tl]{#2}{#3} } \DeclareDocumentCommand\showocgs{O{} m +m}{ \tl_clear_new:N\l_ocgxii_trigger_tl \keys_set:nn{ocgx2/ocgpcmd}{#1} \showocg*[\l_ocgxii_trigger_tl]{#2}{#3} } \DeclareDocumentCommand\hideocgs{O{} m +m}{ \tl_clear_new:N\l_ocgxii_trigger_tl \keys_set:nn{ocgx2/ocgpcmd}{#1} \hideocg*[\l_ocgxii_trigger_tl]{#2}{#3} } \DeclareDocumentCommand\setocgs{O{} m m m +m}{ \tl_clear_new:N\l_ocgxii_trigger_tl \keys_set:nn{ocgx2/ocgpcmd}{#1} \actionsocg*[\l_ocgxii_trigger_tl]{#2}{#3}{#4}{#5} } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \cs_new_protected:Nn\ocgxii_process_ocgref:NN{ \tl_if_exist:cTF{ocgx2.ocg.#2}{ \tl_put_right:Nx#1{~\tl_use:c{ocgx2.ocg.#2}} }{ \msg_warning:nnx{ocgx2}{undefined~OCG}{#2} \tl_if_exist:NF\g_ocgxii_refundefwarned_tl{ \tl_new:N\g_ocgxii_refundefwarned_tl \AtEndDocument{\msg_warning:nn{ocgx2}{undefined~OCGs}} } } } %ocg environment opts \keys_define:nn{ocgx2/ocgenv}{ viewocg .choice:, viewocg / always .code:n={ \tl_set:Nn\l_ocgxii_view_tl{/View<>}}, viewocg / never .code:n={ \tl_set:Nn\l_ocgxii_view_tl{/View<>}}, viewocg / ifvisible .code:n={ \tl_clear:N\l_ocgxii_view_tl}, viewocg .default:n={ifvisible}, printocg .choice:, printocg / always .code:n={ \tl_set:Nn\l_ocgxii_print_tl{/Print<>}}, printocg / never .code:n={ \tl_set:Nn\l_ocgxii_print_tl{/Print<>}}, printocg / ifvisible .code:n={ \tl_clear:N\l_ocgxii_print_tl}, printocg .default:n={ifvisible}, exportocg .choice:, exportocg / always .code:n={ \tl_set:Nn\l_ocgxii_export_tl{/Export<>}}, exportocg / never .code:n={ \tl_set:Nn\l_ocgxii_export_tl{/Export<>}}, exportocg / ifvisible .code:n={\tl_clear:N\l_ocgxii_export_tl}, exportocg .default:n={ifvisible}, showingui .choices:nn = {true,false,always,never,iffirstuse}{ \bool_if:nTF{ \str_if_eq_p:ee{#1}{false} || \str_if_eq_p:ee{#1}{never} }{ \bool_set_false:N\l_ocgxii_showingui_bool }{ \bool_set_true:N\l_ocgxii_showingui_bool } }, showingui .default:n={true}, listintoolbar .meta:n = {showingui=#1}, listintoolbar .default:n={true}, radiobtngrps .code:n = { \clist_map_inline:nn{#1}{ \seq_if_in:NxF\l_ocgxii_rbgrps_seq{##1}{ \seq_put_right:Nx\l_ocgxii_rbgrps_seq{##1} } } }, radiobtngrps .value_required:n = {true}, radiobtngrp .meta:n={radiobtngrps={#1}}, radiobtngrp .value_required:n = {true} } \cs_new_protected:Nn\ocgxii_reset_cmd_opts:{ \tl_set_eq:NN\l_ocgxii_view_tl\g_ocgxii_view_tl \tl_set_eq:NN\l_ocgxii_print_tl\g_ocgxii_print_tl \tl_set_eq:NN\l_ocgxii_export_tl\g_ocgxii_export_tl \bool_set_eq:NN\l_ocgxii_showingui_bool\g_ocgxii_showingui_bool %stack of radio button group names the current ocg belongs to \seq_clear_new:N\l_ocgxii_rbgrps_seq } \msg_set:nnn{ocgx2}{rerun}{Rerun~to~get~OCG~references~right!} \msg_set:nnn{ocgx2}{undefined~OCG}{ Line~\msg_line_number: :~OCG~`#1'~is~not~defined. } \msg_set:nnn{ocgx2}{undefined~OCGs}{There~were~undefined~OCGs!} \msg_set:nnn{ocgx2}{unknown~option}{ Line~\msg_line_number: :~unknown~option~`#1'. } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % patch hyperref to ensure compatibility with our `ocgcolorlinks' option % % Plus: % % * add `ocgcolorlinks' support to all drivers % % * allows for `ocgcolorlinks' extending over % % line-breaks AND page-breaks % % with pdftex, luatex, xetex, dvipdfmx drivers % % based on Ben Lerner's idea % http://tex.stackexchange.com/a/104227; % with some improvements %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \cs_new_protected_nopar:Nn\ocgxii_colourlink_begin:{} \cs_new_protected_nopar:Nn\ocgxii_colourlink_end:{} \cs_new_protected_nopar:Nn\ocgxii_colourlink_nobreak_begin:{ \hbox_set:Nw\l_tmpa_box\color@begingroup \tl_set_eq:NN\color@setgroup\group_begin: } \cs_new_protected_nopar:Nn\ocgxii_colourlink_nobreak_end:{ \color@endgroup\hbox_set_end: \mbox{ \ocgbase_oc_bdc:n{\tl_use:c{ocgxii_ocg_OCPrint}} \hbox_to_zero:n{\box_use:N\l_tmpa_box\hss} \ocgbase_oc_emc: \ocgbase_oc_bdc:n{\tl_use:c{ocgxii_ocg_OCView}} \group_begin: \exp_after:wN\HyColor@UseColor\l_ocgxii_lnkcol_tl \box_use_drop:N\l_tmpa_box \group_end: \ocgbase_oc_emc: } } \seq_new:N\g_ocgxii_lnk_color_seq %stack of colours of currently open links \tl_new:N\g_ocgxii_patch_tl % `BorderArrayPatch' for dvips %we'll be using the downscaled space character (32) from the pzdr % (dingbats) font at the end of every ocgcolorlink in order to prevent % empty links from flooding the page with link colour \cs_new_protected_nopar:Npn\ocgxii_tiny_space_char:{ \group_begin: \font\l_tmpa_tl=pzdr~scaled~1\l_tmpa_tl\char32 \group_end: } \bool_if:nTF{\sys_if_output_dvi_p: && !\g_ocgxii_dvipdfmx_bool}{ % non-breakable links in dvips \cs_new_protected_nopar:Nn\ocgxii_enable_ocglinks:{ \def\Hy@colorlink##1{ \group_begin: \tl_set:Nn\l_ocgxii_lnkcol_tl{##1} \ocgxii_colourlink_nobreak_begin: } \def\Hy@endcolorlink{ \ocgxii_colourlink_nobreak_end: \group_end: } } \tl_gset:Nn\g_ocgxii_patch_tl{BorderArrayPatch} }{ % pdftex,luatex,xetex,dvipdfmx: % ocgcolorlinks that extend over line and page breaks \cs_new_protected_nopar:Nn\ocgxii_enable_ocglinks:{ \def\Hy@colorlink##1{ \ifx\Hy@setbreaklinks\@gobble\else \Hy@breaklinkstrue \fi \ifHy@breaklinks \seq_get_left:NNT\g_ocgxii_lnk_color_seq\l_tmpa_tl{ \tl_gset_eq:NN\g_ocgxii_lnkcol_tl\l_tmpa_tl \ocgxii_colourlink_end: } \group_begin: \ocgxii_colourlink_begin: \seq_gpush:Nx\g_ocgxii_lnk_color_seq{{##1}} \ocgxii_stack_shipout:NN\ocgxii@lnkcol@stack@on@page\g_ocgxii_lnk_color_seq \else \group_begin: \tl_set:Nn\l_ocgxii_lnkcol_tl{##1} \ocgxii_colourlink_nobreak_begin: \fi } \def\Hy@endcolorlink{ \ifHy@breaklinks \seq_gpop:NN\g_ocgxii_lnk_color_seq\l_tmpa_tl \tl_gset_eq:NN\g_ocgxii_lnkcol_tl\l_tmpa_tl \ocgxii_stack_shipout:NN\ocgxii@lnkcol@stack@on@page\g_ocgxii_lnk_color_seq \ocgxii_colourlink_end: \group_end: \seq_get_left:NNT\g_ocgxii_lnk_color_seq\l_tmpa_tl{ \tl_gset_eq:NN\g_ocgxii_lnkcol_tl\l_tmpa_tl \ocgxii_colourlink_begin: } \else \ocgxii_colourlink_nobreak_end: \group_end: \fi } } \bool_if:NT\l_ocgxii_ocgcolorlinks_bool{ \cs_gset_protected_nopar:Nn\ocgxii_colourlink_begin:{ %tiny space char put here in order to neutralise possible transformation %matrix modifications from previous \pdfliteral{} (TikZ makes a lot use of it) \hbox_overlap_left:n{\ocgxii_tiny_space_char:} \pbs_literal:nn{page}{q~7~Tr} } \cs_gset_protected_nopar:Nn\ocgxii_colourlink_end:{ %a tiny space char should keep empty link annots from flooding the page % with link colour \hbox_overlap_left:n{\ocgxii_tiny_space_char:} \ocgbase_oc_bdc:n{\tl_use:c{ocgxii_ocg_OCPrint}} \pbs_literal:nn{page}{-88888~-88888~99999~99999~re~f} \ocgbase_oc_emc: \ocgbase_oc_bdc:n{\tl_use:c{ocgxii_ocg_OCView}} \group_begin: \exp_after:wN\HyColor@UseColor\g_ocgxii_lnkcol_tl \pbs_literal:nn{page}{-88888~-88888~99999~99999~re~f} \group_end: \ocgbase_oc_emc: \pbs_literal:nn{page}{0~Tr~Q} } } } % user command for protecting graphical content (external file, inline % [e. g. TikZ], \fbox{...}) inside breakable ocgcolorlink \bool_if:NTF\l_ocgxii_ocgcolorlinks_bool{ \DeclareDocumentCommand\ocglinkprotect{m}{ \seq_get_left:NNT\g_ocgxii_lnk_color_seq\l_tmpa_tl{ \tl_gset_eq:NN\g_ocgxii_lnkcol_tl\l_tmpa_tl \ocgxii_colourlink_end: \group_begin: \tl_set_eq:NN\l_ocgxii_lnkcol_tl\l_tmpa_tl \ocgxii_colourlink_nobreak_begin: } \group_begin: \cs_set_nopar:Npn\ocglinkprotect##1{##1} % in case of nesting \sys_if_output_pdf:TF{ \leavevmode \hbox_set:Nn\l_tmpb_box{#1} \hbox_to_wd:nn{\box_wd:N\l_tmpb_box}{ \vrule~width~\c_zero_dim~height~\box_ht:N\l_tmpb_box~ depth~\box_dp:N\l_tmpb_box \pbs_pdfxform:nnnnn{1}{0}{}{}{\l_tmpb_box} \pbs_pdfrefxform:n{\pbs_pdflastxform:} \hss } }{#1} \group_end: \seq_get_left:NNT\g_ocgxii_lnk_color_seq\l_tmpa_tl{ \ocgxii_colourlink_nobreak_end: \group_end: \ocgxii_colourlink_begin: } } }{ \DeclareDocumentCommand\ocglinkprotect{m}{#1} } \bool_if:NT\l_ocgxii_ocgcolorlinks_bool{ \@ifpackageloaded{hyperref}{ \Hy@colorlinkstrue \AtBeginDocument{ \ocgbase_new_ocg:nnn{OCView}{ /Print<> /Export<> }{on} \tl_gset:cx{ocgxii_ocg_OCView}{\ocgbase_last_ocg:} %in case somebody wants to reopen with \begin{ocg}... \tl_gset:cx{ocgxii_ocg_OCView.opts}{ showingui=never,printocg=never,exportocg=never } \ocgbase_new_ocg:nnn{OCPrint}{ /Print<> /Export<> }{off} \tl_gset:cx{ocgxii_ocg_OCPrint}{\ocgbase_last_ocg:} \tl_gset:cx{ocgxii_ocg_OCPrint.opts}{ showingui=never,printocg=always,exportocg=always } \ocgxii_enable_ocglinks: \iow_now:Nx\@mainaux{ \token_to_str:N\ocgxii@newkey{ocgx2.ocg.OCView}{ \tl_use:c{ocgxii_ocg_OCView}} } \iow_now:Nx\@mainaux{ \token_to_str:N\ocgxii@newkey{ocgx2.ocg.OCPrint}{ \tl_use:c{ocgxii_ocg_OCPrint}} } } }{ \msg_error:nnn{ocgx2}{missing~package}{hyperref} } } \bool_if:NT\l_ocgxii_tikz_bool{\cs_set_eq:NN\ocgxii@trmspc\tl_trim_spaces:N} } \group_begin: \bool_if:nF{ \bool_lazy_or_p:nn{ \bool_lazy_and_p:nn{ \cs_if_exist_p:N\pdfmanagement_if_active_p: }{ \pdfmanagement_if_active_p: } }{ \bool_if_p:N\l_ocgxii_tikz_bool } }{\aftergroup\endinput} \group_end: \ExplSyntaxOff \if@ocgxii@testphase\else %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % TikZ related code follows (to be enabled with package option `tikz') \RequirePackage{tikz} \usetikzlibrary{calc} % helper, replaces all spaces in #1 with `_' \def\ocgxii@cnvspc#1{\expandafter\ocgxii@@cnvspc#1 \@nil} \def\ocgxii@@cnvspc#1 #2\@nil{#1\ifx\@nil#2\@nil\else_\ocgxii@@cnvspc#2\@nil\fi} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Copyright notice: The code that follows until the end of the file was % taken in large parts from Paul Gaborit's `tikzlibraryocgx.code.tex' with % some additions/fixes: % % - opts={...} inside ocg={...} allows ocg-environment options to be passed % to a TikZ scope % % - The style ocmd={...} is another way for turning a TikZ scope into a PDF % layer (in addition to ocg={...}). It has two sub-keys, ref={...} and % visibility={...}, which have the same meaning as the optional and the % mandatory arguments of the `ocmd' environment % % - TikZ objects to be turned into OCG switching links accept the additional % key % % trigger ocg = onmousenter | onmouseexit | onmousedown | onmouseup | % onmouseall % % to react to various mouse gestures % % - switching links properly sized and working in scaled tikzpictures % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \tikzset{ ocg/.style={ocg/.cd,#1,/tikz/.cd}, ocg={ % parameters name/.store in=\ocgxii@name, opts/.store in=\ocgxii@opts, visibility/.store in=\ocgxii@visibility, status/.is choice, status/visible/.style={visibility=1}, status/invisible/.style={visibility=0}, status/true/.style={visibility=1}, status/false/.style={visibility=0}, status/on/.style={visibility=1}, status/off/.style={visibility=0}, status/1/.style={visibility=1}, status/0/.style={visibility=0}, % default values name=, opts=, % NEW status=on, % ref ref/.style={ /tikz/execute at begin scope={% \begin{ocg}[\ocgxii@opts]{% \ifx\empty\ocgxii@name\empty#1\else\ocgxii@name\fi% }{#1}{\ocgxii@visibility}}, /tikz/execute at end scope={\end{ocg}}, } }, ocmd/.style={ ocmd/.cd, #1, /tikz/execute at begin scope={% \begin{ocmd}[\ocgxii@ocmdref]{\ocgxii@ocmdvisibility}% }, /tikz/execute at end scope={\end{ocmd}}, /tikz/.cd }, ocmd={ % parameters ref/.store in=\ocgxii@ocmdref, visibility/.store in=\ocgxii@ocmdvisibility, % default values ref=, visibility=, }, trigger ocg/.store in=\ocgxii@trigger, trigger ocg/.value required, switch ocg/.style={ postaction={ path picture={ \path (path picture bounding box.south west) coordinate (p1) (path picture bounding box.north east) coordinate (p2) (p1) node[inner sep=0pt,anchor=south west,outer sep=0pt] {% \ifdefined\ocgxii@trigger% \switchocg*[\ocgxii@trigger]{#1}{% \tikz \useasboundingbox (p1) rectangle (p2);}% \else% \switchocg*{#1}{\tikz \useasboundingbox (p1) rectangle (p2);}% \fi% }; } } }, switch ocg with mark on/.style 2 args={ postaction={ path picture={% \edef\ocgxii@argone{#1}%ocg ref for checkmark \ocgxii@trmspc\ocgxii@argone% \global\let\ocgxii@argone\ocgxii@argone% %default ocg ref for checkmark, if nothing provided in #1 \xdef\ocgxii@argtwo{#2.mark}% \xdef\ocgxii@argtwo{\ocgxii@cnvspc{\ocgxii@argtwo}}% \begin{ocg}[showingui=false]{% \ifx\ocgxii@argone\@empty\ocgxii@argtwo\else\ocgxii@argone\fi% }{% \ifx\ocgxii@argone\@empty\ocgxii@argtwo\else\ocgxii@argone\fi% }{on}% \draw (path picture bounding box.south west) -- (path picture bounding box.north east) (path picture bounding box.south east) -- (path picture bounding box.north west) ; \end{ocg}% }, switch ocg={% \ifx\ocgxii@argone\@empty\ocgxii@argtwo\else{\ocgxii@argone}\fi\space #2% } } }, switch ocg with mark off/.style 2 args={ postaction={ path picture={% \edef\ocgxii@argone{#1}%ocg ref for checkmark \ocgxii@trmspc\ocgxii@argone% \global\let\ocgxii@argone\ocgxii@argone% %default ocg ref for checkmark, if nothing provided in #1 \xdef\ocgxii@argtwo{#2.mark}% \xdef\ocgxii@argtwo{\ocgxii@cnvspc{\ocgxii@argtwo}}% \begin{ocg}[showingui=false]{% \ifx\ocgxii@argone\@empty\ocgxii@argtwo\else\ocgxii@argone\fi% }{% \ifx\ocgxii@argone\@empty\ocgxii@argtwo\else\ocgxii@argone\fi% }{off}% \draw (path picture bounding box.south west) -- (path picture bounding box.north east) (path picture bounding box.south east) -- (path picture bounding box.north west) ; \end{ocg}% }, switch ocg={% \ifx\ocgxii@argone\@empty\ocgxii@argtwo\else{\ocgxii@argone}\fi\space #2% } } }, show ocg/.style={ postaction={ path picture={ \path (path picture bounding box.south west) coordinate (p1) (path picture bounding box.north east) coordinate (p2) (p1) node[inner sep=0pt,anchor=south west,outer sep=0pt] {% \ifdefined\ocgxii@trigger% \showocg*[\ocgxii@trigger]{#1}{% \tikz \useasboundingbox (p1) rectangle (p2);}% \else% \showocg*{#1}{\tikz \useasboundingbox (p1) rectangle (p2);}% \fi% }; } } }, hide ocg/.style={ postaction={ path picture={ \path (path picture bounding box.south west) coordinate (p1) (path picture bounding box.north east) coordinate (p2) (p1) node[inner sep=0pt,anchor=south west,outer sep=0pt] {% \ifdefined\ocgxii@trigger% \hideocg*[\ocgxii@trigger]{#1}{% \tikz \useasboundingbox (p1) rectangle (p2);}% \else% \hideocg*{#1}{\tikz \useasboundingbox (p1) rectangle (p2);}% \fi% }; } } }, actions ocg/.style n args={3}{ postaction={ path picture={ \path (path picture bounding box.south west) coordinate (p1) (path picture bounding box.north east) coordinate (p2) (p1) node[inner sep=0pt,anchor=south west,outer sep=0pt] {% \ifdefined\ocgxii@trigger% \actionsocg*[\ocgxii@trigger]{#1}{#2}{#3}{% \tikz \useasboundingbox (p1) rectangle (p2);}% \else% \actionsocg*{#1}{#2}{#3}{% \tikz \useasboundingbox (p1) rectangle (p2);}% \fi% }; } } } } \fi \begingroup \if@ocgxii@testphase\else\aftergroup\endinput\fi \endgroup %%%%%%%%%%%%%%%%%%%%%% /pdfmanagement-testphase %%%%%%%%%%%%%%%%%%% % pdfmanagement-testphase version starts here %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ocgx2.sty % % Copyright 2015--\today, Alexander Grahn % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % The intent of this package is to be a drop-in replacement for the already % existing CTAN package `ocgx' by Paul Gaborit, and also for `ocg-p' and `ocg'. % % It re-implements the functionality of the ocg, ocgx and ocg-p packages % and adds support for all known engines and backends including % latex+dvips+ps2pdf, xelatex, latex+dvipdfmx, lualatex. % % With ocgx2, PDF layers may extend across page breaks. % % ocgx2 implements OCMDs (optional content membership dictionaries) % % Adds some minor improvements, such as package options, remembering option. % settings of reopened ocgs, correct behaviour of ocg switching links that were % themselves placed on layers, compatibility with the animate and media9 % packages. % % Re-implements hyperref's `ocgcolorlinks' option to produce coloured links % that may wrap around line breaks and page breaks. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License. % % The latest version of this license is in % http://mirrors.ctan.org/macros/latex/base/lppl.txt % % This work has the LPPL maintenance status `maintained'. % % The Current Maintainer of this work is A. Grahn. \def\g@ocgxii@date@tl{2024/03/18} \def\g@ocgxii@version@tl{0.57} \ProvidesExplPackage{ocgx2}{\g@ocgxii@date@tl}{\g@ocgxii@version@tl} {ports `ocgx' functionality to dvips+ps2pdf, xelatex and dvipdfmx} %creating global definitions \cs_new_protected:Npn\ocgxii@newkey#1#2{\tl_gset:cx{#1}{#2}} \cs_new_protected:Npn\ocgxii@newkeynoexp#1#2{\tl_gset:cn{#1}{#2}} \AtBeginDocument{ \iow_now:Nx\@mainaux{ \token_to_str:N\providecommand\token_to_str:N\ocgxii@newkey[2]{} } \iow_now:Nx\@mainaux{ \token_to_str:N\providecommand\token_to_str:N\ocgxii@newkeynoexp[2]{} } \iow_now:Nx\@mainaux{ \token_to_str:N\providecommand\token_to_str:N\ocgxii@ocg@stack@on@page[2]{} } \iow_now:Nx\@mainaux{ \token_to_str:N\providecommand \token_to_str:N\ocgxii@lnkcol@stack@on@page[2]{} } } \msg_set:nnn{ocgx2}{generic~msg}{#1\\#2} % package options \msg_set:nnnn{ocgx2}{unknown~package~option}{Unknown~package~option~`#1'.}{ Package option~'#1'~is~unknown;\\ perhaps~it~is~spelled~incorrectly. } \bool_new:N\g_ocgxii_dvipdfmx_bool \bool_new:N\l_ocgxii_tikz_bool \bool_new:N\l_ocgxii_ocgcolorlinks_bool \bool_new:N\g_ocgxii_showingui_bool \bool_new:N\l_ocgxii_showingui_bool \keys_define:nn{ocgx2}{ pdftex.code:n = {}, pdftex.value_forbidden:n = true, luatex.code:n = {}, luatex.value_forbidden:n = true, xetex.code:n = {}, xetex.value_forbidden:n = true, dvips.code:n = {}, dvips.value_forbidden:n = true, dvipdfmx .code:n = { \PassOptionsToPackage{dvipdfmx}{ocgbase} }, dvipdfmx .value_forbidden:n = true, viewocg .choice:, viewocg / always .code:n={ \tl_gset:Nn\g_ocgxii_view_tl{/View<>}}, viewocg / never .code:n={ \tl_gset:Nn\g_ocgxii_view_tl{/View<>}}, viewocg / ifvisible .code:n={\tl_gclear_new:N\g_ocgxii_view_tl}, viewocg .default:n={ifvisible}, printocg .choice:, printocg / always .code:n={ \tl_gset:Nn\g_ocgxii_print_tl{/Print<>}}, printocg / never .code:n={ \tl_gset:Nn\g_ocgxii_print_tl{/Print<>}}, printocg / ifvisible .code:n={\tl_gclear_new:N\g_ocgxii_print_tl}, printocg .default:n={ifvisible}, exportocg .choice:, exportocg / always .code:n={ \tl_gset:Nn\g_ocgxii_export_tl{/Export<>}}, exportocg / never .code:n={ \tl_gset:Nn\g_ocgxii_export_tl{/Export<>}}, exportocg / ifvisible .code:n={\tl_gclear_new:N\g_ocgxii_export_tl}, exportocg .default:n={ifvisible}, showingui .choices:nn = {true,false,always,never,iffirstuse}{ \bool_if:nTF{ \str_if_eq_p:ee{#1}{false} || \str_if_eq_p:ee{#1}{never} }{ \bool_gset_false:N\g_ocgxii_showingui_bool }{ \bool_gset_true:N\g_ocgxii_showingui_bool } }, showingui .default:n={true}, listintoolbar .meta:n = {showingui=#1}, listintoolbar .default:n={true}, tikz .bool_set:N = \l_ocgxii_tikz_bool, tikz .default:n = true, ocgcolorlinks .bool_set:N = \l_ocgxii_ocgcolorlinks_bool, ocgcolorlinks .default:n = true, unknown .code:n = { \msg_error:nnx{ocgx2}{unknown~package~option}{\l_keys_key_tl} } } %package options preset \keys_set:nn{ocgx2}{viewocg,printocg,exportocg,showingui,tikz=false} %process package options \ProcessKeyOptions[ocgx2] \RequirePackage{ocgbase} %also loads pdfbase.sty \bool_gset_eq:NN\g_ocgxii_dvipdfmx_bool\g_pbs_dvipdfmx_bool \bool_if:NT\g_pbs_dvisvgm_bool{ \msg_error:nnn{ocgx2}{generic~msg}{ Package~`ocgx2'~is~incompatible~with~the~`dvisvgm'~backend. } } %re-implement ocg-p's `ocg' environment \DeclareDocumentEnvironment{ocg}{O{}mmm}{ \ocgxii_begin_ocg:nnnn{#1}{#2}{#3}{#4} }{ \ocgxii_end_ocg: } \cs_new_protected_nopar:Nn\ocgxii_begin_ocg:nnnn{ \group_begin: \ocgxii_reset_cmd_opts: % ... to the user-set package options \tl_set:Nx\l_ocgxii_argiv_tl{\tl_trim_spaces:n{#4}} \tl_if_exist:cTF{ocgxii_ocg_#3}{ %re-open existing layer \tl_set:Nx\l_tempa_tl{\tl_use:c{ocgxii_ocg_#3.opts},#1} \tl_gset:cx{ocgxii_ocg_#3.opts}{\l_tempa_tl} %new options appended \keys_set:nV{ocgx2/ocgenv}\l_tempa_tl \bool_if:nTF{ %initial visibility \str_if_eq_p:ee{\l_ocgxii_argiv_tl}{1} || \str_if_eq_p:ee{\l_ocgxii_argiv_tl}{on} || \str_if_eq_p:ee{\l_ocgxii_argiv_tl}{true} }{ \ocgbase_del_from_off_list:n{\tl_use:c{ocgxii_ocg_#3}} }{ \ocgbase_add_to_off_list:n{\tl_use:c{ocgxii_ocg_#3}} } }{ \tl_set:Nx\l_tempa_tl{#1} \tl_gset:cx{ocgxii_ocg_#3.opts}{\l_tempa_tl} \keys_set:nV{ocgx2/ocgenv}\l_tempa_tl \ocgbase_new_ocg:nnn{#2}{ \l_ocgxii_view_tl\l_ocgxii_print_tl\l_ocgxii_export_tl }{\l_ocgxii_argiv_tl} \tl_gset:cx{ocgxii_ocg_#3}{\ocgbase_last_ocg:} \tl_gset:cx{ocgx2.ocg.\ocgbase_last_ocg:}{\ocgbase_last_ocg:} \tl_gset:cx{ocgx2.ocg.#3}{\ocgbase_last_ocg:} \iow_now:Nx\@mainaux{ \token_to_str:N\ocgxii@newkey{ocgx2.ocg.#3}{\ocgbase_last_ocg:} } } \bool_if:nT{ !\cs_if_exist:cTF{ocgx2.ocg.#3}{ \str_if_eq_p:ee{\tl_use:c{ocgx2.ocg.#3}}{\tl_use:c{ocgxii_ocg_#3}} }{ \c_false_bool } }{ \tl_if_exist:NF\g_ocgxii_rerunwarned_tl{ \tl_new:N\g_ocgxii_rerunwarned_tl \AtEndDocument{\msg_warning:nn{ocgx2}{rerun}} } } \seq_map_inline:Nn\l_ocgxii_rbgrps_seq{% process list of radio btn groups \ocgbase_add_ocg_to_radiobtn_grp:nn{##1}{\tl_use:c{ocgxii_ocg_#3}} } \ocgbase_open_stack_push:n{\tl_use:c{ocgxii_ocg_#3}} \ocgxii_make_oc_entry: \ocgxii_stack_shipout:NN\ocgxii@ocg@stack@on@page\g_ocgbase_open_stack_seq % insert OCG into Order tree \bool_if:NT\l_ocgxii_showingui_bool{ \ocgbase_tree_node_begin:n{\tl_use:c{ocgxii_ocg_#3}} } \group_end: \ocgbase_oc_bdc:n{\tl_use:c{ocgxii_ocg_#3}} \ignorespaces } \cs_new_protected_nopar:Nn\ocgxii_end_ocg:{ \unskip \ocgbase_oc_emc: \ocgbase_tree_node_end: \ocgbase_open_stack_pop:N\l_trash_tl \ocgxii_make_oc_entry: \ocgxii_stack_shipout:NN\ocgxii@ocg@stack@on@page\g_ocgbase_open_stack_seq } % OCMD implementation \DeclareDocumentEnvironment{ocmd}{O{}m}{ \ocgxii_begin_ocmd:on{#1}{#2} \ignorespaces }{ \unskip \ocgxii_end_ocmd: } \cs_new_protected_nopar:Nn\ocgxii_protected_dummy_cs:n{} \cs_new_protected_nopar:Nn\ocgxii_begin_ocmd:nn{ % #1: id, \bool_if:nTF{ % #2: visib. expr. or policy \tl_if_blank:oTF{#1}{ \c_false_bool }{ \tl_if_exist_p:c{ocgxii_ocmd_#1} } }{ % re-open existing ocmd \tl_set_eq:Nc\l_ocgxii_cur_ocmd_tl{ocgxii_ocmd_#1} }{ % new ocmd \group_begin: \cs_set_eq:NN\AllOn \ocgxii_protected_dummy_cs:n \cs_set_eq:NN\AnyOn \ocgxii_protected_dummy_cs:n \cs_set_eq:NN\AnyOff\ocgxii_protected_dummy_cs:n \cs_set_eq:NN\AllOff\ocgxii_protected_dummy_cs:n \cs_set_eq:NN\Not \ocgxii_protected_dummy_cs:n \cs_set_eq:NN\And \ocgxii_protected_dummy_cs:n \cs_set_eq:NN\Or \ocgxii_protected_dummy_cs:n \ocgxii_ocmd_read_visbility:xN{#2}\l_ocgxii_ocmd_visibility_tl \pdf_object_new:xn{g_object_\int_use:N\g_ocgbase_int _pdf}{dict} \pdf_object_write:xx{g_object_\int_use:N\g_ocgbase_int _pdf}{ /Type/OCMD\l_ocgxii_ocmd_visibility_tl } \tl_gset:cx{g_pbs_objname_\pdf_object_ref_last: _tl}{ g_object_\int_use:N\g_ocgbase_int _pdf } \int_gincr:N\g_ocgbase_int %if only visb. policy is given, generate equivalent visib. expression, %needed for stack of open layers and \ocgxii_make_oc_entry: command \ocgxii_ocmd_make_equiv_ve:xN{#2}\l_ocgxii_ocmd_equiv_ve_tl \tl_gset:co{ocgx2.ocmd.\pdf_object_ref_last:}{\l_ocgxii_ocmd_equiv_ve_tl} \group_end: \tl_set:Nx\l_ocgxii_cur_ocmd_tl{\pdf_object_ref_last:} \tl_if_blank:oF{#1}{ \tl_gset:cx{ocgxii_ocmd_#1}{\pdf_object_ref_last:} \group_begin: \cs_set_eq:NN\AllOn \ocgxii_protected_dummy_cs:n \cs_set_eq:NN\AnyOn \ocgxii_protected_dummy_cs:n \cs_set_eq:NN\AnyOff\ocgxii_protected_dummy_cs:n \cs_set_eq:NN\AllOff\ocgxii_protected_dummy_cs:n \cs_set_eq:NN\Not \ocgxii_protected_dummy_cs:n \cs_set_eq:NN\And \ocgxii_protected_dummy_cs:n \cs_set_eq:NN\Or \ocgxii_protected_dummy_cs:n \iow_now:Nx\@mainaux{\exp_not:N\ocgxii@newkeynoexp{ocgx2.ocmd.#1}{#2}} \group_end: } } \ocgbase_open_stack_push:n{\l_ocgxii_cur_ocmd_tl} \ocgxii_make_oc_entry: \ocgxii_stack_shipout:NN\ocgxii@ocg@stack@on@page\g_ocgbase_open_stack_seq \ocgbase_oc_bdc:n{\l_ocgxii_cur_ocmd_tl} } \cs_generate_variant:Nn\ocgxii_begin_ocmd:nn{on} \cs_new_protected_nopar:Nn\ocgxii_end_ocmd:{ \ocgbase_oc_emc: \ocgbase_open_stack_pop:N\l_trash_tl \ocgxii_make_oc_entry: %update \ocgxii_stack_shipout:NN\ocgxii@ocg@stack@on@page\g_ocgbase_open_stack_seq } %visibility expressions \cs_new_protected_nopar:Nn\ocgxii_ocmd_read_visbility:nN{ \int_zero:N\l_ocgxii_ve_cnt_int \int_zero:N\l_ocgxii_vp_cnt_int \tl_clear_new:N#2 \clist_map_inline:nn{#1}{\ocgxii_omcd_parse_argument:nN{##1}#2} } \cs_generate_variant:Nn\ocgxii_ocmd_read_visbility:nN{xN} \cs_new_protected_nopar:Nn\ocgxii_omcd_parse_argument:nN{ \cs_set_eq:NN\AllOn \ocgxii_vp_check:n \cs_set_eq:NN\AnyOn \ocgxii_vp_check:n \cs_set_eq:NN\AnyOff\ocgxii_vp_check:n \cs_set_eq:NN\AllOff\ocgxii_vp_check:n \cs_set_eq:NN\Not\ocgxii_ve_check:n \cs_set_eq:NN\And\ocgxii_ve_check:n \cs_set_eq:NN\Or \ocgxii_ve_check:n \tl_if_exist:cTF{ocgx2.ocg.#1}{ \msg_error:nnxx{ocgx2}{generic~msg}{ OCG~ids~cannot~be~directly~used~in~the~visibility~argument~of~an~ `ocmd'~environment. }{\g_ocgxii_help_msg_tl} }{ \tl_if_exist:cTF{ocgx2.ocmd.#1}{ \msg_error:nnxx{ocgx2}{generic~msg}{ OCMD~ids~cannot~be~directly~used~in~the~visibility~argument~of~an~ `ocmd'~environment. }{\g_ocgxii_help_msg_tl} }{ \tl_if_exist:cTF{ocgxii_#1}{ \str_case_e:nn{\tl_use:c{ocgxii_#1}}{ {VisExpr}{ \int_incr:N\l_ocgxii_ve_cnt_int \int_compare:nNnTF{\l_ocgxii_ve_cnt_int}>{1}{ \msg_error:nnnn{ocgx2}{generic~msg}{ More~than~one~visibility~expression~passed~to~the~`ocmd'~ environment. }{ At~most~one~visibility~expression~is~allowed.~A~visibility~ expression~is~a~boolean~expression~built~by~nesting~any~number~of~ \And{...},~\Or{...},~\Not{...}~commands. } }{ \tl_put_right:Nx#2{/VE~} \ocgxii_ocmd_expression_parser:nN{#1}#2 } } {VisPol}{ \int_incr:N\l_ocgxii_vp_cnt_int \int_compare:nNnTF{\l_ocgxii_vp_cnt_int}>{1}{ \msg_error:nnnn{ocgx2}{generic~msg}{ More~than~one~visibility~policy~passed~to~the~`ocmd'~environment. }{ At~most~one~visibility~policy~out~of~\AllOn{...},~\AnyOn{...},~ \AnyOff{...},~\AllOff{...}~is~allowed.~Any~number~of~OCG~IDs,~ separated~by~commas,~may~be~passed~as~arguments~to~these~ commands,~but~commands~may~not~be~nested.~For~complex~visibilty~ relations,~consider~using~a~visibility~expression. } }{ \ocgxii_ocmd_expression_parser:nN{#1}#2 } } } }{ \msg_error:nnxx{ocgx2}{generic~msg}{ The~visibility~argument~of~the~`ocmd'~environment~cannot~be~parsed. }{\g_ocgxii_help_msg_tl} } } } } \cs_generate_variant:Nn\ocgxii_omcd_parse_argument:nN{xN} \cs_generate_variant:Nn\ocgxii_omcd_parse_argument:nN{oN} \cs_new_protected_nopar:Nn\ocgxii_ocmd_expression_parser:nN{ \cs_set_eq:NN\AllOn \ocgxii_vp_check:n \cs_set_eq:NN\AnyOn \ocgxii_vp_check:n \cs_set_eq:NN\AnyOff\ocgxii_vp_check:n \cs_set_eq:NN\AllOff\ocgxii_vp_check:n \cs_set_eq:NN\Not\ocgxii_ve_check:n \cs_set_eq:NN\And\ocgxii_ve_check:n \cs_set_eq:NN\Or \ocgxii_ve_check:n \tl_if_exist:cTF{ocgx2.ocg.#1}{% ocg reference \tl_put_right:Nx#2{~\tl_use:c{ocgx2.ocg.#1}} }{ \tl_if_exist:cTF{ocgx2.ocmd.#1}{% ocmd reference \ocgxii_ocmd_expression_parser:vN{ocgx2.ocmd.#1}#2 }{ \tl_if_exist:cTF{ocgxii_#1}{% visib. bool expression or policy directive \bool_if:nTF{ \str_if_eq_p:ee{\tl_use:c{ocgxii_#1}}{VisExpr} && \bool_if_p:N\l_ocgxii_vp_open_bool || \str_if_eq_p:ee{\tl_use:c{ocgxii_#1}}{VisPol} && \bool_if_p:N\l_ocgxii_ve_open_bool }{ \msg_error:nnxx{ocgx2}{generic~msg}{ Visibility~policy~and~expression~commands~cannot~be~mixed. }{\g_ocgxii_help_msg_tl} }{ \bool_if:nT{ \str_if_eq_p:ee{\tl_use:c{ocgxii_#1}}{VisPol} && \bool_if_p:N\l_ocgxii_vp_open_bool }{ \msg_error:nnxx{ocgx2}{generic~msg}{ Visibility~policy~commands~cannot~be~nested.~For~more~complex~ visibilty~relations,~consider~using~a~visibility~expression. }{\g_ocgxii_help_msg_tl} } } \cs_set_eq:NN\AllOn \ocgxii_vp_allon:nN \cs_set_eq:NN\AnyOn \ocgxii_vp_anyon:nN \cs_set_eq:NN\AnyOff\ocgxii_vp_anyoff:nN \cs_set_eq:NN\AllOff\ocgxii_vp_alloff:nN \cs_set_eq:NN\Not\ocgxii_ve_not:nN \cs_set_eq:NN\And\ocgxii_ve_and:nN \cs_set_eq:NN\Or \ocgxii_ve_or:nN #1#2 }{ \msg_warning:nnx{ocgx2}{undefined~OCG}{#1} \tl_if_exist:NF\g_ocgxii_refundefwarned_tl{ \tl_new:N\g_ocgxii_refundefwarned_tl \AtEndDocument{\msg_warning:nn{ocgx2}{undefined~OCGs}} } } } } } \cs_generate_variant:Nn\ocgxii_ocmd_expression_parser:nN{vN} % visib. policy directives \cs_new_protected_nopar:Nn\ocgxii_vp_allon:nN{ \bool_set_true:N\l_ocgxii_vp_open_bool \tl_put_right:Nx#2{/P/AllOn/OCGs~\g_ocgxii_left_bracket_tl} \clist_map_inline:nn{#1}{\ocgxii_ocmd_expression_parser:nN{##1}#2} \tl_put_right:Nx#2{\g_ocgxii_right_bracket_tl} \bool_set_false:N\l_ocgxii_vp_open_bool } \cs_new_protected_nopar:Nn\ocgxii_vp_anyon:nN{ \bool_set_true:N\l_ocgxii_vp_open_bool \tl_gput_right:Nx#2{/P/AnyOn/OCGs~\g_ocgxii_left_bracket_tl} \clist_map_inline:nn{#1}{\ocgxii_ocmd_expression_parser:nN{##1}#2} \tl_gput_right:Nx#2{\g_ocgxii_right_bracket_tl} \bool_set_false:N\l_ocgxii_vp_open_bool } \cs_new_protected_nopar:Nn\ocgxii_vp_anyoff:nN{ \bool_set_true:N\l_ocgxii_vp_open_bool \tl_gput_right:Nx#2{/P/AnyOff/OCGs~\g_ocgxii_left_bracket_tl} \clist_map_inline:nn{#1}{\ocgxii_ocmd_expression_parser:nN{##1}#2} \tl_gput_right:Nx#2{\g_ocgxii_right_bracket_tl} \bool_set_false:N\l_ocgxii_vp_open_bool } \cs_new_protected_nopar:Nn\ocgxii_vp_alloff:nN{ \bool_set_true:N\l_ocgxii_vp_open_bool \tl_gput_right:Nx#2{/P/AllOff/OCGs~\g_ocgxii_left_bracket_tl} \clist_map_inline:nn{#1}{\ocgxii_ocmd_expression_parser:nN{##1}#2} \tl_gput_right:Nx#2{\g_ocgxii_right_bracket_tl} \bool_set_false:N\l_ocgxii_vp_open_bool } % policy to expression conversion \cs_new_protected_nopar:Nn\ocgxii_ocmd_make_equiv_ve:nN{ \int_zero:N\l_ocgxii_ve_cnt_int \tl_clear_new:N#2 \clist_map_inline:nn{#1}{\ocgxii_omcd_convert_vp:nN{##1}#2} } \cs_generate_variant:Nn\ocgxii_ocmd_make_equiv_ve:nN{xN} \cs_new_protected_nopar:Nn\ocgxii_omcd_convert_vp:nN{ \cs_set_eq:NN\AllOn \ocgxii_vp_check:n \cs_set_eq:NN\AnyOn \ocgxii_vp_check:n \cs_set_eq:NN\AnyOff\ocgxii_vp_check:n \cs_set_eq:NN\AllOff\ocgxii_vp_check:n \cs_set_eq:NN\Not\ocgxii_ve_check:n \cs_set_eq:NN\And\ocgxii_ve_check:n \cs_set_eq:NN\Or \ocgxii_ve_check:n \tl_if_exist:cT{ocgxii_#1}{ \str_case_e:nn{\tl_use:c{ocgxii_#1}}{ {VisExpr}{ \int_incr:N\l_ocgxii_ve_cnt_int \tl_set:Nn#2{#1} } {VisPol}{ \int_compare:nNnT{\l_ocgxii_ve_cnt_int}={0}{ \cs_set_eq:NN\AllOn \ocgxii_allon_to_ve:n \cs_set_eq:NN\AnyOn \ocgxii_anyon_to_ve:n \cs_set_eq:NN\AnyOff\ocgxii_anyoff_to_ve:n \cs_set_eq:NN\AllOff\ocgxii_alloff_to_ve:n \tl_set:No#2{#1} } } } } } \cs_new_protected_nopar:Nn\ocgxii_allon_to_ve:n{\And{#1}} \cs_new_protected_nopar:Nn\ocgxii_anyon_to_ve:n{\Or{#1}} \cs_new_protected_nopar:Nn\ocgxii_anyoff_to_ve:n{\Not{\And{#1}}} \cs_new_protected_nopar:Nn\ocgxii_alloff_to_ve:n{\Not{\Or{#1}}} % visib. boolean expressions \cs_new_protected_nopar:Nn\ocgxii_ve_and:nN{ \bool_set_true:N\l_ocgxii_ve_open_bool \tl_put_right:Nx#2{\g_ocgxii_left_bracket_tl/And} \clist_map_inline:nn{#1}{\ocgxii_ocmd_expression_parser:nN{##1}#2} \tl_put_right:Nx#2{\g_ocgxii_right_bracket_tl} \bool_set_false:N\l_ocgxii_ve_open_bool } \cs_new_protected_nopar:Nn\ocgxii_ve_or:nN{ \bool_set_true:N\l_ocgxii_ve_open_bool \tl_put_right:Nx#2{\g_ocgxii_left_bracket_tl/Or} \clist_map_inline:nn{#1}{\ocgxii_ocmd_expression_parser:nN{##1}#2} \tl_put_right:Nx#2{\g_ocgxii_right_bracket_tl} \bool_set_false:N\l_ocgxii_ve_open_bool } \cs_new_protected_nopar:Nn\ocgxii_ve_not:nN{ \bool_set_true:N\l_ocgxii_ve_open_bool % only one item allowed in \Not{...} argument \int_compare:nNnT{\clist_count:n{#1}}>{\c_one_int}{ \msg_error:nnnn{ocgx2}{generic~msg}{ More~than~one~item~passed~to~\Not{...}. }{ Only~one~item~is~allowed. } } \int_compare:nNnT{\clist_count:n{#1}}={\c_one_int}{ \tl_put_right:Nx#2{\g_ocgxii_left_bracket_tl/Not} \clist_map_inline:nn{#1}{\ocgxii_ocmd_expression_parser:nN{##1}#2} \tl_put_right:Nx#2{\g_ocgxii_right_bracket_tl} } \bool_set_false:N\l_ocgxii_ve_open_bool } \cs_new_protected_nopar:Nn\ocgxii_ve_check:n{VisExpr} \tl_set:cn{ocgxii_VisExpr}{VisExpr} \cs_new_protected_nopar:Nn\ocgxii_vp_check:n{VisPol} \tl_set:cn{ocgxii_VisPol}{VisPol} \tl_set:Nx\g_ocgxii_left_bracket_tl{\tl_to_str:N[} \tl_set:Nx\g_ocgxii_right_bracket_tl{\tl_to_str:N]} \int_new:N\l_ocgxii_vp_cnt_int %number of visib. policies \int_new:N\l_ocgxii_ve_cnt_int %number of visib. expressions \bool_new:N\l_ocgxii_vp_open_bool %for nesting test \bool_new:N\l_ocgxii_ve_open_bool %for nesting test \tl_set:Nn\g_ocgxii_help_msg_tl{ At~most~one~visibility~policy~and,~separated~by~a~comma,~at~most~one~ visibility~expression~may~be~passed~as~the~2nd~argument~to~the~`ocmd'~ environment.~A~visibility~ policy~is~defined~by~one~of~\AllOn{...},~\AnyOn{...},~\AnyOff{...},~ \AllOff{...}.~A~visibility~expression~is~a~boolean~expression~built~by~ nesting~any~number~of~\And{...},~\Or{...},~\Not{...}~commands.~If~both~are~ provided,~the~visibility~expression~takes~precedence~over~the~policy,~but~the~ latter~may~be~used~as~fallback~by~non-conforming~PDF~viewers. } %command that builds /OC entry from open layer stack \cs_new_protected_nopar:Nn\ocgxii_make_oc_entry:{ \group_begin: \tl_gclear:N\g_ocgxii_oc_entry_tl \tl_clear:N\l_tempa_tl \seq_if_empty:NF\g_ocgbase_open_stack_seq{ \seq_clear:N\l_tempa_seq %additional level of braces around indirect PDF objects (needed for dvips) \seq_map_inline:Nn\g_ocgbase_open_stack_seq{ \seq_put_right:Nn\l_tempa_seq{{##1}} } \ocgxii_omcd_parse_argument:xN{ \exp_not:N\And{\seq_use:Nn\l_tempa_seq{,}} }\l_tempa_tl \tl_gset:Nx\g_ocgxii_oc_entry_tl{/OC~<>} } \group_end: } %programmer/author command that inserts /OC << >> entry; for use in %annotation/xobject dicts, in order to make them layer-aware \cs_new_nopar:Nn\ocgxii_insert_oc:{\g_ocgxii_oc_entry_tl} \cs_gset_eq:NN\ocgbase_insert_oc:\ocgxii_insert_oc: \cs_gset_eq:NN\ocgbase@insert@oc\ocgxii_insert_oc: \tl_new:N\g_ocgxii_oc_entry_tl \cs_new_protected_nopar:Nn\ocgxii_stack_shipout:NN{ \iow_shipout_x:Nx\@mainaux{ \token_to_str:N#1{ \exp_not:N\int_use:N\g_ocgxii_page_int }{\seq_use:Nn#2{,}} } } \cs_new_protected_nopar:Npn\ocgxii@ocg@stack@on@page#1#2{ \seq_gset_from_clist:cn{g_pending_ocgs_on_#1_seq}{#2} %re-add braces around items for dvips \bool_if:nT{\sys_if_output_dvi_p: && !\g_ocgxii_dvipdfmx_bool}{ \seq_map_inline:cn{g_pending_ocgs_on_#1_seq}{ \seq_gpop_left:cN{g_pending_ocgs_on_#1_seq}\l_trash_tl \seq_gput_right:cn{g_pending_ocgs_on_#1_seq}{{##1}} } } } \ocgxii@ocg@stack@on@page{0}{} %initialize \cs_new_protected_nopar:Npn\ocgxii@lnkcol@stack@on@page#1#2{ \seq_gset_from_clist:cn{g_pending_lnkcols_on_#1_seq}{#2} } \ocgxii@lnkcol@stack@on@page{0}{} %initialize %end-of-page action \pbs_eop_action:n{ \seq_if_exist:cT{g_pending_lnkcols_on_\int_use:c{g_ocgxii_page_int}_seq}{ %check whether end-of-page link colour stack has settled \iow_shipout:Nx\@mainaux{ \token_to_str:N\ocgxii@newkey{ocgx2.oldlnkcol.\int_use:N\g_ocgxii_page_int}{ \seq_use:cn{g_pending_lnkcols_on_\int_use:c{g_ocgxii_page_int}_seq}{,} } } \bool_if:nT{ !\cs_if_exist:cTF{ocgx2.oldlnkcol.\int_use:N\g_ocgxii_page_int}{ \str_if_eq_p:ee{ \tl_use:c{ocgx2.oldlnkcol.\int_use:N\g_ocgxii_page_int} }{ \seq_use:cn{g_pending_lnkcols_on_\int_use:c{g_ocgxii_page_int}_seq}{,} } }{ \c_false_bool } }{ \tl_if_exist:NF\g_ocgxii_rerunwarned_tl{ \tl_new:N\g_ocgxii_rerunwarned_tl \AtEndDocument{\msg_warning:nn{ocgx2}{rerun}} } } % now close the colourlink opened last \seq_get:cNT{g_pending_lnkcols_on_\int_use:c{g_ocgxii_page_int}_seq}\l_tmpa_tl{ \tl_gset_eq:NN\g_ocgxii_lnkcol_tl\l_tmpa_tl \ocgxii_colourlink_end: } } %check whether end-of-page ocg stack has settled \iow_shipout:Nx\@mainaux{ \token_to_str:N\ocgxii@newkey{ocgx2.oldstack.\int_use:N\g_ocgxii_page_int}{ \seq_use:cn{g_pending_ocgs_on_\int_use:c{g_ocgxii_page_int}_seq}{,} } } \bool_if:nT{ !\cs_if_exist:cTF{ocgx2.oldstack.\int_use:N\g_ocgxii_page_int}{ \str_if_eq_p:ee{ \tl_use:c{ocgx2.oldstack.\int_use:N\g_ocgxii_page_int} }{ \seq_use:cn{g_pending_ocgs_on_\int_use:c{g_ocgxii_page_int}_seq}{,} } }{ \c_false_bool } }{ \tl_if_exist:NF\g_ocgxii_rerunwarned_tl{ \tl_new:N\g_ocgxii_rerunwarned_tl \AtEndDocument{\msg_warning:nn{ocgx2}{rerun}} } } %now close pending ocgs \seq_map_variable:cNn{ g_pending_ocgs_on_\int_use:c{g_ocgxii_page_int}_seq }\l_tmpb_tl{\ocgbase_oc_emc:} } %begin-of-page action \pbs_bop_action:n{ % re-open all pending ocgs in original order \seq_set_eq:Nc\l_ocgxii_pending_ocgs_seq{ g_pending_ocgs_on_\int_use:c{g_ocgxii_page_int}_seq } \seq_reverse:N\l_ocgxii_pending_ocgs_seq \seq_map_variable:NNn\l_ocgxii_pending_ocgs_seq\l_tmpa_tl{ \ocgbase_oc_bdc:n{\l_tmpa_tl} } % re-open the colourlink opened last \seq_get:cNT{g_pending_lnkcols_on_\int_use:c{g_ocgxii_page_int}_seq}\l_tmpa_tl{ \tl_gset_eq:NN\g_ocgxii_lnkcol_tl\l_tmpa_tl \ocgxii_colourlink_begin: } \int_gincr:N\g_ocgxii_page_int % copy pending ocg stack from previous page, if it has not been initialized % yet from aux file \seq_if_exist:cF{g_pending_ocgs_on_\int_use:c{g_ocgxii_page_int}_seq}{ \seq_gset_eq:cc{ g_pending_ocgs_on_\int_use:c{g_ocgxii_page_int}_seq }{ g_pending_ocgs_on_\int_eval:n{\g_ocgxii_page_int-\c_one_int}_seq } } %the same for link colour stack \seq_if_exist:cF{g_pending_lnkcols_on_\int_use:c{g_ocgxii_page_int}_seq}{ \seq_gset_eq:cc{ g_pending_lnkcols_on_\int_use:c{g_ocgxii_page_int}_seq }{ g_pending_lnkcols_on_\int_eval:n{\g_ocgxii_page_int-\c_one_int}_seq } } } \int_new:N\g_ocgxii_page_int %abs. page counter \cs_new_protected_nopar:Nn\ocgxii_ocglist_reset:{ \tl_clear_new:N\l_ocgxii_u_list_tl \tl_clear_new:N\l_ocgxii_d_list_tl \tl_clear_new:N\l_ocgxii_e_list_tl \tl_clear_new:N\l_ocgxii_x_list_tl } \cs_new_protected_nopar:Nn\ocgxii_ocglist_build:Nn{ \tl_set:Nx\l_ocglistarg_tl{#2}\tl_trim_spaces:N\l_ocglistarg_tl % first try splitting at commas (ocgx2 syntax) \tl_set_eq:NN\l_tmpa_tl\l_ocglistarg_tl \tl_remove_all:Nn\l_tmpa_tl{~} \seq_set_split:NnV\l_ocgxii_ocglistarg_seq{,}\l_tmpa_tl \seq_remove_all:Nn\l_ocgxii_ocglistarg_seq{} \tl_set:Nx\l_tmpa_tl{\seq_count:N\l_ocgxii_ocglistarg_seq} % now at spaces (legacy ocgx/ocg-p) \tl_set_eq:NN\l_tmpb_tl\l_ocglistarg_tl \tl_remove_all:Nn\l_tmpb_tl{,} \seq_set_split:NnV\l_tmpb_seq{~}\l_tmpb_tl \seq_remove_all:Nn\l_tmpb_seq{} \tl_set:Nx\l_tmpb_tl{\seq_count:N\l_tmpb_seq} %take the seq having more elements (guessing the separator most likely used) \int_compare:nT{\l_tmpb_tl>\l_tmpa_tl}{ \seq_set_eq:NN\l_ocgxii_ocglistarg_seq\l_tmpb_seq } \seq_map_variable:NNn\l_ocgxii_ocglistarg_seq\l_tempa_tl{ \ocgxii_process_ocgref:NN#1\l_tempa_tl } } \cs_new_protected_nopar:Nn\ocgxii_commalist_process:n{ \seq_set_split:Nnn\l_tmpa_seq{,}{#1} \ocgxii_ocglist_build:Nn\l_ocgxii_e_list_tl{\seq_item:Nn\l_tmpa_seq{1}} \ocgxii_ocglist_build:Nn\l_ocgxii_x_list_tl{\seq_item:Nn\l_tmpa_seq{2}} \ocgxii_ocglist_build:Nn\l_ocgxii_d_list_tl{\seq_item:Nn\l_tmpa_seq{3}} \ocgxii_ocglist_build:Nn\l_ocgxii_u_list_tl{\seq_item:Nn\l_tmpa_seq{4}} } \cs_new_protected_nopar:Nn\ocgxii_ocglist_process_idlist:nn{ \ocgxii_ocglist_reset: \tl_set:Nx\l_ocgxii_opt_tl{#1}\tl_remove_all:Nn\l_ocgxii_opt_tl{~} \str_case_e:nnF{\l_ocgxii_opt_tl}{ {onmouseup}{ \ocgxii_ocglist_build:Nn\l_ocgxii_u_list_tl{#2} } {onmousedown}{ \ocgxii_ocglist_build:Nn\l_ocgxii_d_list_tl{#2} } {onmouseenter}{ \ocgxii_ocglist_build:Nn\l_ocgxii_e_list_tl{#2} } {onmouseexit}{ \ocgxii_ocglist_build:Nn\l_ocgxii_x_list_tl{#2} } {onmouseall}{ \ocgxii_commalist_process:n{#2} } }{ \msg_error:nnx{ocgx2}{unknown~option}{\l_ocgxii_opt_tl} } } \int_new:N\g_ocgxii_widcount_int% widget counter %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % re-implement commands from ocgx.sty (all engines including ps2pdf [gs>=9.15]) % adding optional `*' (arg 1) -> non-breakable link instead of plain (multiline) % Link; % adding optional 2nd argument -> Button Widget (non-breakable) with one of % various mouse triggers (`troggerocgs` option from ocg-p) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \DeclareDocumentCommand\switchocg{s O{} m +m}{ \ocgxii_actionsocg:nnnnnn{#1}{#2}{#3}{}{}{#4} } \DeclareDocumentCommand\showocg{s O{} m +m}{ \ocgxii_actionsocg:nnnnnn{#1}{#2}{}{#3}{}{#4} } \DeclareDocumentCommand\hideocg{s O{} m +m}{ \ocgxii_actionsocg:nnnnnn{#1}{#2}{}{}{#3}{#4} } \DeclareDocumentCommand\actionsocg{s O{} m m m +m}{ \ocgxii_actionsocg:nnnnnn{#1}{#2}{#3}{#4}{#5}{#6} } \bool_new:N\l_ocgxii_mouse_triggers_bool \bool_new:N\l_ocgxii_nobreak_bool \cs_new_protected:Nn\ocgxii_actionsocg:nnnnnn{ \group_begin: \mode_leave_vertical: \bool_set_false:N\l_ocgxii_mouse_triggers_bool \bool_set_false:N\l_ocgxii_nobreak_bool % explicitly non-breakable? \bool_if:nT{#1}{\bool_set_true:N\l_ocgxii_nobreak_bool} % %clear actions for various mouse triggers (e,d,x) \tl_clear:N\l_ocgxii_toswitch_e_tl \tl_clear:N\l_ocgxii_toswitch_x_tl \tl_clear:N\l_ocgxii_toswitch_d_tl \tl_clear:N\l_ocgxii_toshow_e_tl \tl_clear:N\l_ocgxii_toshow_x_tl \tl_clear:N\l_ocgxii_toshow_d_tl \tl_clear:N\l_ocgxii_tohide_e_tl \tl_clear:N\l_ocgxii_tohide_x_tl \tl_clear:N\l_ocgxii_tohide_d_tl % %process *all* mouse triggers (e,d,u,x) \ocgxii_ocglist_process_idlist:nn{ \tl_if_blank:oTF{#2}{onmouseup}{#2} }{#3} \tl_set_eq:NN\l_ocgxii_toswitch_e_tl\l_ocgxii_e_list_tl \tl_set_eq:NN\l_ocgxii_toswitch_x_tl\l_ocgxii_x_list_tl \tl_set_eq:NN\l_ocgxii_toswitch_d_tl\l_ocgxii_d_list_tl \tl_set_eq:NN\l_ocgxii_toswitch_u_tl\l_ocgxii_u_list_tl \ocgxii_ocglist_process_idlist:nn{ \tl_if_blank:oTF{#2}{onmouseup}{#2} }{#4} \tl_set_eq:NN\l_ocgxii_toshow_e_tl\l_ocgxii_e_list_tl \tl_set_eq:NN\l_ocgxii_toshow_x_tl\l_ocgxii_x_list_tl \tl_set_eq:NN\l_ocgxii_toshow_d_tl\l_ocgxii_d_list_tl \tl_set_eq:NN\l_ocgxii_toshow_u_tl\l_ocgxii_u_list_tl \ocgxii_ocglist_process_idlist:nn{ \tl_if_blank:oTF{#2}{onmouseup}{#2} }{#5} \tl_set_eq:NN\l_ocgxii_tohide_e_tl\l_ocgxii_e_list_tl \tl_set_eq:NN\l_ocgxii_tohide_x_tl\l_ocgxii_x_list_tl \tl_set_eq:NN\l_ocgxii_tohide_d_tl\l_ocgxii_d_list_tl \tl_set_eq:NN\l_ocgxii_tohide_u_tl\l_ocgxii_u_list_tl %any triggers apart from mouse-up? \str_if_eq:eeF{ \l_ocgxii_toswitch_e_tl\l_ocgxii_toswitch_x_tl\l_ocgxii_toswitch_d_tl \l_ocgxii_toshow_e_tl\l_ocgxii_toshow_x_tl\l_ocgxii_toshow_d_tl \l_ocgxii_tohide_e_tl\l_ocgxii_tohide_x_tl\l_ocgxii_tohide_d_tl }{}{ \bool_set_true:N\l_ocgxii_mouse_triggers_bool } % \bool_if:nTF{\l_ocgxii_nobreak_bool || \l_ocgxii_mouse_triggers_bool}{ \hbox_set:Nn\l_tmpa_box{#6} \bool_if:NTF\l_ocgxii_mouse_triggers_bool{ % e,d,x mouse triggers require (non-breakable) /Widget annot \pdfannot_dict_put:nnn{link/GoTo}{Subtype}{/Widget} \pdfannot_dict_remove:nn{link/GoTo}{Border} }{ \pdfannot_dict_put:nnn{link/GoTo}{Border}{[0~0~0]} } \pdfannot_dict_remove:nn{link/GoTo}{C} \pbs_pdfannot:nnnn{ \dim_use:N\box_wd:N\l_tmpa_box}{ \dim_use:N\box_ht:N\l_tmpa_box}{ \dim_use:N\box_dp:N\l_tmpa_box }{ \pdfannot_dict_use:n{link/GoTo} \bool_if:NTF\l_ocgxii_mouse_triggers_bool{ /Ff~65536/FT/Btn/BS<> /T~(ocgx2@\int_use:N\g_ocgxii_widcount_int) %treat mouse-up as mouse-click --> handle through /A dictionary \str_if_eq:eeF{}{ \l_ocgxii_toswitch_u_tl\l_ocgxii_toshow_u_tl\l_ocgxii_tohide_u_tl }{ /A <> } % other mouse triggers need add. actions dict /AA << %\str_if_eq:eeF{}{ % mouse-up % \l_ocgxii_toswitch_u_tl\l_ocgxii_toshow_u_tl\l_ocgxii_tohide_u_tl %}{ % /U <> %} \str_if_eq:eeF{}{ % mouse-down \l_ocgxii_toswitch_d_tl\l_ocgxii_toshow_d_tl\l_ocgxii_tohide_d_tl }{ /D <> } \str_if_eq:eeF{}{ % mouse-enter \l_ocgxii_toswitch_e_tl\l_ocgxii_toshow_e_tl\l_ocgxii_tohide_e_tl }{ /E <> } \str_if_eq:eeF{}{ % mouse-exit \l_ocgxii_toswitch_x_tl\l_ocgxii_toshow_x_tl\l_ocgxii_tohide_x_tl }{ /X <> } >> }{ %mouse-up alone /A <> } }\box_use_drop:N\l_tmpa_box \bool_if:NT\l_ocgxii_mouse_triggers_bool{ \pbs_appendtofields:n{\pbs_pdflastann:} \int_gincr:N\g_ocgxii_widcount_int } }{ \bool_if:NF\g_ocgxii_hyperref_loaded_bool{ \pdfannot_dict_put:nnn{link/GoTo}{Border}{[0~0~0]} } %line-breakable annotation \pbs_pdflink:nn{ %look and feel of hyperref internal links \pdfannot_dict_use:n{link/GoTo} /A <> }{ \hook_use:n{pdfannot/link/GoTo/begin} #6 \hook_use:n{pdfannot/link/GoTo/end} } } \group_end: } %mimic commands from ocg-p \keys_define:nn{ocgx2/ocgpcmd}{ triggerocg .choices:nn = { onmouseenter, onmouseexit, onmousedown, onmouseup, allactions }{ \str_if_eq:eeTF{\l_keys_choice_tl}{allactions}{ \tl_set:Nn\l_ocgxii_trigger_tl{onmouseall} }{ \tl_set_eq:NN\l_ocgxii_trigger_tl\l_keys_choice_tl } } } \DeclareDocumentCommand\toggleocgs{O{} m +m}{ \tl_clear_new:N\l_ocgxii_trigger_tl \keys_set:nn{ocgx2/ocgpcmd}{#1} \switchocg*[\l_ocgxii_trigger_tl]{#2}{#3} } \DeclareDocumentCommand\showocgs{O{} m +m}{ \tl_clear_new:N\l_ocgxii_trigger_tl \keys_set:nn{ocgx2/ocgpcmd}{#1} \showocg*[\l_ocgxii_trigger_tl]{#2}{#3} } \DeclareDocumentCommand\hideocgs{O{} m +m}{ \tl_clear_new:N\l_ocgxii_trigger_tl \keys_set:nn{ocgx2/ocgpcmd}{#1} \hideocg*[\l_ocgxii_trigger_tl]{#2}{#3} } \DeclareDocumentCommand\setocgs{O{} m m m +m}{ \tl_clear_new:N\l_ocgxii_trigger_tl \keys_set:nn{ocgx2/ocgpcmd}{#1} \actionsocg*[\l_ocgxii_trigger_tl]{#2}{#3}{#4}{#5} } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \cs_new_protected:Nn\ocgxii_process_ocgref:NN{ \tl_if_exist:cTF{ocgx2.ocg.#2}{ \tl_put_right:Nx#1{~\tl_use:c{ocgx2.ocg.#2}} }{ \msg_warning:nnx{ocgx2}{undefined~OCG}{#2} \tl_if_exist:NF\g_ocgxii_refundefwarned_tl{ \tl_new:N\g_ocgxii_refundefwarned_tl \AtEndDocument{\msg_warning:nn{ocgx2}{undefined~OCGs}} } } } %ocg environment opts \keys_define:nn{ocgx2/ocgenv}{ viewocg .choice:, viewocg / always .code:n={ \tl_set:Nn\l_ocgxii_view_tl{/View<>}}, viewocg / never .code:n={ \tl_set:Nn\l_ocgxii_view_tl{/View<>}}, viewocg / ifvisible .code:n={ \tl_clear:N\l_ocgxii_view_tl}, viewocg .default:n={ifvisible}, printocg .choice:, printocg / always .code:n={ \tl_set:Nn\l_ocgxii_print_tl{/Print<>}}, printocg / never .code:n={ \tl_set:Nn\l_ocgxii_print_tl{/Print<>}}, printocg / ifvisible .code:n={ \tl_clear:N\l_ocgxii_print_tl}, printocg .default:n={ifvisible}, exportocg .choice:, exportocg / always .code:n={ \tl_set:Nn\l_ocgxii_export_tl{/Export<>}}, exportocg / never .code:n={ \tl_set:Nn\l_ocgxii_export_tl{/Export<>}}, exportocg / ifvisible .code:n={\tl_clear:N\l_ocgxii_export_tl}, exportocg .default:n={ifvisible}, showingui .choices:nn = {true,false,always,never,iffirstuse}{ \bool_if:nTF{ \str_if_eq_p:ee{#1}{false} || \str_if_eq_p:ee{#1}{never} }{ \bool_set_false:N\l_ocgxii_showingui_bool }{ \bool_set_true:N\l_ocgxii_showingui_bool } }, showingui .default:n={true}, listintoolbar .meta:n = {showingui=#1}, listintoolbar .default:n={true}, radiobtngrps .code:n = { \clist_map_inline:nn{#1}{ \seq_if_in:NxF\l_ocgxii_rbgrps_seq{##1}{ \seq_put_right:Nx\l_ocgxii_rbgrps_seq{##1} } } }, radiobtngrps .value_required:n = {true}, radiobtngrp .meta:n={radiobtngrps={#1}}, radiobtngrp .value_required:n = {true} } \cs_new_protected:Nn\ocgxii_reset_cmd_opts:{ \tl_set_eq:NN\l_ocgxii_view_tl\g_ocgxii_view_tl \tl_set_eq:NN\l_ocgxii_print_tl\g_ocgxii_print_tl \tl_set_eq:NN\l_ocgxii_export_tl\g_ocgxii_export_tl \bool_set_eq:NN\l_ocgxii_showingui_bool\g_ocgxii_showingui_bool %stack of radio button group names the current ocg belongs to \seq_clear_new:N\l_ocgxii_rbgrps_seq } \msg_set:nnn{ocgx2}{rerun}{Rerun~to~get~OCG~references~right!} \msg_set:nnn{ocgx2}{undefined~OCG}{ Line~\msg_line_number: :~OCG~`#1'~is~not~defined. } \msg_set:nnn{ocgx2}{undefined~OCGs}{There~were~undefined~OCGs!} \msg_set:nnn{ocgx2}{unknown~option}{ Line~\msg_line_number: :~unknown~option~`#1'. } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % allows `ocgcolorlinks' to extend over line-breaks AND page-breaks with % pdftex, luatex, xetex, dvipdfmx drivers % % based on Ben Lerner's idea % http://tex.stackexchange.com/a/104227; % with some improvements %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % we'll be using the downscaled space character (32) from the pzdr % (dingbats) font at the start and at the end of every ocgcolorlink \cs_new_protected_nopar:Npn\ocgxii_tiny_space_char:{ \group_begin: \font\l_tmpa_tl=pzdr~scaled~1\l_tmpa_tl\char32 \group_end: } \bool_new:N\g_ocgxii_hyperref_loaded_bool \AddToHook{package/hyperref/after}{ \bool_gset_true:N\g_ocgxii_hyperref_loaded_bool \bool_if:NT\l_ocgxii_ocgcolorlinks_bool{\hypersetup{ocgcolorlinks}} \bool_if:nTF{\sys_if_output_dvi_p: && !\g_ocgxii_dvipdfmx_bool}{ \prop_map_inline:Nn\c__hyp_map_hyp_annot_prop{ \hook_gremove_code:nn{pdfannot/link/#2/begin}{hyp/ocg} \hook_gput_code:nnn{pdfannot/link/#2/begin}{hyp/ocg}{ \bool_if:cT{l_hyp_annot_ocgcolor#1_bool}{ \ocgxii_colorlinks_init: \group_begin: \color_export:nnN{hyp/color/#1}{backend}\l_tempb_tl \tl_set:Nx\l_tempa_tl{\tl_item:Nn\l_tempb_tl{1}} \tl_set:Nx\l_tempb_tl{\tl_item:Nn\l_tempb_tl{2}} \str_replace_all:Nnn\l_tempb_tl{~}{,} \tl_set:Nx\l_ocgxii_lnkcol_tl{{\l_tempa_tl}{\l_tempb_tl}} \ocgxii_colourlink_nobreak_begin: } } \hook_gremove_code:nn{pdfannot/link/#2/end}{hyp/ocg} \hook_gput_code:nnn{pdfannot/link/#2/end}{hyp/ocg}{ \bool_if:cT{l_hyp_annot_ocgcolor#1_bool}{ \ocgxii_colourlink_nobreak_end: \group_end: } } } }{ \prop_map_inline:Nn\c__hyp_map_hyp_annot_prop{ \hook_gremove_code:nn{pdfannot/link/#2/begin}{hyp/ocg} \hook_gput_code:nnn{pdfannot/link/#2/begin}{hyp/ocg}{ \bool_if:cT{l_hyp_annot_ocgcolor#1_bool}{ \ocgxii_colorlinks_init: \seq_get_left:NNT\g_ocgxii_lnk_color_seq\l_tmpa_tl{ \tl_gset_eq:NN\g_ocgxii_lnkcol_tl\l_tmpa_tl \ocgxii_colourlink_end: } \group_begin: \ocgxii_colourlink_begin: %query current link colour, export it into backend format %and put it on the link colour stack \color_export:nnN{hyp/color/#1}{backend}\l_tempb_tl \tl_set:Nx\l_tempa_tl{\tl_item:Nn\l_tempb_tl{1}} % model \tl_set:Nx\l_tempb_tl{\tl_item:Nn\l_tempb_tl{2}} % components \str_replace_all:Nnn\l_tempb_tl{~}{,} \seq_gpush:Nx\g_ocgxii_lnk_color_seq{{\l_tempa_tl}{\l_tempb_tl}} \ocgxii_stack_shipout:NN\ocgxii@lnkcol@stack@on@page\g_ocgxii_lnk_color_seq } } \hook_gremove_code:nn{pdfannot/link/#2/end}{hyp/ocg} \hook_gput_code:nnn{pdfannot/link/#2/end}{hyp/ocg}{ \bool_if:cT{l_hyp_annot_ocgcolor#1_bool}{ \seq_gpop:NN\g_ocgxii_lnk_color_seq\l_tmpa_tl \tl_gset_eq:NN\g_ocgxii_lnkcol_tl\l_tmpa_tl \ocgxii_stack_shipout:NN\ocgxii@lnkcol@stack@on@page\g_ocgxii_lnk_color_seq \ocgxii_colourlink_end: \group_end: \seq_get_left:NNT\g_ocgxii_lnk_color_seq\l_tmpa_tl{ \tl_gset_eq:NN\g_ocgxii_lnkcol_tl\l_tmpa_tl \ocgxii_colourlink_begin: } } } } } }{} \seq_new:N\g_ocgxii_lnk_color_seq %stack of colours of currently open links \cs_new_protected_nopar:Nn\ocgxii_colourlink_begin:{ %tiny space char put here in order to neutralise possible transformation %matrix modifications from previous \pdfliteral{} (TikZ makes a lot use of it) \hbox_overlap_left:n{\ocgxii_tiny_space_char:} \pbs_literal:nn{page}{q~7~Tr} } \cs_new_protected_nopar:Nn\ocgxii_colourlink_end:{ %a tiny space char should keep empty link annots from flooding the page % with link colour %tiny space char put here in order to neutralise possible transformation \hbox_overlap_left:n{\ocgxii_tiny_space_char:} \ocgbase_oc_bdc:n{\tl_use:c{ocgxii_ocg_OCPrint}} \pbs_literal:nn{page}{-88888~-88888~99999~99999~re~f} \ocgbase_oc_emc: \ocgbase_oc_bdc:n{\tl_use:c{ocgxii_ocg_OCView}} \group_begin: \exp_after:wN\color_select:nn\g_ocgxii_lnkcol_tl \pbs_literal:nn{page}{-88888~-88888~99999~99999~re~f} \group_end: \ocgbase_oc_emc: \pbs_literal:nn{page}{0~Tr~Q} } \cs_new_protected_nopar:Nn\ocgxii_colourlink_nobreak_begin:{ \hbox_set:Nw\l_tmpa_box } \cs_new_protected_nopar:Nn\ocgxii_colourlink_nobreak_end:{ \hbox_set_end: \mbox{ \ocgbase_oc_bdc:n{\tl_use:c{ocgxii_ocg_OCPrint}} \hbox_overlap_right:n{\box_use:N\l_tmpa_box} \ocgbase_oc_emc: \ocgbase_oc_bdc:n{\tl_use:c{ocgxii_ocg_OCView}} \group_begin: \exp_after:wN\color_select:nn\l_ocgxii_lnkcol_tl \box_use_drop:N\l_tmpa_box \group_end: \ocgbase_oc_emc: } } % creates OCG objects for printing and viewing \cs_new_protected:Npn\ocgxii_colorlinks_init:{ \ocgbase_new_ocg:nnn{OCView}{ /Print<> /Export<> }{on} \tl_gset:cx{ocgxii_ocg_OCView}{\ocgbase_last_ocg:} %in case somebody wants to reopen with \begin{ocg}... \tl_gset:cx{ocgxii_ocg_OCView.opts}{ showingui=never,printocg=never,exportocg=never } \ocgbase_new_ocg:nnn{OCPrint}{ /Print<> /Export<> }{off} \tl_gset:cx{ocgxii_ocg_OCPrint}{\ocgbase_last_ocg:} \tl_gset:cx{ocgxii_ocg_OCPrint.opts}{ showingui=never,printocg=always,exportocg=always } \iow_now:Nx\@mainaux{ \token_to_str:N\ocgxii@newkey{ocgx2.ocg.OCView}{ \tl_use:c{ocgxii_ocg_OCView}} } \iow_now:Nx\@mainaux{ \token_to_str:N\ocgxii@newkey{ocgx2.ocg.OCPrint}{ \tl_use:c{ocgxii_ocg_OCPrint}} } \cs_gset:Npn\ocgxii_colorlinks_init:{} } % user command for protecting graphical content (external file, inline % [e. g. TikZ], \fbox{...}) inside breakable ocgcolorlink \DeclareDocumentCommand\ocglinkprotect{m}{ \seq_get_left:NNTF\g_ocgxii_lnk_color_seq\l_tmpa_tl{ \tl_gset_eq:NN\g_ocgxii_lnkcol_tl\l_tmpa_tl \ocgxii_colourlink_end: \group_begin: \tl_set_eq:NN\l_ocgxii_lnkcol_tl\l_tmpa_tl \ocgxii_colourlink_nobreak_begin: \cs_set_nopar:Npn\ocglinkprotect##1{##1} % in case of nesting \sys_if_output_pdf:TF{ \mode_leave_vertical: \hbox_set:Nn\l_tmpb_box{\skip_horizontal:n{1em}#1\skip_horizontal:n{1em}} \box_set_ht:Nn\l_tmpb_box{\box_ht:N\l_tmpb_box+1em} \box_set_dp:Nn\l_tmpb_box{\box_dp:N\l_tmpb_box+1em} \hbox_to_wd:nn{\box_wd:N\l_tmpb_box-2em}{ \vrule~ width~\c_zero_dim~ height~\dim_eval:n{\box_ht:N\l_tmpb_box-1em}~ depth~\dim_eval:n{\box_dp:N\l_tmpb_box -1em}~ \pbs_pdfxform:nnnnn{1}{0}{}{}{\l_tmpb_box} \skip_horizontal:n{-1em} \pbs_pdfrefxform:n{\pbs_pdflastxform:} \hss } }{#1} \ocgxii_colourlink_nobreak_end: \group_end: \ocgxii_colourlink_begin: }{#1} } \group_begin: \bool_if:NF\l_ocgxii_tikz_bool{\aftergroup\endinput} \group_end: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % TikZ related code follows (to be enabled with package option `tikz') \cs_set_eq:NN\ocgxii@trmspc\tl_trim_spaces:N \ExplSyntaxOff \RequirePackage{tikz} \usetikzlibrary{calc} % helper, replaces all spaces in #1 with `_' \def\ocgxii@cnvspc#1{\expandafter\ocgxii@@cnvspc#1 \@nil} \def\ocgxii@@cnvspc#1 #2\@nil{#1\ifx\@nil#2\@nil\else_\ocgxii@@cnvspc#2\@nil\fi} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Copyright notice: The code that follows until the end of the file was % taken in large parts from Paul Gaborit's `tikzlibraryocgx.code.tex' with % some additions/fixes: % % - opts={...} inside ocg={...} allows ocg-environment options to be passed % to a TikZ scope % % - The style ocmd={...} is another way for turning a TikZ scope into a PDF % layer (in addition to ocg={...}). It has two sub-keys, ref={...} and % visibility={...}, which have the same meaning as the optional and the % mandatory arguments of the `ocmd' environment % % - TikZ objects to be turned into OCG switching links accept the additional % key % % trigger ocg = onmousenter | onmouseexit | onmousedown | onmouseup | % onmouseall % % to react to various mouse gestures % % - switching links properly sized and working in scaled tikzpictures % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \tikzset{ ocg/.style={ocg/.cd,#1,/tikz/.cd}, ocg={ % parameters name/.store in=\ocgxii@name, opts/.store in=\ocgxii@opts, visibility/.store in=\ocgxii@visibility, status/.is choice, status/visible/.style={visibility=1}, status/invisible/.style={visibility=0}, status/true/.style={visibility=1}, status/false/.style={visibility=0}, status/on/.style={visibility=1}, status/off/.style={visibility=0}, status/1/.style={visibility=1}, status/0/.style={visibility=0}, % default values name=, opts=, % NEW status=on, % ref ref/.style={ /tikz/execute at begin scope={% \begin{ocg}[\ocgxii@opts]{% \ifx\empty\ocgxii@name\empty#1\else\ocgxii@name\fi% }{#1}{\ocgxii@visibility}}, /tikz/execute at end scope={\end{ocg}}, } }, ocmd/.style={ ocmd/.cd, #1, /tikz/execute at begin scope={% \begin{ocmd}[\ocgxii@ocmdref]{\ocgxii@ocmdvisibility}% }, /tikz/execute at end scope={\end{ocmd}}, /tikz/.cd }, ocmd={ % parameters ref/.store in=\ocgxii@ocmdref, visibility/.store in=\ocgxii@ocmdvisibility, % default values ref=, visibility=, }, trigger ocg/.store in=\ocgxii@trigger, trigger ocg/.value required, switch ocg/.style={ postaction={ path picture={ \path (path picture bounding box.south west) coordinate (p1) (path picture bounding box.north east) coordinate (p2) (p1) node[inner sep=0pt,anchor=south west,outer sep=0pt] {% \ifdefined\ocgxii@trigger% \switchocg*[\ocgxii@trigger]{#1}{% \tikz \useasboundingbox (p1) rectangle (p2);}% \else% \switchocg*{#1}{\tikz \useasboundingbox (p1) rectangle (p2);}% \fi% }; } } }, switch ocg with mark on/.style 2 args={ postaction={ path picture={% \edef\ocgxii@argone{#1}%ocg ref for checkmark \ocgxii@trmspc\ocgxii@argone% \global\let\ocgxii@argone\ocgxii@argone% %default ocg ref for checkmark, if nothing provided in #1 \xdef\ocgxii@argtwo{#2.mark}% \xdef\ocgxii@argtwo{\ocgxii@cnvspc{\ocgxii@argtwo}}% \begin{ocg}[showingui=false]{% \ifx\ocgxii@argone\@empty\ocgxii@argtwo\else\ocgxii@argone\fi% }{% \ifx\ocgxii@argone\@empty\ocgxii@argtwo\else\ocgxii@argone\fi% }{on}% \draw (path picture bounding box.south west) -- (path picture bounding box.north east) (path picture bounding box.south east) -- (path picture bounding box.north west) ; \end{ocg}% }, switch ocg={% \ifx\ocgxii@argone\@empty\ocgxii@argtwo\else{\ocgxii@argone}\fi\space #2% } } }, switch ocg with mark off/.style 2 args={ postaction={ path picture={% \edef\ocgxii@argone{#1}%ocg ref for checkmark \ocgxii@trmspc\ocgxii@argone% \global\let\ocgxii@argone\ocgxii@argone% %default ocg ref for checkmark, if nothing provided in #1 \xdef\ocgxii@argtwo{#2.mark}% \xdef\ocgxii@argtwo{\ocgxii@cnvspc{\ocgxii@argtwo}}% \begin{ocg}[showingui=false]{% \ifx\ocgxii@argone\@empty\ocgxii@argtwo\else\ocgxii@argone\fi% }{% \ifx\ocgxii@argone\@empty\ocgxii@argtwo\else\ocgxii@argone\fi% }{off}% \draw (path picture bounding box.south west) -- (path picture bounding box.north east) (path picture bounding box.south east) -- (path picture bounding box.north west) ; \end{ocg}% }, switch ocg={% \ifx\ocgxii@argone\@empty\ocgxii@argtwo\else{\ocgxii@argone}\fi\space #2% } } }, show ocg/.style={ postaction={ path picture={ \path (path picture bounding box.south west) coordinate (p1) (path picture bounding box.north east) coordinate (p2) (p1) node[inner sep=0pt,anchor=south west,outer sep=0pt] {% \ifdefined\ocgxii@trigger% \showocg*[\ocgxii@trigger]{#1}{% \tikz \useasboundingbox (p1) rectangle (p2);}% \else% \showocg*{#1}{\tikz \useasboundingbox (p1) rectangle (p2);}% \fi% }; } } }, hide ocg/.style={ postaction={ path picture={ \path (path picture bounding box.south west) coordinate (p1) (path picture bounding box.north east) coordinate (p2) (p1) node[inner sep=0pt,anchor=south west,outer sep=0pt] {% \ifdefined\ocgxii@trigger% \hideocg*[\ocgxii@trigger]{#1}{% \tikz \useasboundingbox (p1) rectangle (p2);}% \else% \hideocg*{#1}{\tikz \useasboundingbox (p1) rectangle (p2);}% \fi% }; } } }, actions ocg/.style n args={3}{ postaction={ path picture={ \path (path picture bounding box.south west) coordinate (p1) (path picture bounding box.north east) coordinate (p2) (p1) node[inner sep=0pt,anchor=south west,outer sep=0pt] {% \ifdefined\ocgxii@trigger% \actionsocg*[\ocgxii@trigger]{#1}{#2}{#3}{% \tikz \useasboundingbox (p1) rectangle (p2);}% \else% \actionsocg*{#1}{#2}{#3}{% \tikz \useasboundingbox (p1) rectangle (p2);}% \fi% }; } } } }