[luatex] [lltx] Patching fonts with the luaotfload callback "luaotfload.patch_font"

Philipp Stephani st_philipp at yahoo.de
Sat Feb 5 21:31:46 CET 2011

Am 05.02.2011 um 15:28 schrieb Ulrike Fischer:

> I'm really happy that the newest version of luaotfload offers a
> callback "luaotfload.patch_font" which can be use to change fonts. I
> think that is really useful. As a first step I would like to use it
> to reencode some chess fonts so that they can be used with latex.
> Below is a small example of my first tests. It tries to replace the
> "a" by a "b". For all fonts in the example I get the expected "look"
> in the pdf ("bbc").
> I have two questions:
> 1. I added some messages to the function (for diagnosing), but they
> don't appear for all fonts in the log/terminal. I see messages from
> the fontspec-fonts "The font is \EU2/lmr/m/it/10" but not from
> "\font\mytest" and "\font\mytestb". The curious thing is that I get
> a "The font is \mytest"-message if I uncomment the last fontspec, I
> can't find a rule, why some messages appear and other not. Has
> someone an idea?

Font loading happens *before* the font is assigned to a font definition token, and before selecting the font. `font.current()`, however, returns the number of the most recently selected font, which is never the font being loaded (because TeX simply doesn't know the latter yet). You can test that by comparing the requested with the current font:

local function patch_font(fontdata)
 texio.write_nl("The font is " .. tex.fontidentifier(font.current()) .. " while loading " .. fontdata.name)

This gives:

The font is \EU2/lmr/m/n/10 while loading Georgia  % while executing \font\mytest=Georgia; the default font (Latin Modern Roman) is the current one
The font is EU2/lmr/m/n/10 while loading file:lmroman10-italic:script=latn;+trep;+tlig;  % while executing \itshape; LM Roman is still selected
The font is \EU2/lmr/m/it/10 while loading Arial  % while \font\mytestb="Arial"; LM Italic is the current font

> 2. For the first test I simply set 
> fontdata.characters[0x0061]=fontdata.characters[0x0062]. 
> But I'm really unsure which table(s) should be copied how.

That line doesn't copy anything, it just has char 0x61 point to the same table as char 0x62.

> I think I
> copied to much: When I copy&paste the output in the pdf I get "aac
> aac aac". This means my code also affected the input "b" - something
> I don't want.
> What I want is
> - only the input "a" should be affected.
> - its output in the pdf should be "b" (with correct width and heigth
> etc
> - it should copy&paste as "a" or - better - as a completly different
> character e.g. "d".

You have to set the `tounicode` table entry:

 fontdata.tounicode = 1
 local a = { }
 local b = fontdata.characters[0x0062]
 local c = fontdata.characters[0x0063]
 for k, v in pairs(b) do
   a[k] = v
 a.tounicode = "0064"
 b.tounicode = "0062"
 c.tounicode = "0063"
 fontdata.characters[0x0061] = a

This doesn't work as expected (you still get "ddc" instead of "dbc"), so I'm CC'ing Taco.

I think for your problem you should prefer virtual fonts over font patching. I requested the addition of the patch_font callback to be able to fix minor bugs (e.g. a wrong math parameter in Cambria Math) during font loading. Remapping characters should probably be done by means of virtual fonts:

local real_id = font.id("realfont")
local real_data = font.getfont(real_id)
local virtual_data = {
  type = "virtual",
  name = "test",
  tounicode = 1,
  fonts = {
    { id = font.id("realfont"), size = 0xA0000 }
  characters = { }
for k, v in pairs(real_data.characters) do
  local i, c, u
  if k == 0x0061 then
    i = 0x0062
    c = real_data.characters[i]
    u = "0064"
    i = k
    c = v
    u = c.tounicode or (i < 0xFFFF and string.format("%04X", i))
  virtual_data.characters[k] = {
    width = c.width,
    height = c.height,
    depth = c.depth,
    tounicode = u,
    commands = {
      { "char", i }
local virtual_id = font.define(virtual_data)
function select_virtual_font()

The tounicode entries still don't work as expected; hopefully Taco can enlighten us.

More information about the luatex mailing list