Skip to content

Instantly share code, notes, and snippets.

@pgundlach
Created April 3, 2010 19:56
Show Gist options
  • Save pgundlach/354808 to your computer and use it in GitHub Desktop.
Save pgundlach/354808 to your computer and use it in GitHub Desktop.
Fontloader für LuaTeX (http://www.luatex.de)
\pdfadjustspacing=2
\directlua{
dofile("fontloader.lua")
}
\def\ladefont#1#2{\directlua{
local ok,f = define_font("#1",65536 * 12)
if ok then
local num = font.define(f)
tex.definefont("#2",num)
else
texio.write_nl(f)
end
}}
\def\beispieltext{Hallo Welt aäÄ oöÖ uüÜ ß ⌀ ℃ -- Va --- fl fi ffi fk ffl}
\ladefont{texgyreheros-regular.otf}{TeXgyreherosRegular}
\ladefont{texgyreheros-bold.otf}{TeXgyreherosBold}
\ladefont{lmr10.pfb}{LatinModern}
\TeXgyreherosRegular
\beispieltext
\TeXgyreherosBold
\beispieltext
\LatinModern
\beispieltext
\bye
-- Gibt "truetype", "opentype" oder "type1" zurück, je nach Endung von "dateiname".
-- Wenn der Typ nicht ermittelt werden kann, wird nil zurückgegeben.
function guess_fonttype( dateiname )
local f=dateiname:lower()
if f:match(".*%.ttf") then return "truetype"
elseif f:match(".*%.otf") then return "opentype"
elseif f:match(".*%.pfb") then return "type1"
else return nil
end
end
function to_utf16(codepoint)
assert(codepoint)
if codepoint < 65536 then
return string.format("%04X",codepoint)
else
return string.format("%04X%04X",codepoint / 1024 + 0xD800 ,codepoint % 1024 + 0xDC00)
end
end
-- "name" ist der Dateiname, "size" ist eine Zahl in scaled points (sp)
-- Rückkabe sind zwei Werte. Wenn der erste Wert "false" ist, dann ist im zweiten Wert
-- eine Fehlermeldung, wenn der erste Wert "true" ist, dann ist im zweiten Wert eine
-- TeX-Tabelle mit dem Font.
function define_font(name, size)
local lookup_codepoint_by_name = {}
local lookup_codepoint_by_number = {}
local dateiname_mit_pfad = kpse.lookup(name)
if not dateiname_mit_pfad then return false, string.format("Fontdatei '%s' nicht gefunden.",dateiname_mit_pfad or name) end
local fontinfo = fontloader.to_table(fontloader.open(dateiname_mit_pfad))
if fontinfo == nil then return false, string.format("Problem beim Laden des Fonts '%s'",tostring(dateiname_mit_pfad)) end
fontinfo.dateiname_mit_pfad = dateiname_mit_pfad
local is_unicode = (fontinfo.pfminfo.unicoderanges ~= nil)
-- Es wird ein Mapping Zeichennummer -> codepoint benötigt, damit wir beim Durchgehen der
-- Zeichen die direkt an die richtige Stelle (codepoint) geben können.
-- Das Problem ist, dass TTF/OTF und Type1 unterschiedlich behandelt werden müssen.
-- TTF/OTF haben ein Unicode Mapping, das mit map.backmap (key: glyph, value: Codepoint)
-- durchgegangen werden kann. Type1 benötigt die Information aus glyph.unicode.
-- Ebenso wird fürs Kerning ein Mapping Zeichenname -> codepoint benötigt.
if is_unicode then
-- TTF/OTF, benutze map.backmap
for i = 1,#fontinfo.glyphs do
local g=fontinfo.glyphs[i]
local cp = fontinfo.map.backmap[i]
lookup_codepoint_by_name[g.name] = cp
lookup_codepoint_by_number[i] = cp
end
else
-- Type1, benutze glyph.unicode
for i = 1,#fontinfo.glyphs do
local g=fontinfo.glyphs[i]
local cp = g.unicode
lookup_codepoint_by_name[g.name] = cp
lookup_codepoint_by_number[i] = cp
end
end -- is unicode
if (size < 0) then size = (- 655.36) * size end
if fontinfo.units_per_em == 0 then fontinfo.units_per_em = 1000 end -- manche type1 fonts haben u_p_em=0
local mag = size / fontinfo.units_per_em -- magnification
local f = { } -- Fontstruktur für TeX (Kap. 7 LuaTeX)
f.characters = { } -- alle Zeichen für TeX, Index ist der Unicode Codepoint
f.fontloader = fontinfo
f.name = fontinfo.fontname
f.fullname = fontinfo.fontname
f.designsize = size
f.size = size
f.direction = 0
f.filename = fontinfo.dateiname_mit_pfad
f.type = 'real'
f.encodingbytes = 2
f.tounicode = 1
f.stretch = 30
f.shrink = 20
f.step = 10
f.auto_expand = true
f.parameters = { }
f.parameters.slant = 0
f.parameters.space = 0.25 * size
f.parameters.space_stretch = 0.3 * size
f.parameters.space_shrink = 0.1 * size
f.parameters.x_height = 0.4 * size
f.parameters.quad = 1.0 * size
f.parameters.extra_space = 0
f.format = guess_fonttype(name)
if f.format==nil then return false,"Konnte Fontformat der Datei '".. fontinfo.dateiname_mit_pfad .."' nicht bestimmen." end
f.embedding = "subset"
f.cidinfo = fontinfo.cidinfo
-- Hier stehen die Schreibsysteme (scripts) und Sprachen, für die die Ligaturen gesucht werden sollen.
local scripts_OK = { DFLT = true, latn = true }
local langs_OK = { dflt = true }
local lookups = {}
if fontinfo.gsub then
for i=1,#fontinfo.gsub do
local gsub = fontinfo.gsub[i]
for j=1,#gsub.features do
local features = gsub.features[j]
if features.tag =="liga" then
for k=1,#features.scripts do
local script_tbl = features.scripts[k]
if scripts_OK[script_tbl.script] then
-- die richtigen "scripts" haben wir gefunden, jetzt kommt es noch
-- auf die richtige Sprache an.
for l=1,#script_tbl.langs do
local lang = script_tbl.langs[l]
if langs_OK[lang] then
lookups[gsub.subtables[1].name] = true
end
end
end
end
end
end
end
end
ligatures = {}
for i=1,#fontinfo.glyphs do
local glyph = fontinfo.glyphs[i]
local codepoint = lookup_codepoint_by_number[i]
if glyph.lookups then
for k,v in pairs(glyph.lookups) do
if lookups[k] then
for i,w in ipairs(v) do
ligatures[#ligatures + 1] = w
end
end
end
end
-- TeX benutzt U+002D HYPHEN-MINUS als Trennstrich, korrekt wäre U+2010 HYPHEN. Da
-- aber die Fonts unberechenbar sind, mappen wir alle HYPHEN auf 0x2D (dez. 45)
if glyph.name:lower():match("^hyphen$") then codepoint=45 end
f.characters[codepoint] = {
index = i,
width = glyph.width * mag,
name = glyph.name,
expansion_factor = 1000,
}
-- Höhe und Tiefe des Zeichens
if glyph.boundingbox[4] then f.characters[codepoint].height = glyph.boundingbox[4] * mag end
if glyph.boundingbox[2] then f.characters[codepoint].depth = -glyph.boundingbox[2] * mag end
-- tounicode setzen. Damit bei Kapitälchen etc. auch copy und paste funktioniert. Strenggenommen
-- benötigen wir die Funktionalität bei diesem einfachen Fontloader nicht (keine OTF-Features)
if glyph.name:match("%.") then
-- Bsp: Kapitälchen a hat a.sc oder a.c2sc als Name. Wir interessieren uns nur für den Teil vor dem Punkt.
-- ziemlich einfache Variante, aber es scheint ganz gut zu funktionieren
local destname = glyph.name:gsub("^([^%.]*)%..*$","%1")
local cp = lookup_codepoint_by_name[destname]
if cp then
f.characters[codepoint].tounicode=to_utf16(cp)
end
end
-- Optischer Randausgleich, dazu muss \pdfprotrudechars=2 eingeschaltet sein. Hier nur als Beispiel.
local faktor = 0.5
if (glyph.name=="hyphen" or glyph.name=="period" or glyph.name=="comma") then
f.characters[codepoint]["right_protruding"] = glyph.width * faktor
end
-- Kerning
local kerns={}
if glyph.kerns then
for _,kern in pairs(glyph.kerns) do
local ziel = lookup_codepoint_by_name[kern.char]
if ziel and ziel > 0 then
kerns[ziel] = kern.off * mag
else
end
end
end
f.characters[codepoint].kerns = kerns
end
-- Zu diesem Zeitpunkt sind alle Zeichen bekannt, nun können die gewünschten Ligaturen
-- in diese Zeichen eingefügt werden.
for _,v in ipairs(ligatures) do
local spec = v.specification
local result_cp = lookup_codepoint_by_name[spec.char]
if result_cp > 0 then -- -1 == unencoded
local components = string.explode(spec.components) -- z.B. "ff i"
if #components == 2 then -- wir behandeln nur Ligaturen mit 2 Komponenten
local char = f.characters[lookup_codepoint_by_name[components[1]]]
char.ligatures = char.ligatures or {}
char.ligatures[lookup_codepoint_by_name[components[2]]] = { char = result_cp }
end
end
end
-- Ligaturen sind ergänzt worden
return true,f
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment