2

I am writing a package that requires extracting dimensions of certain glyphs. The package would support both xelatex & lualatex. In my testing, xelatex always returns accurate glyph dimensions with its \XeTeXglyphbounds interface. Lualatex solution provided here does not return accurate glyph dimensions for .ttf files, but does for .otf files. Here's the two files you will need to see for yourself:

sidebearings.lua (with some additions on top of code here):

--- sidebearings.lua
  packagedata                  = packagedata or { }
  packagedata.sidebearings     = { }
  local sidebearings           = packagedata.sidebearings

  local utfbyte                = utf.byte
  local texsprint              = tex.sprint

  local get_sidebearings = function (id, char)
    local tfmdata = font.getfont (id)

    if not (tfmdata and tfmdata.shared) then
      return 0, 0
    end

    local descriptions = tfmdata.shared.rawdata.descriptions
    local glyphdata    = descriptions [char]
    if not glyphdata then
      --- font lacks the glyph
      return 0, 0, 0, 0
    end

    local boundingbox   = glyphdata.boundingbox
    local lside         = boundingbox [1] or 0
    local wd            = boundingbox [3] or glyphdata.width
    local rside         = glyphdata.width - wd
    local tside         = glyphdata.height or 0
    local bside         = glyphdata.depth or 0

    --- If you would like to print the values to terminal
    inspect (glyphdata)

    return lside / 100, rside / 100, tside / 100, bside / 100
  end

  local sidebearings = function (id, char, side)
    char = utfbyte (char)
    local lside, rside, tside, bside = get_sidebearings (id, char)
    if side == "left" then
      texsprint (tostring (lside), "pt")
    elseif side == "right" then
      texsprint (tostring (rside), "pt")
    elseif side == "top" then
      texsprint (tostring (tside), "pt")
    elseif side == "bottom" then
      texsprint (tostring (bside), "pt")
    end
  end

  packagedata.sidebearings.left  = function (char)
    return sidebearings (font.current (), char, "left")
  end

  packagedata.sidebearings.right = function (char)
    return sidebearings (font.current (), char, "right")
  end

  packagedata.sidebearings.top = function (char)
    return sidebearings (font.current (), char, "top")
  end

  packagedata.sidebearings.bottom = function (char)
    return sidebearings (font.current (), char, "bottom")
  end

and main file sidebearings.tex:

\documentclass[notitlepage,letterpaper]{article}
\usepackage[letterpaper,left=0in,right=0in,top=0in,bottom=0in]{geometry}
\usepackage{fontspec}
\usepackage{microtype}

