Skip to content

Instantly share code, notes, and snippets.

@phi-gamma
Last active December 18, 2015 16:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save phi-gamma/5812290 to your computer and use it in GitHub Desktop.
Save phi-gamma/5812290 to your computer and use it in GitHub Desktop.
example code for http://tex.stackexchange.com/q/119883 (tracking missing glyphs with luaotfload)
-----------------------------------------------------------------------
-- FILE: track-missing-glyphs.lua
-- USAGE: dofile "track-missing-glyphs.lua"
-- DESCRIPTION: output a message if a font lacks a glyph for a
-- codepoint
-- REQUIREMENTS: luatex, luaotfload
-- COPYRIGHT: Hans Hagen, Pragma ADE, Hasselt NL
-- AUTHOR: Philipp Gesang (Phg), <phg42.2a@gmail.com>
-- MODIFIED: 2013-06-22 12:39:26+0200
-----------------------------------------------------------------------
--
local stringformat = string.format
local traverse_id = node.traverse_id
local utf8char = unicode.utf8.char
local fonthashes = fonts.hashes
local fontdata = fonthashes.identifiers
local chardata = characters.data
local names_report = logs.names_report
local glyph_t = nodes.nodecodes.glyph
local nullfont = 0
--- namespace
documentdata = documentdata or { }
documentdata.missing_glyphs = documentdata.missing_glyphs or { }
local missing_glyphs = documentdata.missing_glyphs
local complain = function (font_id, char)
local tfmdata = fontdata[font_id]
if tfmdata then
local fontname = tfmdata.fontname or ""
names_report("both", 0, "missing glyph",
"%d [%s] of %s", char, utf8char(char), fontname)
else
names_report("both", 0, "missing glyph",
"%d [%s] of %d", char, utf8char(char), font_id)
end
end
local fontcharacters = { }
table.setmetatableindex(fontcharacters, function (t, k)
if k == true then
return fontcharacters[currentfont()]
else
local tfmdata = fontdata[k]
if not tfmdata then --- unsafe
tfmdata = font.fonts[k]
if not (tfmdata and type (tfmdata) == "table") then
return false
end
end
local characters = tfmdata.characters
t[k] = characters
return characters
end
end)
local initialize = function ( )
local chardef = assert(kpse.find_file ("char-def.lua", "lua"),
"\nError: cannot find char-def.lua; \z
please install Context.")
dofile(chardef) --- will overwrite the partial character table
chardata = characters.data
return chardata
end
local is_character = table.tohash({ --- from char-ini.lua
"lu", "ll", "lt", "lm", "lo",
"nd", "nl", "no",
"mn",
"nl", "no",
"pc", "pd", "ps", "pe", "pi", "pf", "po",
"sm", "sc", "sk", "so"
})
local once = false --- complain only once per glyph
local missing = { } --- (font_id, glyph_id set) hash_t
local initialized = false --- track loading of char-def for older versions
local nodeprocessor = function (head)
local lastfont, characters = nil, nil
local missing = missing
for n in traverse_id(glyph_t, head) do
local font = n.font
local char = n.char
if missing[font] and missing[font][char] == true then
--- already registered
if once == false then
complain(font, char)
end
else
if font ~= lastfont and font ~= nullfont then
characters = fontcharacters[font]
end
if characters ~= false then
lastfont = font
if not characters[char] then
local category = chardata[char].category
if not category then --- old luaotfload
initialize()
category = chardata[char].category
end
if category and is_character[category] then
missing[font] = missing[font] or { }
--- could have a counter and do some stats here
missing[font][char] = true
complain(font, char)
end
end
end
end
end
return head, false
end
local active = false
local enable = function (parm)
if active == false then
if parm == "once" then
once = true
end
luatexbase.add_to_callback(
"pre_linebreak_filter", nodeprocessor, "user.missing_glyphs")
luatexbase.add_to_callback(
"hpack_filter", nodeprocessor, "user.missing_glyphs")
active = true
end
end
local disable = function ( )
if active == true then
luatexbase.remove_from_callback(
"pre_linebreak_filter", "user.missing_glyphs")
luatexbase.remove_from_callback(
"hpack_filter", "user.missing_glyphs")
once = false
active = false
end
end
documentdata.missing_glyphs.enable = enable
documentdata.missing_glyphs.disable = disable
\documentclass{article}
\usepackage{luacode,fontspec}
%% 1) initialize the tracker code (could go to separate file)
\makeatletter
\directlua{dofile "track_missing_glyphs.lua"}
%% 2) Environment start: the optional argument “once”, in square
%% brackets, requests that the missing glyph message be printed
%% only once per character and font.
\def\startreportmissingglyphs{%
\@ifnextchar[\missingglyphs@start@indeed%
{\missingglyphs@start@indeed[]}%
}
\def\missingglyphs@start@indeed[#1]{%
\directlua{documentdata.missing_glyphs.enable"\luaescapestring{#1}"}%
}
%% 3) Environment stop: we need to force a \par here to
%% have the callback apply to the current paragraph.
\def\stopreportmissingglyphs{%
\endgraf %% paragraph-based callback!
\directlua{documentdata.missing_glyphs.disable()}%
}
\makeatother
%% Usage examples.
\begin{document}
%% Latin modern lacks glyphs for the Greek script so we use that for
%% testing.
\startreportmissingglyphs
Program the μC, please.
%% Works in math mode (different font model) as well.
$f = ma$
\stopreportmissingglyphs
lorem schmipsum
\startreportmissingglyphs[once]
%% With the “once” flag, no message is emitted for repetitions of
%% missing chars.
Θάλαττα, θάλαττα.
\stopreportmissingglyphs
\end{document}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment