修改 lualatex 代码以自动将 \smash 应用于带有降部和数学的字母

修改 lualatex 代码以自动将 \smash 应用于带有降部和数学的字母

我正在研究此 Stack Exchange 线程中提供的解决方案:有没有办法自动粉碎所有内联数学(但仅限于垂直方向)?,最初由用户 Mico 回答。我在下面附上了 Mico 回答的相关代码。此代码将 \smash 应用于 $$(内联数学)环境中的所有内容。但是,我有兴趣扩展此功能,以便不仅将 \smash 应用于数学,还将 \smash 应用于正文中带有降序的任何字母,例如“p”、“q”、“y”等。

正如我与 Mico 讨论的那样,实现这一附加功能似乎比最初预期的更具挑战性。如果您有任何建议或替代方法来实现预期效果,我将不胜感激您的帮助。感谢您的帮助!

% !TEX TS-program = lualatex
\documentclass{article}
\usepackage{amsmath}  % for '\dfrac' macro
\usepackage{lipsum}   % filler text
\usepackage{luacode}  % for 'luacode' env.
\begin{luacode}
   function smash ( s )
     return ( s:gsub ( "%b$$" , "\\smash{%1}" ) )
   end
\end{luacode}
\newcommand\SmashOn{\directlua{luatexbase.add_to_callback (
   "process_input_buffer", smash, "smash" )}}
\newcommand\SmashOff{\directlua{luatexbase.remove_from_callback (
   "process_input_buffer", "smash" )}}
  
\begin{document} 
\hrule % draw line to illustrate width of textblock
\lipsum[1][1-5] $\dfrac{\dfrac{v}{v}}{\dfrac{w}{x}}$ \lipsum[1][5-8]
\hrule
\SmashOn % activate the Lua function
\lipsum[1][1-5] $\dfrac{\dfrac{v}{v}}{\dfrac{w}{x}}$ \lipsum[1][5-8]
\hrule
\SmashOff % deactivate the Lua function 
\lipsum[1][1-5] $\dfrac{\dfrac{v}{v}}{\dfrac{w}{x}}$ \lipsum[1][5-8]
\hrule
\end{document}

答案1

在我看来,这似乎是一个非常糟糕的主意——通常你希望下降部分有深度,这样它们就不会与其他字符发生碰撞。然而,它不是使用 Lua 自动执行此操作比较棘手:

\documentclass{standalone}

%% The next section contains the code that is necessary to automatically smash
%% descenders in math and text modes.
%%% BEGIN REQUIRED CODE %%%
\newif\ifautosmash

\usepackage{luacode}
\begin{luacode*}
    local iftrue = token.create("iftrue").mode

    -- Handle descenders in text mode
    local function smash_nodes(head)
        -- Check to see if autosmashing is enabled
        if token.create("ifautosmash").mode ~= iftrue then
            return head
        end

        -- Iterate over all nodes in the list
        local n = head
        repeat
            -- Remove descenders
            if n.id == node.id("glyph") then
                local new = node.copy_list(n, n.next)

                -- We can't modify the depth of a glyph directly, so we
                -- need to pack it into a box.
                local box = node.hpack(new)
                box.depth = 0

                -- Replace the glyph
                head, box = node.insert_after(head, n, box)
                head = node.remove(head, n)

                n = node.free(n)
            end
            n = n.next
        until not n

        return head
    end

    luatexbase.add_to_callback("pre_linebreak_filter", smash_nodes, "smash_nodes")
    luatexbase.add_to_callback("hpack_filter", smash_nodes, "smash_nodes")


    -- Handle descenders in math mode
    local function smash_math(...)
        local n = node.mlist_to_hlist(...)

        if token.create("ifautosmash").mode == iftrue then
            n = node.hpack(n)
            n.depth = 0
        end

        return n
    end

    luatexbase.add_to_callback("mlist_to_hlist", smash_math, "smash_math")
\end{luacode*}
%%% END REQUIRED CODE %%%


