%%% License %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % This package is licensed under the terms of the MIT License. % Copyright (c) 2025-2026 Kosei Kawaguchi % Permission is hereby granted, free of charge, % to any person obtaining a copy of this software and associated documentation files % (the "Software"), to deal in the Software without restriction, % including without limitation the rights to use, copy, modify, merge, publish, % distribute, sublicense, and/or sell copies of the Software, % and to permit persons to whom the Software is furnished to do so, % subject to the following conditions: % The above copyright notice and this permission notice % shall be included in all copies or substantial portions of the Software. % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, % EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. % IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, % DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, % ARISING FROM, % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \NeedsTeXFormat{LaTeX2e} \ProvidesExplPackage {modernruler} {2026/05/19}{Version 2.2.0}{ `modernruler` provides enhanced ruler commands} %%% parameters \cs_if_exist:NF \zw { \dim_new:N \zw \dim_set:Nn \zw { 1em } } \cs_if_exist:NF \ltjgetparameter { \cs_new:Npn \ltjgetparameter #1 { 4 } } %%% %%% basical settings \RequirePackage{kvoptions, pgfkeys} \RequirePackage{varwidth, zref-savepos} \RequirePackage[most]{tcolorbox} %%% %%% keyval package options for \undernote \SetupKeyvalOptions{% family=undernote,% prefix=undernote@% } \DeclareStringOption[\footnotesize]{notesize} \DeclareStringOption[3mm]{notepos} \DeclareStringOption[4mm]{noteshift} \DeclareStringOption[.07\zw]{noterulethickness} \DeclareStringOption[1.5mm]{noterulehshift} \DeclareStringOption[1.5mm]{noterulehsize} \DeclareStringOption[0em]{notesep} \DeclareStringOption[5em]{noteoverhang} \DeclareStringOption[0]{noteparstyle} \DeclareStringOption[black]{notelinecolor} \ProcessKeyvalOptions* % kvoptions は \undernote@xxx を生成するので、内部名 \xxx@internal@undernote % へ橋渡し。pgfkeys の .store~in も後者へ書くので、ここで名前空間を統一する。 % % 各キーの意味: % notesize 注釈部分の文字サイズ % notepos 注釈へ伸ばす縦線の長さの最小値 % noteshift 衝突回避で下にずらす1段階分の移動量 % noterulethickness 注釈線の太さ % noterulehshift 下線左端から縦線までの右ずれ % noterulehsize 縦線の下から伸ばす横線の長さ % notesep 次の注釈との最低横間隔(これ未満なら前の注釈を下げる) % noteoverhang 注釈本体を後ろに張り出させる量 % noteparstyle 0=囲みなし / 1=実線で囲う / 2=点線で囲う % notelinecolor 注釈線の色 \clist_map_inline:nn { notesize, notepos, noteshift, noterulethickness, noterulehshift, noterulehsize, notesep, noteoverhang, noteparstyle, notelinecolor } { \cs_set_eq:cc { #1 @internal@undernote } { undernote@ #1 } } \msg_new:nnn { modernruler } { invalid-number } { The ~ input ~ is ~ an ~ invalid ~ number. ~ `noteparstyle` ~ must ~ be ~ `0`, ~ `1`, ~ or ~ `2`. } %%% %%% noteparstyle dispatcher % \__modernruler_set_par_lines:n {} % \hline@undernote@par と \vlines@undernote@par を再定義する。 % 引数 #1 は \mruleth / \mruletv のオプションリストに前置されるキー値断片 % (例えば "dash=true," を渡せば点線スタイルになる)。 \cs_new_protected:Npn \__modernruler_set_par_lines:n #1 { \cs_set:Npn \hline@undernote@par { \mruleth [ #1 height = \noterulethickness@internal@undernote , width = \undernote@textlen@tempo , color = \notelinecolor@internal@undernote ] } \cs_set:Npn \vlines@undernote@par { \mruletv [ #1 height = \l__modernruler_vline_dim , width = \noterulethickness@internal@undernote , depth = 0pt , color = \notelinecolor@internal@undernote ] \mruleth [ #1 height = \noterulethickness@internal@undernote , width = \noterulehsize@internal@undernote , depth = 0pt , color = \notelinecolor@internal@undernote ] } } % \__modernruler_apply_noteparstyle:n {} % \wrap@undernote / \wrap@undernote@par を切り替え、 % 下線(\hline@undernote@par)と縦線+横線(\vlines@undernote@par)を % noteparstyle=#1 に対応する形で再定義する。 % - 0: 囲みなし(NoframeBox) 実線 % - 1: 実線で囲う(FramedBox) 実線 % - 2: 点線で囲う(DashedBox) 点線 \cs_new_protected:Npn \__modernruler_apply_noteparstyle:n #1 { \int_case:nnF {#1} { { 0 } { \cs_set_eq:NN \wrap@undernote \wrap@undernote@styleC \cs_set_eq:NN \wrap@undernote@par \wrap@undernote@par@styleC \__modernruler_set_par_lines:n { } } { 1 } { \cs_set_eq:NN \wrap@undernote \wrap@undernote@styleA \cs_set_eq:NN \wrap@undernote@par \wrap@undernote@par@styleA \__modernruler_set_par_lines:n { } } { 2 } { \cs_set_eq:NN \wrap@undernote \wrap@undernote@styleB \cs_set_eq:NN \wrap@undernote@par \wrap@undernote@par@styleB \__modernruler_set_par_lines:n { dash = true , } } } { \msg_warning:nn { modernruler } { invalid-number } } } %%% %%% setting commands for \undernote \pgfkeys{ /KKTeX/unote/.is~family, /KKTeX/unote, notesize/.store~in = \notesize@internal@undernote, notepos/.store~in = \notepos@internal@undernote, noteshift/.store~in = \noteshift@internal@undernote, noterulethickness/.store~in = \noterulethickness@internal@undernote, noterulehshift/.store~in = \noterulehshift@internal@undernote, noterulehsize/.store~in = \noterulehsize@internal@undernote, notesep/.store~in = \notesep@internal@undernote, noteoverhang/.store~in = \noteoverhang@internal@undernote, notelinecolor/.store~in = \notelinecolor@internal@undernote, noteparstyle/.code = { \cs_set:Npn \noteparstyle@internal@undernote {#1} \__modernruler_apply_noteparstyle:n {#1} } } \NewDocumentCommand{\SetUNote}{ O{} }{% \pgfkeys{/KKTeX/unote, #1}% } %%% %%% modern rule commands % 変数の宣言 \dim_new:N \l_mrule_width_dim \dim_new:N \l_mrule_height_dim \dim_new:N \l_mrule_depth_dim \dim_new:N \l_mrule_dash_len_dim \dim_new:N \l_mrule_gap_len_dim \tl_new:N \l_mrule_color_tl \tl_new:N \l_mrule_gap_color_tl \bool_new:N \l_mrule_dash_bool \dim_new:N \l__mrule_tmp_remaining_dim \dim_new:N \l__mrule_segment_dim % キーの定義 \keys_define:nn { modernrule } { width .dim_set:N = \l_mrule_width_dim, height .dim_set:N = \l_mrule_height_dim, depth .dim_set:N = \l_mrule_depth_dim, color .tl_set:N = \l_mrule_color_tl, dash .bool_set:N = \l_mrule_dash_bool, dash-len .dim_set:N = \l_mrule_dash_len_dim, gap-len .dim_set:N = \l_mrule_gap_len_dim, gap-color .tl_set:N = \l_mrule_gap_color_tl, % デフォルト値 width .initial:n = 0pt, height .initial:n = 0pt, depth .initial:n = 0pt, color .initial:n = black, dash .initial:n = false, dash-len .initial:n = 3pt, gap-len .initial:n = 2.5pt, gap-color .initial:n = white, } % 1セグメント描画関数。第1引数: 色、第2引数: 長さ。 % 水平方向は \vrule を、垂直方向は \hrule をそれぞれの「長さ方向」に伸ばす。 \cs_new_protected:Npn \__modernruler_seg_h:nn #1 #2 { { \color{#1} \vrule width #2 height \l_mrule_height_dim depth \l_mrule_depth_dim } } \cs_new_protected:Npn \__modernruler_seg_v:nn #1 #2 { { \color{#1} \hrule width \l_mrule_width_dim height #2 } } % \__modernruler_dash_loop:N \ % \l__mrule_tmp_remaining_dim を残量として、 % メイン色(\l_mrule_color_tl)→ ギャップ色(\l_mrule_gap_color_tl)を % 交互に出力しながら残量を 0 まで減らす。 \cs_new_protected:Npn \__modernruler_dash_loop:N #1 { \dim_while_do:nNnn { \l__mrule_tmp_remaining_dim } > { 0pt } { % メイン色: 残量と dash 長の min を出力 \dim_set:Nn \l__mrule_segment_dim { \dim_min:nn { \l__mrule_tmp_remaining_dim } { \l_mrule_dash_len_dim } } #1 { \l_mrule_color_tl } { \l__mrule_segment_dim } \dim_sub:Nn \l__mrule_tmp_remaining_dim { \l__mrule_segment_dim } % ギャップ色: 残量があれば、残量と gap 長の min を出力 \dim_compare:nNnT { \l__mrule_tmp_remaining_dim } > { 0pt } { \dim_set:Nn \l__mrule_segment_dim { \dim_min:nn { \l__mrule_tmp_remaining_dim } { \l_mrule_gap_len_dim } } #1 { \l_mrule_gap_color_tl } { \l__mrule_segment_dim } \dim_sub:Nn \l__mrule_tmp_remaining_dim { \l__mrule_segment_dim } } } } % 水平方向:2色点線 \NewDocumentCommand{\mruleth}{ O{} } { \group_begin: \keys_set:nn { modernrule } { #1 } \mode_if_vertical:T { \nointerlineskip } \bool_if:NTF \l_mrule_dash_bool { \hbox_to_wd:nn { \l_mrule_width_dim } { \dim_set:Nn \l__mrule_tmp_remaining_dim { \l_mrule_width_dim } \__modernruler_dash_loop:N \__modernruler_seg_h:nn \hss } } { \hbox_to_wd:nn { \l_mrule_width_dim } { \__modernruler_seg_h:nn { \l_mrule_color_tl } { \l_mrule_width_dim } } } \mode_if_vertical:T { \nointerlineskip } \group_end: } % 垂直方向:2色点線 \NewDocumentCommand{\mruletv}{ O{} } { \group_begin: \keys_set:nn { modernrule } { #1 } \bool_if:NTF \l_mrule_dash_bool { \vbox:n { \dim_set:Nn \l__mrule_tmp_remaining_dim { \l_mrule_height_dim } \__modernruler_dash_loop:N \__modernruler_seg_v:nn } } { \vbox:n { \__modernruler_seg_v:nn { \l_mrule_color_tl } { \l_mrule_height_dim } } } \group_end: } %%% %%% undernote % 注釈囲い枠(実線/点線兼用)。 % 第1引数: borderline の線種(solid または dashed)。 \DeclareTotalTCBox{\FramedBox@undernote}{ m O{} +m }{% on~line, arc=0pt, sharp~corners, boxsep=-.5mm+.25\zw, left=.3mm, right=.3mm, top=.3mm, bottom=.3mm, colback=white, colframe=white, boxrule=0pt, enhanced, before={\hspace*{.25\zw}\hspace{-.5mm}}, borderline={\noterulethickness@internal@undernote}{-.25\zw+.5mm}{#1, \notelinecolor@internal@undernote}, #2 }{#3} \DeclareTotalTCBox{\NoframeBox@undernote}{ O{} +m }{% on~line, arc=0pt, sharp~corners, boxsep=-.5mm, left=.8mm, right=.8mm, top=0.3mm, bottom=0.3mm, colback=white, colframe=white, boxrule=0pt, enhanced, before={\hspace*{-.172\zw}}, #1 }{#2} \dim_new:N \undernote@textlen@tempo % モジュール内スクラッチ変数(旧コードの \dimen@ / \count@ / \@tempcnt[ab] / % \@tempdim[ab] を意味のある名前に置き換えたもの) \dim_new:N \l__modernruler_vline_dim % \vlines@undernote@par の縦線長さ \dim_new:N \l__modernruler_aux_width_dim % aux に書き出すノート横幅 \dim_new:N \l__modernruler_yrange_min_dim % 行範囲判定の y 下限 \dim_new:N \l__modernruler_yrange_max_dim % 行範囲判定の y 上限 \dim_new:N \l__modernruler_noteshift_dim % 1 段分の縦シフト量(除数) \dim_new:N \l__modernruler_required_drop_dim % 衝突回避に必要な総縦移動量 \dim_new:N \l__modernruler_right_edge_dim % 注釈右端の物理的限界 \int_new:N \l__modernruler_iter_int % 汎用ループカウンタ \int_new:N \l__modernruler_line_start_int % 現行範囲の開始 id \int_new:N \l__modernruler_line_end_int % 現行範囲の終了 id \int_new:N \l__modernruler_i_int % 衝突判定の外側ループ \int_new:N \l__modernruler_j_int % 衝突判定の内側ループ \int_new:N \l__modernruler_level_int % 計算した重ね段数 \int_new:N \l__modernruler_expected_int % \undernotedata@internal の連番チェック % 注釈の parbox 部分(3スタイル共通)。 % 幅は、語句の幅から縦線位置を引いて張り出し分を足した値。 \cs_new_protected:Npn \__modernruler_undernote_parbox:n #1 { \parbox [t] { \dim_eval:n { \undernote@textlen@tempo - \noterulehsize@internal@undernote - \noterulehshift@internal@undernote + \noteoverhang@internal@undernote } } { \skip_set:Nn \baselineskip { .215\zw } #1 } } \NewDocumentCommand{\wrap@undernote@par@styleA}{ +m } { { \FramedBox@undernote {solid} { \__modernruler_undernote_parbox:n {#1} } } } \NewDocumentCommand{\wrap@undernote@par@styleB}{ +m } { { \FramedBox@undernote {dashed} { \__modernruler_undernote_parbox:n {#1} } } } \NewDocumentCommand{\wrap@undernote@par@styleC}{ +m } { { \NoframeBox@undernote { \__modernruler_undernote_parbox:n {#1} } } } \NewDocumentCommand{\wrap@undernote@styleA}{ +m }{ { \FramedBox@undernote {solid} {#1} } } \NewDocumentCommand{\wrap@undernote@styleB}{ +m }{ { \FramedBox@undernote {dashed} {#1} } } \NewDocumentCommand{\wrap@undernote@styleC}{ +m }{ { \NoframeBox@undernote {#1} } } \NewDocumentCommand{\wrap@undernote}{ +m }{{#1}} \NewDocumentCommand{\wrap@undernote@par}{ +m }{{\wrap@undernote@styleC{#1}}} \__modernruler_apply_noteparstyle:n { \noteparstyle@internal@undernote } \box_new:N \@undernote@maintext \box_new:N \@undernote@subtext \newcounter{undernote@id} % #1 (star): 注釈本体を parbox に包むかどうか % #2 (opt): 注釈を下にずらす「行数」(空指定なら自動段数) % #3: 注釈をつける語句 % #4: 注釈 \NewDocumentCommand{\undernote}{ s O{} m +m } { \group_begin: \stepcounter{undernote@id} \mode_if_math:TF { \@note@save@conters \zsavepos { unote- \int_use:N \c@undernote@id } \@math@undernote {#2} {#3} { \__modernruler_undernote_body:nn {#1} {#4} } } { \settowidth { \undernote@textlen@tempo } {#3} \mode_leave_vertical: \zsavepos { unote- \int_use:N \c@undernote@id } \@text@undernote {#2} {#3} { \__modernruler_undernote_body:nn {#1} {#4} } } \group_end: } \cs_new_protected:Npn \__modernruler_undernote_body:nn #1 #2 { \int_compare:nNnTF { \ltjgetparameter { direction } } = { 4 } { \__modernruler_undernote_varwidth:nn {#1} {#2} } { \raisebox { .38\zw } { \__modernruler_undernote_varwidth:nn {#1} {#2} } } } \cs_new_protected:Npn \__modernruler_undernote_varwidth:nn #1 #2 { \begin{varwidth}[t]{\maxdimen} \IfBooleanTF{#1}{ \wrap@undernote@par{#2} }{ \wrap@undernote{#2} } \end{varwidth} } % 名称カウンタ \def\@note@save@conters{ \group_begin: \cs_set:Npn \@elt ##1 { \str_if_eq:nnF { ##1 } { page } { \int_gset:cn { c@ ##1 } { \int_use:c { c@ ##1 } } } } \tl_set:Nx \l_tmpa_tl { \cl@@ckpt } \exp_args:NNNV \group_end: \tl_set:Nn \l_tmpa_tl \l_tmpa_tl % グループ外への持ち出し \cs_gset_eq:NN \@note@restore@counters \l_tmpa_tl } % 数式モードの場合への対応。 % 4 つの math style ごとに、語句の幅を測ってから \@text@undernote へ流す。 % #1: math style 切替コマンド (\displaystyle / \textstyle / \scriptstyle / \scriptscriptstyle) % #2: \undernote の段数引数, #3: 語句, #4: 注釈 \cs_new_protected:Npn \__modernruler_math_one:Nnnn #1 #2 #3 #4 { \settowidth { \undernote@textlen@tempo } { \m@th $ #1 #3 $ } \@note@restore@counters \@text@undernote {#2} { \m@th $ #1 #3 $ } {#4} } \cs_set:Npn \@math@undernote #1 #2 #3 { \mathchoice { \__modernruler_math_one:Nnnn \displaystyle {#1} {#2} {#3} } { \__modernruler_math_one:Nnnn \textstyle {#1} {#2} {#3} } { \__modernruler_math_one:Nnnn \scriptstyle {#1} {#2} {#3} } { \__modernruler_math_one:Nnnn \scriptscriptstyle {#1} {#2} {#3} } } % 注釈の内部実装 \prg_new_conditional:Nnn \__modernruler_if_file_write: { p, T, F, TF } { \legacy_if:nTF { @filesw } { \prg_return_true: } { \prg_return_false: } } \cs_new_protected:Npn \@text@undernote #1 #2 #3 { \hbox:n { % #1: \undernote の第 2 引数(段数指定)。空なら自動段数を採用 \tl_set:Nn \@UNDATA@vsize {#1} \tl_if_empty:cT { @UNDATA@vsize } { \cs_if_eq:cNTF { @UNDATAS@ \int_use:N \c@undernote@id } \scan_stop: { \tl_set:Nn \@UNDATA@vsize { 1 } } { \tl_set_eq:Nc \@UNDATA@vsize { @UNDATAS@ \int_use:N \c@undernote@id } } } \hbox_set:Nn \@undernote@maintext { #2 \mruletv [ height = .8\zw , width = 0pt , color = \notelinecolor@internal@undernote ] } \hbox_set:Nn \@undernote@subtext { \notesize@internal@undernote #3 } \vbox_top:n { \box_use:N \@undernote@maintext \group_begin: \notesize@internal@undernote \__modernruler_if_file_write:TF { \dim_set:Nn \l__modernruler_aux_width_dim { \box_wd:N \@undernote@subtext + \notesep@internal@undernote } % .aux に % \undernotedata@internal {} {} {} % {} {} {} {} {} % を書き出す(全部 sp 値) \iow_shipout:Nx \@auxout { \token_to_str:N \undernotedata@internal { \int_use:N \c@undernote@id } { \exp_not:N \zposx { unote- \int_use:N \c@undernote@id } } { \exp_not:N \zposy { unote- \int_use:N \c@undernote@id } } { \number \l__modernruler_aux_width_dim } { \number \box_ht:N \@undernote@subtext } { \number \box_dp:N \@undernote@subtext } { \int_use:N \c@page } { \number \box_dp:N \@undernote@maintext } } } { \iow_term:n { } } \group_end: \skip_vertical:n { 1pt } \hline@undernote@par % アンダーライン \hbox:n { \notesize@internal@undernote \skip_horizontal:n { \noterulehshift@internal@undernote } % ノートの縦幅 (= 1 段分の縦シフト * (段数-1)) + 縦線の最低長さ \dim_set:Nn \l__modernruler_vline_dim { \noteshift@internal@undernote * ( \@UNDATA@vsize - 1 ) + \notepos@internal@undernote } \vlines@undernote@par \box_move_down:nn { 0.38 \zw } { \hbox_to_zero:n { \box_use:N \@undernote@subtext \hss } } } \skip_vertical:n { .3\zw } } } } \int_new:N \@UNDATA@min \int_new:N \@UNDATA@max \int_gset:Nn \@UNDATA@max { -10000 } \tl_gclear:N \@UNDATA@idlist \cs_set_eq:NN \@UNDATA@elt \relax \msg_new:nnn { modernruler } { multiple-labels } { There ~ were ~ multiply-defined ~ labels } \cs_new_protected:Npn \undernotedata@internal #1 #2 #3 #4 #5 #6 #7 #8 { \cs_if_exist:cTF { @UNDATA@ #1 } { \msg_warning:nn { modernruler } { multiple-labels } } { \int_set:Nn \l__modernruler_expected_int { \@UNDATA@max + 1 } \int_compare:nNnTF { \l__modernruler_expected_int } = {#1} { \int_gset:Nn \@UNDATA@max { \l__modernruler_expected_int } } { \int_compare:nNnF { \@UNDATA@max } < { 0 } { \tl_gput_right:Nx \@UNDATA@idlist { \exp_not:N \@UNDATA@elt { \int_use:N \@UNDATA@min } { \int_use:N \@UNDATA@max } } } \int_gset:Nn \@UNDATA@min {#1} \int_gset:Nn \@UNDATA@max { \@UNDATA@min } } } \cs_gset:cpn { @UNDATA@ #1 } { {#2}{#3}{#4}{#5}{#6}{#7}{#8} } } \cs_set:Npn \@undernotedata #1#2#3#4#5#6#7#8 { \str_if_eq:eeTF { \cs_if_exist_use:c { @UNDATA@ #1 } } { {#2}{#3}{#4}{#5}{#6}{#7}{#8} } { } { \legacy_if_set_true:n { @tempswa } } } \cs_set:Npn \checkundernotedata { \int_compare:nNnF { \@UNDATA@max } < { 0 } { \tl_gput_right:Nx \@UNDATA@idlist { \exp_not:N \@UNDATA@elt { \int_use:N \@UNDATA@min } { \int_use:N \@UNDATA@max } } \group_begin: \cs_set_eq:NN \@UNDATA@elt \@check@undernotedata \@UNDATA@idlist \group_end: } } \cs_set:Npn \@check@undernotedata #1 #2 { %%% RESET \tl_set:Nn \@currpage { -10000 } \int_set:Nn \l__modernruler_line_start_int { 0 } \int_set:Nn \l__modernruler_line_end_int { 0 } \dim_set:Nn \l__modernruler_yrange_min_dim { -1pt } \dim_set:Nn \l__modernruler_yrange_max_dim { -1pt } \tl_clear:N \@linelist \cs_set_eq:NN \@elt \scan_stop: %%% \int_set:Nn \l__modernruler_iter_int { #1 - 1 } \int_while_do:nNnn { \l__modernruler_iter_int } < {#2} { \int_incr:N \l__modernruler_iter_int \@check@undernotedata@split \l__modernruler_iter_int % \l_tmpa_bool := 「現在のノートが直前と同じ行範囲に入るか」 \bool_set_false:N \l_tmpa_bool \int_compare:nNnT { \@currpage } = { \@thispage } { \dim_compare:nNnF { \@thisy sp } < { \l__modernruler_yrange_min_dim } { \dim_compare:nNnF { \@thisy sp } > { \l__modernruler_yrange_max_dim } { \bool_set_true:N \l_tmpa_bool } } } \bool_if:NTF \l_tmpa_bool { % 範囲内:行範囲の終端を更新 \int_set_eq:NN \l__modernruler_line_end_int \l__modernruler_iter_int } { % 範囲外:直前の範囲を書き出してリセット \int_compare:nNnT { \l__modernruler_line_start_int } > { 0 } { \tl_put_right:Nx \@linelist { \@elt { \int_use:N \l__modernruler_line_start_int } { \int_use:N \l__modernruler_line_end_int } } } \int_set_eq:NN \l__modernruler_line_start_int \l__modernruler_iter_int \int_set_eq:NN \l__modernruler_line_end_int \l__modernruler_iter_int \cs_set_eq:NN \@currpage \@thispage \dim_set:Nn \l__modernruler_yrange_min_dim { \@thisy sp } \dim_set_eq:NN \l__modernruler_yrange_max_dim \l__modernruler_yrange_min_dim \dim_sub:Nn \l__modernruler_yrange_min_dim { \c__modernruler_yfuzz_dim } \dim_add:Nn \l__modernruler_yrange_max_dim { \c__modernruler_yfuzz_dim } } } \tl_put_right:Nx \@linelist { \@elt { \int_use:N \l__modernruler_line_start_int } { \int_use:N \l__modernruler_line_end_int } } \cs_set_eq:NN \@elt \@check@undernotedata@elt \@linelist } \dim_const:Nn \c__modernruler_yfuzz_dim { 3pt } % \undernotedata@internal で .aux に書き出したデータ \@UNDATA@ を、split@ % の 7 引数 (+ \@nnil 区切りの第8引数) へ流し込む。 % 3 段の \exp_after:wN は \cs:w...\cs_end: で組み立てた CS を split@ の手前で % 1 段展開する古典イディオム。末尾の "0000000" はデータ不足時のダミー: % -- データが十分なら全部 #8 (\@nnil 直前) に吸われる % -- 不足なら 0 が #1..#7 を埋める \cs_new_protected:Npn \@check@undernotedata@split #1 { \exp_after:wN \exp_after:wN \exp_after:wN \@check@undernotedata@split@ \cs:w @UNDATA@ \int_use:N #1 \cs_end: 0000000 \@nnil } \cs_new_protected:Npn \@check@undernotedata@split@ #1 #2 #3 #4 #5 #6 #7 #8 \@nnil { \tl_set:Nn \@thisx {#1} \tl_set:Nn \@thisy {#2} \tl_set:Nn \@thiswd {#3} \tl_set:Nn \@thisht {#4} \tl_set:Nn \@thisdp {#5} \tl_set:Nn \@thispage {#6} \tl_set:Nn \@thismaindp {#7} } % 行範囲 [#1, #2] 内の各ノートについて、後続ノートとの重なりを % \noteshift@internal@undernote 単位で何段下げれば回避できるかを計算し、 % その段数を \@UNDATAS@ に格納する。逆順に i を回し、各 i について % j を i+1..#2 で走らせる二重ループ。 \cs_new_protected:Npn \@check@undernotedata@elt #1 #2 { \int_set:Nn \l__modernruler_i_int { #2 + 1 } \int_while_do:nNnn { \l__modernruler_i_int } > {#1} { \int_decr:N \l__modernruler_i_int \@check@undernotedata@split \l__modernruler_i_int % i のノートの右端(横方向の物理的限界位置) \dim_set:Nn \l__modernruler_right_edge_dim { \@thisx sp + \@thiswd sp + \noterulehsize@internal@undernote } \cs_set_eq:NN \@currht \@thisht \int_set:Nn \l__modernruler_level_int { 1 } \int_set_eq:NN \l__modernruler_j_int \l__modernruler_i_int \int_while_do:nNnn { \l__modernruler_j_int } < {#2} { \int_incr:N \l__modernruler_j_int \@check@undernotedata@split \l__modernruler_j_int \dim_compare:nNnT { \@thisx sp } < { \l__modernruler_right_edge_dim } { \dim_set_eq:NN \l__modernruler_noteshift_dim \noteshift@internal@undernote \dim_set:Nn \l__modernruler_required_drop_dim { \@thisdp sp + \@currht sp + \lineskip + \@thismaindp sp % メインテキストの深さも考慮 } % 段数を比率で計算(重なりがあれば必ず 1 段以上) \int_set:Nn \l_tmpa_int { \fp_eval:n { floor ( \dim_to_fp:n { \l__modernruler_required_drop_dim } / \dim_to_fp:n { \l__modernruler_noteshift_dim } ) } + 1 } \int_add:Nn \l_tmpa_int { \use:c { @UNDATAS@ \int_use:N \l__modernruler_j_int } } \int_set:Nn \l__modernruler_level_int { \int_max:nn { \l__modernruler_level_int } { \l_tmpa_int } } } } \cs_gset:cpx { @UNDATAS@ \int_use:N \l__modernruler_i_int } { \int_use:N \l__modernruler_level_int } } } \AtEndDocument { \legacy_if:nT { @filesw } { \iow_shipout:Nx \@auxout { \token_to_str:N \checkundernotedata } \cs_set_eq:NN \checkundernotedata \scan_stop: \cs_set_eq:NN \undernotedata@internal \@undernotedata } } %%% \endinput