\ifdefined \directlua

  \directlua {require "sidebearings"}

  \def \lsidebearing #1{%
    \directlua {packagedata.sidebearings.left [[#1]]}%
  }

  \def \rsidebearing #1{%
    \directlua {packagedata.sidebearings.right [[#1]]}%
  }

  \def \tsidebearing #1{%
    \directlua {packagedata.sidebearings.top [[#1]]}%
  }

  \def \bsidebearing #1{%
    \directlua {packagedata.sidebearings.bottom [[#1]]}%
  }


\else
\def \lsidebearing #1{%
\ifnum\the\XeTeXcharglyph\expandafter`#1=0
 0pt%
\else%
%\number \XeTeXglyphbounds1 \the \XeTeXcharglyph`#1 sp% %for output in unit sp
\the \XeTeXglyphbounds1 \the \XeTeXcharglyph\expandafter`#1%
\fi%
}
\def \rsidebearing #1{%
\ifnum\the\XeTeXcharglyph\expandafter`#1=0
 0pt%
\else%
\the \XeTeXglyphbounds3 \the \XeTeXcharglyph\expandafter`#1%
\fi%
}
\def \tsidebearing #1{%
\ifnum\the\XeTeXcharglyph\expandafter`#1=0
 0pt%
\else%
\the \XeTeXglyphbounds2 \the \XeTeXcharglyph\expandafter`#1%
\fi%
}
\def \bsidebearing #1{%
\ifnum\the\XeTeXcharglyph\expandafter`#1=0
 0pt%
\else%
\the \XeTeXglyphbounds4 \the \XeTeXcharglyph\expandafter`#1%
\fi%
}

\fi

\setmainfont{Verdana}% switch to Aleo-Regular when testing Aleo 
\def \test #1{[#1] left: \lsidebearing {#1}, right: \rsidebearing {#1}, bottom: \bsidebearing {#1}, top: \tsidebearing {#1}\par}

\begin{document}

\thispagestyle{plain}

\test a
\test b
\test y
\test z
\test а
\test б
\test ю
\test я

\fontname\font

\end{document}

I ran this code with both >> xelatex sidebearings.tex, and >> lualatex sidebearings.tex for multiple .otf, and .ttf fonts. As can be seen from the screenshots below, xelatex & lualatex return same values for .otf font Aleo available for free here, and different values for .ttf font Verdana (generally comes free as system font). In both cases xelatex is accurate, lualatex is inaccurate for .ttf files. As I observe the glyph dimensions in case of .ttf files are always 2.05ish times the correct values, though am not sure this will hold true for all fonts. Is the sidebearings.lua file missing an if statement that makes sure to divide dimensions by some factor for .ttf fonts?

a) Aleo regular (.otf file, both lualatex & xelatex are accurate): enter image description here

b) Verdana regular (.ttf file, xelatex is accurate, lualatex is inaccurate): enter image description here

1 Answer 1

4

The problem is the division by 100: The value is in font units. 1 font unit is equivalent to the fontsize divided by tfmdata.units_per_em. For OTF fonts this is normally 1000 and a fontsize of 10pt, leading to a factor 10pt/1000=1/100. Instead of hardcoding this, you get use the actual value:

--- sidebearings.lua
  packagedata                  = packagedata or { }
  packagedata.sidebearings     = { }
  local sidebearings           = packagedata.sidebearings

  local utfbyte                = utf.byte
  local texsprint              = tex.sprint

  local get_sidebearings = function (id, char)
    local tfmdata = font.getfont (id)

    if not (tfmdata and tfmdata.shared) then
      return 0, 0
    end

    local descriptions = tfmdata.shared.rawdata.descriptions
    local glyphdata    = descriptions [char]
    if not glyphdata then
      --- font lacks the glyph
      return 0, 0, 0, 0
    end

    local boundingbox   = glyphdata.boundingbox
    local lside         = boundingbox [1] or 0
    local wd            = boundingbox [3] or glyphdata.width
    local rside         = glyphdata.width - wd
    local tside         = glyphdata.height or 0
    local bside         = glyphdata.depth or 0
    local factor        = tfmdata.size / tfmdata.units_per_em

    --- If you would like to print the values to terminal
    inspect (glyphdata)

    return lside * factor, rside * factor, tside * factor, bside * factor
  end

  local sidebearings = function (id, char, side)
    char = utfbyte (char)
    local lside, rside, tside, bside = get_sidebearings (id, char)
    if side == "left" then
      texsprint (tostring (lside), "sp")
    elseif side == "right" then
      texsprint (tostring (rside), "sp")
    elseif side == "top" then
      texsprint (tostring (tside), "sp")
    elseif side == "bottom" then
      texsprint (tostring (bside), "sp")
    end
  end

  packagedata.sidebearings.left  = function (char)
    return sidebearings (font.current (), char, "left")
  end

  packagedata.sidebearings.right = function (char)
    return sidebearings (font.current (), char, "right")
  end

  packagedata.sidebearings.top = function (char)
    return sidebearings (font.current (), char, "top")
  end

  packagedata.sidebearings.bottom = function (char)
    return sidebearings (font.current (), char, "bottom")
  end
\documentclass[notitlepage,letterpaper]{article}
\usepackage[letterpaper,left=0in,right=0in,top=0in,bottom=0in]{geometry}
\usepackage{fontspec}
\usepackage{microtype}

\ifdefined \directlua

  \directlua {require "sidebearings"}

  \def \lsidebearing #1{%
    \the\dimexpr\directlua {packagedata.sidebearings.left [[#1]]}\relax%
  }

  \def \rsidebearing #1{%
    \the\dimexpr\directlua {packagedata.sidebearings.right [[#1]]}\relax%
  }

  \def \tsidebearing #1{%
    \the\dimexpr\directlua {packagedata.sidebearings.top [[#1]]}\relax%
  }

  \def \bsidebearing #1{%
    \the\dimexpr\directlua {packagedata.sidebearings.bottom [[#1]]}\relax%
  }


\else
\def \lsidebearing #1{%
\ifnum\the\XeTeXcharglyph\expandafter`#1=0
 0pt%
\else%
%\number \XeTeXglyphbounds1 \the \XeTeXcharglyph`#1 sp% %for output in unit sp
\the \XeTeXglyphbounds1 \the \XeTeXcharglyph\expandafter`#1%
\fi%
}
\def \rsidebearing #1{%
\ifnum\the\XeTeXcharglyph\expandafter`#1=0
 0pt%
\else%
\the \XeTeXglyphbounds3 \the \XeTeXcharglyph\expandafter`#1%
\fi%
}
\def \tsidebearing #1{%
\ifnum\the\XeTeXcharglyph\expandafter`#1=0
 0pt%
\else%
\the \XeTeXglyphbounds2 \the \XeTeXcharglyph\expandafter`#1%
\fi%
}
\def \bsidebearing #1{%
\ifnum\the\XeTeXcharglyph\expandafter`#1=0
 0pt%
\else%
\the \XeTeXglyphbounds4 \the \XeTeXcharglyph\expandafter`#1%
\fi%
}

\fi

\setmainfont{Verdana}% switch to Aleo-Regular when testing Aleo 
\def \test #1{[#1] left: \lsidebearing {#1}, right: \rsidebearing {#1}, bottom: \bsidebearing {#1}, top: \tsidebearing {#1}\par}

\begin{document}

\thispagestyle{plain}

\test a
\test b
\test y
\test z
\test а
\test б
\test ю
\test я

\fontname\font

\end{document}

(I can not add a screenshot because I don't have the font)

1
  • Thanks @Marcel, I confirm that your solution works!
    – codepoet
    Commented Jan 3, 2020 at 6:08

You must log in to answer this question.

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