%% The following code is just to draw a box around its contents; it is
%% *not* necessary for the rest of the code to work.
%%% BEGIN EXTRA CODE %%%
\renewcommand{\arraystretch}{1.75}

\ExplSyntaxOn
\dim_const:Nn \c__max_thickness_dim { 0.1pt }
\dim_new:N \g__max_half_ex_dim
\dim_new:N \l__max_asc_ht_dim
\dim_new:N \l__max_des_dp_dim
\box_new:N \l__max_contents_box
\box_new:N \l__max_hline_box
\box_new:N \l__max_vline_box

\cs_new:Nn \__max_font_size: {
    \fontsize { 0.2em } { 0.2em } \selectfont \scshape
}

\cs_new:Nn \__max_hline:nn {
    \box_move_up:nn { #2 - \g__max_half_ex_dim } {
        \hbox_overlap_left:n {
            \__max_font_size: #1
            \skip_horizontal:n {
                \g__max_half_ex_dim + (\c__max_thickness_dim / 2)
            }
        }
    }

    \box_move_up:nn { #2 } { \box_use:N \l__max_hline_box }

    \box_move_up:nn { #2 - \g__max_half_ex_dim } {
        \hbox_overlap_right:n {
            \skip_horizontal:n {
                \box_wd:N \l__max_contents_box +
                \g__max_half_ex_dim +
                (\c__max_thickness_dim / 2)
            }
            \__max_font_size: #1
        }
    }
}

\cs_new:Nn \__max_vline:n {
    \hbox_set_to_wd:Nnn \l_tmpa_box { 0pt } { \hss \__max_font_size: #1 \hss }

    \box_move_down:nn {
        \box_dp:N \l__max_contents_box +
        \box_ht:N \l_tmpa_box +
        \g__max_half_ex_dim +
        (\c__max_thickness_dim / 2)
    } {
        \box_use:N \l_tmpa_box
    }

    \box_move_up:nn {
        \box_ht:N \l__max_contents_box +
        \box_dp:N \l_tmpa_box +
        \g__max_half_ex_dim +
        (\c__max_thickness_dim / 2)
    } {
        \box_use_drop:N \l_tmpa_box
    }

    \box_use:N \l__max_vline_box
}

\prg_new_conditional:Nnn \__max_dimcomp:nn { T } {
    \bool_if:nTF {
        \dim_compare_p:n {
            #1 < 0.8 #2
        }
        ||
        \dim_compare_p:n {
            #1 > 1.2 #2
        }
    } {
        \prg_return_true:
    } {
        \prg_return_false:
    }
}

\NewDocumentCommand { \drawbox } { m } {
    \group_begin:
        \__max_font_size:
        \dim_gset:Nn \g__max_half_ex_dim { 0.5ex }
    \group_end:

    \hbox_set:Nn \l__max_contents_box { \color_ensure_current: #1 }

    \hbox_set_to_wd:Nnn \l__max_hline_box { 0pt } {
        \vrule
            width \box_wd:N \l__max_contents_box height \c__max_thickness_dim
            height 0.5 \c__max_thickness_dim
            depth 0.5 \c__max_thickness_dim
        \hss
    }

    \hbox_set_to_wd:Nnn \l__max_vline_box { 0pt } {
        \hss
        \vrule
            width \c__max_thickness_dim
            height \dim_eval:n {
                \box_ht:N \l__max_contents_box + (\c__max_thickness_dim / 2)
            }
            depth \dim_eval:n {
                \box_dp:N \l__max_contents_box + (\c__max_thickness_dim / 2)
            }
        \hss
    }

    \hbox_set:Nn \l_tmpa_box { l h t i }
    \dim_set:Nn \l__max_asc_ht_dim { \box_ht:N \l_tmpa_box }

    \hbox_set:Nn \l_tmpa_box { y j p g }
    \dim_set:Nn \l__max_des_dp_dim { \box_dp:N \l_tmpa_box }

    \hbox:n {
        \color_group_begin:
            \color_select:n { red ! 50 ! black ! 75 ! white }

            \__max_hline:nn { bl } { 0pt }
            \__max_hline:nn { ht } { \box_ht:N \l__max_contents_box }
            \__max_hline:nn { ex } { 1ex }

            \__max_hline:nn {
                \__max_dimcomp:nnT {
                    0pt
                } {
                    \box_dp:N \l__max_contents_box
                } {
                    dp
                }
            } { - \box_dp:N \l__max_contents_box }

            \__max_hline:nn {
                \__max_dimcomp:nnT {
                    \l__max_asc_ht_dim
                } {
                    \box_ht:N \l__max_contents_box
                } {
                    asc
                }
            } { \l__max_asc_ht_dim }

            \__max_hline:nn {
                \__max_dimcomp:nnT {
                    \l__max_des_dp_dim
                } {
                    \box_dp:N \l__max_contents_box
                } {
                    des
                }
            } { -\l__max_des_dp_dim }

            \__max_vline:n { l }

            \hbox_overlap_right:n {
                \skip_horizontal:n { \box_wd:N \l__max_contents_box }
                \__max_vline:n { r }
            }
        \color_group_end:

        \box_use_drop:N \l__max_contents_box
    }
}
\ExplSyntaxOff
%%% END EXTRA CODE %%%


%% Example usage and demonstration
\begin{document}
    \begin{tabular}{rl}
        \autosmashfalse \drawbox{glyph glyph glyph} &
        \autosmashtrue  \drawbox{glyph glyph glyph} \\

        \autosmashfalse \drawbox{glyph $\displaystyle\int$ glyph} &
        \autosmashtrue  \drawbox{glyph $\displaystyle\int$ glyph} \\

        \autosmashfalse \drawbox{glyph $\sqrt{x}^2$ glyph} &
        \autosmashtrue  \drawbox{glyph $\sqrt{x}^2$ glyph} \\

        \autosmashfalse \drawbox{glyph $q_{g_{y_{p_j}}}$ glyph} &
        \autosmashtrue  \drawbox{glyph $q_{g_{y_{p_j}}}$ glyph} \\

        \multicolumn{2}{p{2.3in}}{
            \fontsize{8pt}{0pt}\selectfont \autosmashtrue

            The image on top displays how the descenders are automatically
            smashed when \texttt{\char`\\ auto\-smash\-true} is running.
            Collisions are quite frequent, so be very careful with low-hanging
            equations like $\sqrt{x_1} = \int_0^1 \beta_y$. Notice that only the
            depth is tighter---the line above is lowered by \LaTeX{} to account
            for its extra height.
        }
    \end{tabular}
\end{document}

输出

您可以在比较表中看到,只要您打开 ,深度就会被完全移除\autosmashtrue,而不会影响高度、宽度或定位。这在表格中看起来还不错,但在下面的正文演示中看起来很糟糕。

答案2

您不需要修改单个降部字形框以避免它们影响行距,该功能已内置于经典 TeX 中。

在此处输入图片描述

\documentclass{article}
\usepackage{amsmath}  % for '\dfrac' macro
\usepackage{lipsum}   % filler text
\def\SmashOn{\par\lineskiplimit=-\maxdimen}
\def\SmashOff{\par\lineskiplimit= \normallineskiplimit}
\begin{document} 
\hrule % draw line to illustrate width of textblock
\lipsum[1][1-5] $\dfrac{\dfrac{v}{v}}{\dfrac{w}{x}}$ \lipsum[1][5-8]
\hrule
\SmashOn % activate the Lua function
\lipsum[1][1-5] $\dfrac{\dfrac{v}{v}}{\dfrac{w}{x}}$ \lipsum[1][5-8]
\hrule
\SmashOff % deactivate the Lua function 
\lipsum[1][1-5] $\dfrac{\dfrac{v}{v}}{\dfrac{w}{x}}$ \lipsum[1][5-8]
\hrule
\end{document}

相关内容