4

All is in the title: I want to get rid of this horizontal space that comes before a \left[ square bracket spanning multiple rows. See MWE and picture.

MWE

\documentclass[12pt,a4paper,twoside,openright]{report}

\begin{document}

    \noindent
    Text left aligned.\\[0.5ex]

    \noindent
    $\left[%
    \begin{tabular}{l}%
        /X-ion/\\
        N\\
        ACT of Z-ing
    \end{tabular}%
    \right]$
    
\end{document}

enter image description here

1 Answer 1

5

This is a matter of "side bearings", see for example "3. Bearings and Advances".

The easiest way is the trial-and-error approach. Add a negative space before the left bracket until you are satisfied.

An exact solution is quite complicate, because

  • the glyph bounding boxes are not available in vanilla TeX or pdfTeX and
  • it is not easy to figure out the correct font, size and glyph that is actually used in the output.

An analysis specific for this case

The uppercase "T" uses the font "cmr12.pfb" (pdfTeX) or "lmroman12-regular.otf" (luaLaTeX). Happily, both fonts use the same side bearing for "T". I got the font name from the end of the .log file written by the LaTeX run.

The Python/FontForge script extracts the left side bearing from the font and glyph "T": 0.035em

The negative space command for compensating the left side bearing is then:

\kern-0.035em

It gets trickier, if the font is scaled in LaTeX and or internal math glyph constructions are involved. The left bracket is here composed from three parts: the upper, middle and lower part.

The TeX command \showlists helps in identifying the font and the used glyph. For the lower part of the opening bracket:

...\hbox(0.39998+17.60019)x6.66669
....\OMX/cmex/m/n/6 4

At the position of the "character 4" (0x34) I found the glyph bracketleftbt.

The Python/FontForge script reports 0.326em for the left side bearing. Now, the correct font needs to be set to get the correct value of unit "em":

\begingroup
    \csname OMX/cmex/m/n/6\endcsname
    \kern-0.326em\relax
\endgroup

Using the TeX width 6.66669pt of the surrounding \hbox can be used to calculate the side bearing in unit pt, see the Python/FontForge script:

\kern-3.263274684684685pt\relax

I have changed the minimal TeX example to add a thin \fbox with a line width of 0.1pt and separation/margin of the same space to visualize that the "T" and the left bracket start at the same horizontal position.

enter image description here

The LaTeX file:

\documentclass[12pt,a4paper,twoside,openright]{report}

% \showboxdepth=\maxdimen
% \showboxbreadth=\maxdimen
% \tracingonline=1

\begin{document}

    \setlength{\fboxsep}{0.1pt}
    \setlength{\fboxrule}{0.1pt}
    \fbox{%
        \begin{tabular}{@{}l@{}}
            \kern-0.035em Text left aligned.\\[0.5ex]
            % \begingroup
            %   \csname OMX/cmex/m/n/6\endcsname
            %   \kern-0.326em\relax
            % \endgroup
            \kern-3.263274684684685pt\relax
            $\left[%
                \begin{tabular}{l}%
                    /X-ion/\\
                    N\\
                    ACT of Z-ing
                \end{tabular}%
            \right]$
        \end{tabular}%
    }

% \showlists

\end{document}

The Python/FontForge script that extracts the left side bearing values:

#!/usr/bin/env fontforge

import subprocess
from typing import Union

import fontforge  # (Python 3.8)

FONT_FOR_UPPERCASE_T_1 = 'cmr12.pfb'
FONT_FOR_UPPERCASE_T_2 = 'lmroman12-regular.otf'
FONT_FOR_LEFT_BRACKET = 'cmex10.pfb'


def find_font(name: str) -> str:
    process = subprocess.run(
        ['kpsewhich', name],
        check=True,
        stdout=subprocess.PIPE,
    )
    # Error checking omitted
    return process.stdout.decode().strip()


def get_left_side_bearing(font_name: str, glyph: Union[str, int])-> float:
    """
    Return the left side bearing of the specified glyph in the
    font, given by font_name. The unit is em.
    """
    font_path = find_font(font_name)
    font = fontforge.open(font_path)
    try:
        left_side_bearing, _, _, _ = font[glyph].boundingBox()
        return left_side_bearing / font.em
    finally:
        font.close()


def get_advance_width(font_name: str, glyph: Union[str, int])-> float:
    """
    Return the advance width of the specified glyph in the
    font, given by font_name. The unit is em.
    """
    font_path = find_font(font_name)
    font = fontforge.open(font_path)
    try:
        return font[glyph].width / font.em
    finally:
        font.close()


def main() -> None:
    # Side bearing for uppercase T
    # ----------------------------
    left_side_bearing_for_uppercase_t = get_left_side_bearing(
        FONT_FOR_UPPERCASE_T_1,
        'T',
    )
    print(
        f'=> Left side bearing of "T": {left_side_bearing_for_uppercase_t}em'
        f' ({FONT_FOR_UPPERCASE_T_1})'
    )
    left_side_bearing_for_uppercase_t = get_left_side_bearing(
        FONT_FOR_UPPERCASE_T_2,
        'T',
    )
    print(
        f'=> Left side bearing of "T": {left_side_bearing_for_uppercase_t}em'
        f' ({FONT_FOR_UPPERCASE_T_2})'
    )

    # Side bearing for left bracket
    # -----------------------------
    glyph = 'bracketleftbt'
    
    left_side_bearing_for_left_bracket = get_left_side_bearing(
        FONT_FOR_LEFT_BRACKET,
        glyph,
    )
    print(
        f'=> Left side bearing of "[": {left_side_bearing_for_left_bracket}em'
    )
    
    # ...\hbox(0.39998+17.60019)x6.66669
    #  ....\OMX/cmex/m/n/6 4
    
    tex_advance_width = 6.66669  # pt
    print(f'=> TeX advance width: {tex_advance_width}pt')

    advance_width = get_advance_width(FONT_FOR_LEFT_BRACKET, glyph)
    print(f'=> Advance width: {advance_width}em')
    
    left_side_bearing_pt = (
        tex_advance_width / advance_width * left_side_bearing_for_left_bracket
    )
    print(f'=> Left side bearing: {left_side_bearing_pt}pt')


if __name__ == '__main__':
    main()

Run the script with FontForge:

=> Left side bearing of "T": 0.035em (cmr12.pfb)
=> Left side bearing of "T": 0.035em (lmroman12-regular.otf)
=> Left side bearing of "[": 0.326em
=> TeX advance width: 6.66669pt
=> Advance width: 0.666em
=> Left side bearing: 3.263274684684685pt

LuaTeX

LuaTeX can access font glyph bounding boxes, see key "bounding box" in "12.6.2 glyphs" of the LuaTeX manual. Thus, there might be an easier way via some Lua code, e.g. for the uppercase "T" here.

Math mode is much trickier (\left-\right constructs, roots, \mathchoice, ...). Maybe, the math symbols in question can be marked by LuaTeX attributes. After typesetting math in a \hbox, page, ... the node list can be inspected afterwards to identify the marked glyphs to calculate the side bearings. The shifts can then be applied directly in the node list or in the next LaTeX run.

But this approach is just a sketch and needs further investigating and much time for implementing.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .