\NeedsTeXFormat{LaTeX2e}[2022-11-01] \ProvidesExplPackage {paracolrule} {2026-08-22} {0.1.0} {Decorative separator rules for paracol} \RequirePackage{paracol} \RequirePackage{etoolbox} \RequirePackage{l3keys2e} \bool_new:N \g__paracolrule_tikz_bool \@ifpackageloaded{tikz} { \bool_gset_true:N \g__paracolrule_tikz_bool } { } \keys_define:nn { paracolrule/options } { tikz .bool_gset:N = \g__paracolrule_tikz_bool, tikz .default:n = true, } \ProcessKeysOptions{paracolrule/options} \bool_if:NT \g__paracolrule_tikz_bool { \ExplSyntaxOff \RequirePackage{tikz} \ExplSyntaxOn } \dim_new:N \l__paracolrule_dash_length_dim \dim_new:N \l__paracolrule_gap_length_dim \dim_new:N \l__paracolrule_pattern_gap_dim \dim_new:N \l__paracolrule_extend_top_dim \dim_new:N \l__paracolrule_extend_bot_dim \dim_new:N \l__paracolrule_adjusted_height_dim \int_new:N \l__paracolrule_mark_count_int \int_new:N \l__paracolrule_max_mark_count_int \int_new:N \l__paracolrule_gap_total_sp_int \int_new:N \l__paracolrule_gap_sp_int \int_new:N \l__paracolrule_gap_remainder_int \tl_new:N \l__paracolrule_tikz_code_tl \dim_set:Nn \l__paracolrule_dash_length_dim { 4pt } \dim_set:Nn \l__paracolrule_gap_length_dim { 2pt } \msg_new:nnn { paracolrule } { patch-failed } { The~installed~paracol~version~does~not~have~the~expected~rule-building~ internals.\\ paracolrule~has~restored~the~original~macros~and~will~stop~instead~of~ drawing~partial~or~misplaced~rules. } \msg_new:nnn { paracolrule } { renderer-undefined } { The~renderer~control~sequence~'#1'~is~undefined. } \msg_new:nnn { paracolrule } { tikz-required } { The~'custom-line'~setting~requires~TikZ.\\ Load~paracolrule~with~the~'tikz'~option,~or~load~TikZ~first. } \cs_new_protected:Npn \__paracolrule_solid:n #1 { \hrule height #1 width \columnseprule } \cs_new_protected:Npn \__paracolrule_tiled:nnn #1#2#3 { \dim_compare:nNnTF {#2} > { 0pt } { \dim_compare:nNnTF {#1} < { 2 #2 } { \hrule height #1 width \columnseprule } { \dim_set:Nn \l__paracolrule_pattern_gap_dim {#3} \dim_compare:nNnT { \l__paracolrule_pattern_gap_dim } < { 0pt } { \dim_zero:N \l__paracolrule_pattern_gap_dim } \int_set:Nn \l__paracolrule_mark_count_int { \int_div_round:nn { \dim_to_decimal_in_sp:n { #1 + \l__paracolrule_pattern_gap_dim } } { \dim_to_decimal_in_sp:n { #2 + \l__paracolrule_pattern_gap_dim } } } \int_set:Nn \l__paracolrule_max_mark_count_int { \int_div_truncate:nn { \dim_to_decimal_in_sp:n {#1} } { \dim_to_decimal_in_sp:n {#2} } } \int_set:Nn \l__paracolrule_mark_count_int { \int_max:nn { 2 } { \int_min:nn { \l__paracolrule_mark_count_int } { \l__paracolrule_max_mark_count_int } } } \int_set:Nn \l__paracolrule_gap_total_sp_int { \dim_to_decimal_in_sp:n {#1} - \l__paracolrule_mark_count_int * \dim_to_decimal_in_sp:n {#2} } \int_set:Nn \l__paracolrule_gap_sp_int { \int_div_truncate:nn { \l__paracolrule_gap_total_sp_int } { \l__paracolrule_mark_count_int - 1 } } \int_set:Nn \l__paracolrule_gap_remainder_int { \int_mod:nn { \l__paracolrule_gap_total_sp_int } { \l__paracolrule_mark_count_int - 1 } } \vbox to #1 { \offinterlineskip \hrule height #2 width \columnseprule \int_step_inline:nn { \l__paracolrule_mark_count_int - 1 } { \kern \int_use:N \l__paracolrule_gap_sp_int sp \int_compare:nNnF {##1} > { \l__paracolrule_gap_remainder_int } { \kern 1sp } \hrule height #2 width \columnseprule } } } } { \hrule height #1 width \columnseprule } } \cs_new_protected:Npn \__paracolrule_dashed:n #1 { \__paracolrule_tiled:nnn {#1} { \l__paracolrule_dash_length_dim } { \l__paracolrule_gap_length_dim } } \cs_new_protected:Npn \__paracolrule_dotted:n #1 { \__paracolrule_tiled:nnn {#1} { \columnseprule } { \l__paracolrule_gap_length_dim } } \cs_new_protected:Npn \__paracolrule_double:n #1 { \hbox to \columnseprule { \vrule width .3\columnseprule height #1 depth 0pt \hfil \vrule width .3\columnseprule height #1 depth 0pt } } \bool_if:NTF \g__paracolrule_tikz_bool { \cs_new_protected:Npn \__paracolrule_tikz:n #1 { \group_begin: \pcol@colseprulecolor \hbox to \columnseprule { \hss \raisebox{0pt}[#1][0pt] { \begin{tikzpicture} [ x=1pt, y=1pt, inner~sep=0pt, outer~sep=0pt, baseline={(current~bounding~box.south)} ] \node (TOP) at (0,#1) {}; \node (BOT) at (0,0) {}; \tl_use:N \l__paracolrule_tikz_code_tl \end{tikzpicture} } \hss } \group_end: } } { \cs_new_protected:Npn \__paracolrule_tikz:n #1 { \msg_error:nn { paracolrule } { tikz-required } \__paracolrule_solid:n {#1} } } \cs_new_eq:NN \l__paracolrule_renderer_cs \__paracolrule_solid:n \keys_define:nn { paracolrule } { style .choice:, style / solid .code:n = { \cs_set_eq:NN \l__paracolrule_renderer_cs \__paracolrule_solid:n }, style / dashed .code:n = { \cs_set_eq:NN \l__paracolrule_renderer_cs \__paracolrule_dashed:n }, style / dotted .code:n = { \cs_set_eq:NN \l__paracolrule_renderer_cs \__paracolrule_dotted:n }, style / double .code:n = { \cs_set_eq:NN \l__paracolrule_renderer_cs \__paracolrule_double:n }, style .initial:n = solid, dash-length .dim_set:N = \l__paracolrule_dash_length_dim, gap-length .dim_set:N = \l__paracolrule_gap_length_dim, extend-top .dim_set:N = \l__paracolrule_extend_top_dim, extend-bot .dim_set:N = \l__paracolrule_extend_bot_dim, expand .code:n = { \dim_set:Nn \l__paracolrule_extend_top_dim {#1} \dim_set:Nn \l__paracolrule_extend_bot_dim {#1} }, shift .code:n = { \dim_set:Nn \l__paracolrule_extend_bot_dim {#1} \dim_set:Nn \l__paracolrule_extend_top_dim { -\l__paracolrule_extend_bot_dim } }, custom-line .code:n = { \tl_set:Nn \l__paracolrule_tikz_code_tl {#1} \cs_set_eq:NN \l__paracolrule_renderer_cs \__paracolrule_tikz:n }, } \NewDocumentCommand \ParacolRuleSetup { m } { \keys_set:nn { paracolrule } {#1} } \NewDocumentCommand \ParacolRuleSetRenderer { m } { \cs_if_exist:NTF #1 { \cs_set_eq:NN \l__paracolrule_renderer_cs #1 } { \msg_error:nnx { paracolrule } { renderer-undefined } { \token_to_str:N #1 } } } \cs_new_protected:Npn \__paracolrule_segment:n #1 { \l__paracolrule_renderer_cs {#1} } \cs_new_eq:NN \paracolrule@segment \__paracolrule_segment:n \cs_new_protected:Npn \__paracolrule_paracol_segment:n #1 { \dim_set:Nn \l__paracolrule_adjusted_height_dim { #1 + \l__paracolrule_extend_top_dim + \l__paracolrule_extend_bot_dim } \vbox to #1 { \offinterlineskip \dim_compare:nNnTF { \l__paracolrule_adjusted_height_dim } > { 0pt } { \kern -\l__paracolrule_extend_top_dim \__paracolrule_segment:n { \l__paracolrule_adjusted_height_dim } \kern -\l__paracolrule_extend_bot_dim } { \vfil } } } \cs_new_eq:NN \paracolrule@paracolsegment \__paracolrule_paracol_segment:n \cs_new_eq:NN \__paracolrule_original_buildcolseprule: \pcol@buildcolseprule \cs_new_eq:NN \__paracolrule_original_buildcselt: \pcol@buildcselt \bool_new:N \l__paracolrule_patch_build_bool \bool_new:N \l__paracolrule_patch_element_bool \cs_new_protected:Npn \paracolrule@patchbuildsuccess { \bool_set_true:N \l__paracolrule_patch_build_bool } \cs_new_protected:Npn \paracolrule@patchelementsuccess { \bool_set_true:N \l__paracolrule_patch_element_bool } \ExplSyntaxOff \patchcmd{\pcol@buildcolseprule} {\hrule\@height\@tempdima\@width\columnseprule} {\paracolrule@paracolsegment{\@tempdima}} {\paracolrule@patchbuildsuccess} { } \patchcmd{\pcol@buildcselt} {\hrule\@height\@tempdimc\@width\columnseprule} {\paracolrule@paracolsegment{\@tempdimc}} {\paracolrule@patchelementsuccess} { } \ExplSyntaxOn \bool_if:nF { \l__paracolrule_patch_build_bool && \l__paracolrule_patch_element_bool } { \cs_set_eq:NN \pcol@buildcolseprule \__paracolrule_original_buildcolseprule: \cs_set_eq:NN \pcol@buildcselt \__paracolrule_original_buildcselt: \msg_fatal:nn { paracolrule } { patch-failed } } \endinput