\ProvidesExplPackage{magicwatermark}{2024/06/11}{1.2B}{magic watermark, author} \msg_new:nnn {magicwatermark}{ Unable to parse } { Unable~to~parse~this~list~parameter.^^J #1 } \msg_new:nnn {magicwatermark}{ Enable TikZ } { tikz~option~has~been~disabled,^^J style~will~be~ignored. } \msg_new:nnn {magicwatermark}{ Load TikZ } { Please~load~tikz~package~before~magicwatermark. } \seq_new:N \l__mw_tmpa_seq \seq_new:N \l__mw_tmpb_seq \seq_new:N \l__mw_tmpc_seq \seq_new:N \l__mw_tmpd_seq \int_new:N \l__mw_tmpa_int \int_new:N \l__mw_tmpb_int \int_new:N \l__mw_tmpc_int \int_new:N \l__mw_tmpd_int \int_new:N \l__mw_tmpe_int \clist_new:N \l__mw_list_arabic_number_clist \clist_new:N \l__mw_list_roman_number_clist \clist_new:N \l__mw_list_range_clist \clist_new:N \l__mw_list_expression_clist \clist_new:N \l__mw_tmpa_clist \clist_new:N \l__mw_tmpb_clist \clist_new:N \l__mw_tmpc_clist \tl_new:N \l__mw_tmpa_tl \tl_new:N \l__mw_tmpb_tl \tl_new:N \l__mw_tmpc_tl \tl_new:N \g__mw_list_tl \tl_new:N \g__mw_style_tl \tl_new:N \g__mw_content_tl \bool_new:N \g__mw_is_append_bool \bool_set_false:N \g__mw_is_append_bool \bool_new:N \g__mw_tikz_bool \bool_set_true:N \g__mw_tikz_bool \regex_const:Nn \c__mw_arabic_numbers_regex { ^\d+$ } \regex_const:Nn \c__mw_roman_numbers_regex { ^[ivxlcdm]+$ } \int_new:N \g__mw_last_page_int \AtBeginDocument{ \int_compare:nTF { \@abspage@last = \number\maxdimen } { \int_gset:Nn \g__mw_last_page_int { 10 } } { \int_gset:Nn \g__mw_last_page_int { \@abspage@last } } } \cs_new_protected:Npn \__mw_clist_deduplicate:N #1 { \group_begin: \clist_clear:N \l_tmpa_clist \clist_map_inline:Nn #1 { \clist_if_in:NnF \l_tmpa_clist { ##1 } { \clist_put_right:Nn \l_tmpa_clist { ##1 } } } \clist_gset_eq:NN #1 \l_tmpa_clist \group_end: } \cs_new:Npn \__mw_list_parser_aux:n #1 { \clist_clear:N \l__mw_list_arabic_number_clist \clist_clear:N \l__mw_list_roman_number_clist \clist_clear:N \l__mw_list_range_clist \clist_clear:N \l__mw_list_expression_clist % split by commas \group_begin: \seq_set_split:Nnn \l__mw_tmpa_seq { , } { #1 } \seq_map_inline:Nn \l__mw_tmpa_seq { \tl_if_eq:nnTF { ##1 } { odd } % odd, push 2X + 1 { \clist_gput_right:Nn \l__mw_list_expression_clist { 2X + 1 } } { \tl_if_eq:nnTF { ##1 } { even } % even, push 2X { \clist_gput_right:Nn \l__mw_list_expression_clist { 2X } } { \tl_if_in:nnTF { ##1 } { X } % expression, push ##1 { \clist_gput_right:Nn \l__mw_list_expression_clist { ##1 } } { \tl_if_in:nnTF { ##1 } { - } % range, push ##1 { \clist_gput_right:Nn \l__mw_list_range_clist { ##1 } } { \__mw_if_arabic_number:nTF { ##1 } { \clist_gput_right:Nn \l__mw_list_arabic_number_clist { ##1 } } { \__mw_if_roman_number:nTF { ##1 } { \clist_gput_right:Nn \l__mw_list_roman_number_clist { ##1 } } { % error \msg_error:nnn {magicwatermark}{ Unable to parse } { Error~list~parameter~``##1''. } } } } } } } } \group_end: } \prg_new_conditional:Npnn \__mw_if_arabic_number:n #1 { p, T, F, TF } { \regex_match:NnTF \c__mw_arabic_numbers_regex { #1 } { \prg_return_true: } { \prg_return_false: } } \prg_new_conditional:Npnn \__mw_if_roman_number:n #1 { p, T, F, TF } { \regex_match:NnTF \c__mw_roman_numbers_regex { #1 } { \prg_return_true: } { \prg_return_false: } } \prg_generate_conditional_variant:Nnn \__mw_if_arabic_number:n { x } {p, T, F, TF} \prg_generate_conditional_variant:Nnn \__mw_if_roman_number:n { x } {p, T, F, TF} \cs_new:Npn \__mw_list_parser_for_arabic_number:n #1 { % arabic number \fp_compare:nTF { #1 <= \g__mw_last_page_int && #1 > 0 } { \clist_put_right:Nn \l__mw_tmpa_clist { #1 } } { % error \msg_error:nnn {magicwatermark}{ Unable to parse } { Error~list~parameter~``#1''.^^J Error~number~``#1''. } } } \cs_new:Npn \__mw_list_parser_for_roman_number:n #1 { % roman number \int_set:Nn \l__mw_tmpa_int { \g__mw_last_page_int - \int_from_roman:n { #1 } + 1 } \fp_compare:nTF { \l__mw_tmpa_int <= \g__mw_last_page_int && \l__mw_tmpa_int > 0 } { \clist_put_right:NV \l__mw_tmpa_clist \l__mw_tmpa_int } { % error \msg_error:nnn {magicwatermark}{ Unable to parse } { Error~list~parameter~``#1''.^^J Error~roman~number~``#1''. } } } \cs_new:Npn \__mw_list_parser_for_range:n #1 { % range, 1-5, 3-i \seq_set_split:Nnn \l__mw_tmpb_seq { - } { #1 } \__mw_if_arabic_number:xTF { \seq_item:Nn \l__mw_tmpb_seq { 1 } } { \int_set:Nn \l__mw_tmpb_int { \seq_item:Nn \l__mw_tmpb_seq { 1 } } } { \__mw_if_roman_number:xTF { \seq_item:Nn \l__mw_tmpb_seq { 1 } } { \exp_args:NNx \int_set:Nn \l__mw_tmpb_int { \g__mw_last_page_int - \exp_not:N \int_from_roman:n { \seq_item:Nn \l__mw_tmpb_seq { 1 } } + 1 } } { \int_set:Nn \l__mw_tmpb_int { -1 } % error \msg_error:nnn {magicwatermark}{ Unable to parse } { Error~list~parameter~``#1''.^^J Error~range~left~``#1''. } } } \__mw_if_arabic_number:xTF { \seq_item:Nn \l__mw_tmpb_seq { 2 } } { \int_set:Nn \l__mw_tmpc_int { \seq_item:Nn \l__mw_tmpb_seq { 2 } } } { \__mw_if_roman_number:xTF { \seq_item:Nn \l__mw_tmpb_seq { 2 } } { \exp_args:NNx \int_set:Nn \l__mw_tmpc_int { \g__mw_last_page_int - \exp_not:N \int_from_roman:n { \seq_item:Nn \l__mw_tmpb_seq { 2 } } + 1 } } { \int_set:Nn \l__mw_tmpc_int { -1 } % error \msg_error:nnn {magicwatermark}{ Unable to parse } { Error~list~parameter~``#1''.^^J Error~range~right~``#1''. } } } \fp_compare:nT { \l__mw_tmpb_int > 0 && \l__mw_tmpc_int > 0} { \int_step_inline:nnn { \int_min:nn { \l__mw_tmpb_int } { \l__mw_tmpc_int } } { \int_max:nn { \l__mw_tmpb_int } { \l__mw_tmpc_int } } { \clist_put_right:Nn \l__mw_tmpa_clist { ##1 } } } } \cs_new:Npn \__mw_list_parser_for_expression:n #1 { \int_zero:N \l__mw_tmpd_int \int_zero:N \l__mw_tmpe_int \group_begin: \tl_set:Nn \l__mw_tmpa_tl { #1 } \regex_replace_all:nnN { (\d+)X } { \1*\c{l__mw_tmpe_int} } \l__mw_tmpa_tl \tl_replace_all:Nnn \l__mw_tmpa_tl { X } { \l__mw_tmpe_int } % \tl_show:N \l__mw_tmpa_tl \int_while_do:nn { \l__mw_tmpd_int < \g__mw_last_page_int } { \int_set:Nn \l__mw_tmpd_int { \fp_eval:n { \l__mw_tmpa_tl } } \clist_gput_right:NV \l__mw_tmpa_clist \l__mw_tmpd_int \int_incr:N \l__mw_tmpe_int } \group_end: } \cs_new_nopar:Npn \__mw_list_parser:nN #1#2 { % case 1 -> odd, even % case 2 -> number, 1 % case 3 -> roman, i % case 4 -> range, 1-5, 3-ii % case 5 -> expression, 3X + 1 \__mw_list_parser_aux:n { #1 } \clist_clear:N \l__mw_tmpa_clist \clist_map_inline:Nn \l__mw_list_arabic_number_clist { \__mw_list_parser_for_arabic_number:n { ##1 } } \clist_map_inline:Nn \l__mw_list_roman_number_clist { \__mw_list_parser_for_roman_number:n { ##1 } } \clist_map_inline:Nn \l__mw_list_range_clist { \__mw_list_parser_for_range:n { ##1 } } \clist_map_inline:Nn \l__mw_list_expression_clist { \__mw_list_parser_for_expression:n { ##1 } } \clist_sort:Nn \l__mw_tmpa_clist { \int_compare:nNnTF { ##1 } > { ##2 } { \sort_return_swapped: } { \sort_return_same: } } \__mw_clist_deduplicate:N \l__mw_tmpa_clist \clist_set_eq:NN #2 \l__mw_tmpa_clist } \cs_new:Npn \__mw_parser:nN #1#2 { \clist_clear:N #2 \tl_if_eq:nnTF { #1 } { * } { \int_step_inline:nnn { 1 } { \g__mw_last_page_int } { \clist_put_right:Nn #2 { ##1 } } } { \__mw_list_parser:nN { #1 } #2 } } \keys_define:nn { mw / new } { pages .tl_set:N = \g__mw_list_tl, style .tl_set:N = \g__mw_style_tl, % style .code:n = \__mw_parse_style:n { #1 }, content .tl_set:N = \g__mw_content_tl, is~append .bool_set:N = \g__mw_is_append_bool, tikz .bool_set:N = \g__mw_tikz_bool } \cs_new:Npn \__mw_parse_style: { \tl_if_empty:NF \g__mw_style_tl { \bool_if:NTF \g__mw_tikz_bool { \IfPackageLoadedTF{tikz} { \tl_set_eq:NN \l__mw_tmpc_tl \g__mw_style_tl } { \msg_error:nn {magicwatermark}{ Load TikZ } } } { \msg_warning:nn {magicwatermark}{ Enable TikZ } } } } \keys_define:nn { mw } { setup .code:n = \__mw_new_watermark:n {#1} } \cs_new:Npn \__mw_new_watermark:n #1 { \group_begin: \keys_set:nn { mw / new }{#1} % parse style \__mw_parse_style: % parse pages \exp_args:NV \__mw_parser:nN \g__mw_list_tl \l__mw_tmpc_clist \clist_map_inline:Nn \l__mw_tmpc_clist { \bool_if:NTF \g__mw_tikz_bool { \IfPackageLoadedTF{tikz} { \tl_set:Ne \l__mw_tmpb_tl { \exp_not:N \tikz[remember~picture, overlay] { \exp_not:N \node[inner~sep = 0pt, outer~sep = 0pt, opacity = .5, align = left, \exp_not:V \g__mw_style_tl] at (current~page.center) { \exp_not:V \g__mw_content_tl }; } } } { \msg_error:nn {magicwatermark}{ Load TikZ } } } { \tl_set:NV \l__mw_tmpb_tl \g__mw_content_tl } \bool_if:nTF { \g__mw_is_append_bool && \tl_if_exist_p:c { g__mw_content_ \int_to_roman:n { ##1 } _tl } } { \tl_gput_right:cV { g__mw_content_ \int_to_roman:n { ##1 } _tl } \l__mw_tmpb_tl } { \tl_gset:cV { g__mw_content_ \int_to_roman:n { ##1 } _tl } \l__mw_tmpb_tl } } \group_end: } \NewDocumentCommand{\MagicWatermark}{m}{ \group_begin: \AtBeginDocument{ \keys_set:nn { mw } { #1 } \AddToHook{shipout/background}{ \put(.5\paperwidth,-.5\paperheight){ \tl_if_exist:cT { g__mw_content_ \int_to_roman:n { \value{page} }_tl } { \tl_use:c { g__mw_content_ \int_to_roman:n { \value{page} } _tl } } } } } \group_end: } \@onlypreamble\MagicWatermark