2

I am studying the solution provided in this Stack Exchange thread: Is there a way to automatically smash all inline maths (but only vertically)?, which was originally answered by user Mico. I've included the relevant code from Mico's answer below. This code applies \smash to everything within the $$ (inline math) environment. However, I am interested in extending this functionality to also apply \smash to not only math, but also any letters in the main text with descenders, such as 'p', 'q', 'y,' and others.

As I discussed with Mico, it appears to be more challenging than initially expected to implement this additional functionality. If you have any suggestions or alternative approaches to achieve the desired effect, I would greatly appreciate your assistance. Thank you for your help!

% !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}

wrb98
  • 876
  • 2
    If you just want to preserve line spacing, you do not need \smash at all, just set \lineskiplimit see https://tex.stackexchange.com/questions/524976/fontspec-how-to-keep-baselineskip-stable/524980#524980 or https://tex.stackexchange.com/questions/549218/what-is-the-behaviour-of-fontsize-if-baselineskip-is-below-a-certain-value/549219#549219 – David Carlisle Oct 30 '23 at 19:27
  • I'm really looking to see if my exact request is possible. – wrb98 Oct 30 '23 at 19:49
  • Could you please provide the completed code to get this to work? I'd like to avoid long comment sections – wrb98 Oct 30 '23 at 19:59
  • Can you provide a MWE that demonstrates the issue? – keth-tex Feb 29 '24 at 15:41

2 Answers2

4

This seems like a pretty bad idea to me—usually you want the descenders to have depth so that they don't collide with other characters. Nevertheless, it's not too tricky to do this automatically with 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} &amp;
    \autosmashtrue  \drawbox{glyph $\displaystyle\int$ glyph} \\

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

    \autosmashfalse \drawbox{glyph $q_{g_{y_{p_j}}}$ glyph} &amp;
    \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}

output

You can see in the comparison table that the depth is completely removed whenever you toggle on \autosmashtrue, without affecting the height, width, or positioning. This looks alright in the table, but in the body text demo that follows it looks pretty bad.

Max Chernoff
  • 4,667
3

You do not need to modify individual descender glyph boxes to avoid that they affect the line spacing, the feature is already built in to classic TeX.

enter image description here

\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}
David Carlisle
  • 757,742