Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

This question has been solved, in XeTeX, by this code:

\def\kright#1{\leavevmode #1\kern-\XeTeXglyphbounds3 \the\XeTeXcharglyph`#1 }
\def\kleft#1{\leavevmode \kern-\XeTeXglyphbounds1 \the\XeTeXcharglyph`#1 #1}

which allows me to align text more precisely to the ink by accessing the side-bearing measurements (\XeTeXglyphbounds1 and 3).

This XeTeX dependency is currently the only thing stopping me from moving to LuaTeX (mainly for microtype). Is an equivalent function available in LuaTeX, or planned?


After reading a bit more on microtype, could the equivalent effect be achieved by an appropriately-tailored set of character protrusions? That way, my move to LuaTeX might be a lot simpler.

share|improve this question

2 Answers 2

up vote 6 down vote accepted

While preparing a font for Luatex, the fontloader also sets up some extra tables inside the font structure that contain the unprocessed data as returned by fontloader.open(). Among these is the glyph data: For every glyph in the font there is some additional information that is not contained in the characters array since it is unnecessary for Luatex to render a font. For instance, this is a dump of the information about the glyph a:

table={
 ["boundingbox"]={ 45, -10, 432, 460 },
 ["depth"]=10,
 ["height"]=460,
 ["index"]=9,
 ["name"]="a",
 ["slookups"]={
  ["as_l_1_s"]={ 63209, 63329 },
  ["ss_cyrl_l_18_s"]=63209,
  ["ss_cyrl_l_44_s"]=63329,
  ["ss_grek_l_17_s"]=63209,
  ["ss_grek_l_43_s"]=63329,
  ["ss_l_16_s"]=63209,
  ["ss_l_37_s"]=63209,
  ["ss_l_42_s"]=63329,
 },
 ["width"]=512,
}

There we find everything we need to calculate the sidebearings in the boundingbox array: The first value is the left sidebearing. The third value is the bbox width. (Dimensions are in hundredths of a TeX point.)

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
  end

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

  inspect (glyphdata)

  return lside / 100, rside /100
end

local sidebearings = function (id, char, left)
  char = utfbyte (char)
  local lside, rside = get_sidebearings (id, char)
  if left then
    texsprint (tostring (lside), "pt")
  else
    texsprint (tostring (rside), "pt")
  end
end

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

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

Now we can wrap those functions on the TeX end like so:

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

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

These can be used just like \XeTeXglyphbounds with the argument 1 and 3 respectively. For comparison, here is a test file that runs with both Xetex and Luatex:

\ifdefined \directlua
  \input luaotfload.sty
  \directlua {require "sidebearings"}

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

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

  \font \mainfont = "file:Iwona-Regular.otf"

\else
  \def \lsidebearing #1{\the \XeTeXglyphbounds1 \the \XeTeXcharglyph`#1}
  \def \rsidebearing #1{\the \XeTeXglyphbounds3 \the \XeTeXcharglyph`#1}
  \font \mainfont = "[Iwona-Regular.otf]"
\fi

\mainfont

\def \test #1{[#1] left: \lsidebearing {#1}, right: \rsidebearing {#1}\par}

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

\bye

Typeset result

Convenience gist of the files.

share|improve this answer

Yes and no. When you load the font via the built-in fontloader library (which is very low level), you can access all the information of the font file. This includes the bounding box, as far as I can see (look at the section 4.4.5.1.2 "Glyph items" in the LuaTeX reference manual).

But normally you load the fonts via the fontspec / luaotfload packages, which discard that information and only keep what is needed for TeX.

I doubt that inclusion of that information is planned in the luaotfload package, which is based on ConTeXt's font loader.

share|improve this answer
    
Thanks, Patrick. A couple of supplementaries: (1) it looks rather "overloaded" for what I want to do; (2) I'm definitely a fontspec person; and (3) it only has data for left side-bearings, and then "only if nonzero and not equal to bounding box", which leads me to equate the bounding box with the TeX character box, which is definitely what I don't want -- I need to get close to the actual ink. –  Brent.Longborough Feb 23 '12 at 19:06
1  
@Brent.Longborough The bounding box should be the real "tight" box. It is calculated from the actual outlines of the fonts if the font does not contain the bbox info. –  topskip Feb 23 '12 at 20:08
    
I'm sure the bounding box is kept, but you may be looking at the wrong place (too late now, I'll try to find a definite answer in the morning if there wasn’t one by then). –  Khaled Hosny Feb 23 '12 at 21:40
1  
@Brent.Longborough: the side bearings can be seen as the differences between glyphs bounding box and the box TeX sees. –  Khaled Hosny Feb 23 '12 at 21:42
1  
Sorry for not providing an answer as promised, but you can get the bounding box from fonts.identifiers[id].shared.otfdata.descriptions[gid].boundingbox where id is the font number and gid is glyph id. –  Khaled Hosny Feb 26 '12 at 23:30

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.