diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/font-afm.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/font-afm.lua | 941 |
1 files changed, 523 insertions, 418 deletions
diff --git a/Master/texmf-dist/tex/context/base/font-afm.lua b/Master/texmf-dist/tex/context/base/font-afm.lua index fd931547233..81b47a2698c 100644 --- a/Master/texmf-dist/tex/context/base/font-afm.lua +++ b/Master/texmf-dist/tex/context/base/font-afm.lua @@ -17,18 +17,27 @@ where we handles font encodings. Eventually font encoding goes away.</p> --ldx]]-- -fonts = fonts or { } -fonts.afm = fonts.afm or { } -fonts.afm.version = 1.26 -- incrementing this number one up will force a re-cache -fonts.afm.syncspace = true -- when true, nicer stretch values -fonts.afm.enhance_data = true -- best leave this set to true -fonts.afm.trace_features = false -fonts.afm.features = { } -fonts.afm.features.aux = { } -fonts.afm.features.data = { } -fonts.afm.features.list = { } -fonts.afm.features.default = { } -fonts.afm.cache = containers.define("fonts", "afm", fonts.afm.version, true) +local trace_features = false trackers.register("afm.features", function(v) trace_features = v end) +local trace_indexing = false trackers.register("afm.indexing", function(v) trace_indexing = v end) +local trace_loading = false trackers.register("afm.loading", function(v) trace_loading = v end) + +local format, match, gmatch, lower = string.format, string.match, string.gmatch, string.lower + +fonts = fonts or { } +fonts.afm = fonts.afm or { } + +local afm = fonts.afm +local tfm = fonts.tfm + +afm.version = 1.400 -- incrementing this number one up will force a re-cache +afm.syncspace = true -- when true, nicer stretch values +afm.enhance_data = true -- best leave this set to true +afm.features = { } +afm.features.aux = { } +afm.features.data = { } +afm.features.list = { } +afm.features.default = { } +afm.cache = containers.define("fonts", "afm", afm.version, true) --[[ldx-- <p>We start with the basic reader which we give a name similar to the @@ -51,199 +60,197 @@ built in <l n='tfm'/> and <l n='otf'/> reader.</p> --~ Comment DELIM 2390 1010 --~ Comment AXISHEIGHT 250 -do - - local c = lpeg.P("Comment") - local s = lpeg.S(" \t") - local l = lpeg.S("\n\r") - local w = lpeg.C((1 - l)^1) - local n = lpeg.C((lpeg.R("09") + lpeg.S("."))^1) / tonumber * s^0 - - local fd = { } - - local pattern = ( c * s^1 * ( - ("CODINGSCHEME" * s^1 * w ) / function(a) end + - ("DESIGNSIZE" * s^1 * n * w ) / function(a) fd[ 1] = a end + - ("CHECKSUM" * s^1 * n * w ) / function(a) fd[ 2] = a end + - ("SPACE" * s^1 * n * "plus" * n * "minus" * n) / function(a,b,c) fd[ 3], fd[ 4], fd[ 5] = a, b, c end + - ("QUAD" * s^1 * n ) / function(a) fd[ 6] = a end + - ("EXTRASPACE" * s^1 * n ) / function(a) fd[ 7] = a end + - ("NUM" * s^1 * n * n * n ) / function(a,b,c) fd[ 8], fd[ 9], fd[10] = a, b, c end + - ("DENOM" * s^1 * n * n ) / function(a,b ) fd[11], fd[12] = a, b end + - ("SUP" * s^1 * n * n * n ) / function(a,b,c) fd[13], fd[14], fd[15] = a, b, c end + - ("SUB" * s^1 * n * n ) / function(a,b) fd[16], fd[17] = a, b end + - ("SUPDROP" * s^1 * n ) / function(a) fd[18] = a end + - ("SUBDROP" * s^1 * n ) / function(a) fd[19] = a end + - ("DELIM" * s^1 * n * n ) / function(a,b) fd[20], fd[21] = a, b end + - ("AXISHEIGHT" * s^1 * n ) / function(a) fd[22] = a end + - (1-l)^0 - ) + (1-c)^1)^0 - - function fonts.afm.scan_comment(str) - fd = { } - pattern:match(str) - return fd - end - +local c = lpeg.P("Comment") +local s = lpeg.S(" \t") +local l = lpeg.S("\n\r") +local w = lpeg.C((1 - l)^1) +local n = lpeg.C((lpeg.R("09") + lpeg.S("."))^1) / tonumber * s^0 + +local fd = { } + +local pattern = ( c * s^1 * ( + ("CODINGSCHEME" * s^1 * w ) / function(a) end + + ("DESIGNSIZE" * s^1 * n * w ) / function(a) fd[ 1] = a end + + ("CHECKSUM" * s^1 * n * w ) / function(a) fd[ 2] = a end + + ("SPACE" * s^1 * n * "plus" * n * "minus" * n) / function(a,b,c) fd[ 3], fd[ 4], fd[ 5] = a, b, c end + + ("QUAD" * s^1 * n ) / function(a) fd[ 6] = a end + + ("EXTRASPACE" * s^1 * n ) / function(a) fd[ 7] = a end + + ("NUM" * s^1 * n * n * n ) / function(a,b,c) fd[ 8], fd[ 9], fd[10] = a, b, c end + + ("DENOM" * s^1 * n * n ) / function(a,b ) fd[11], fd[12] = a, b end + + ("SUP" * s^1 * n * n * n ) / function(a,b,c) fd[13], fd[14], fd[15] = a, b, c end + + ("SUB" * s^1 * n * n ) / function(a,b) fd[16], fd[17] = a, b end + + ("SUPDROP" * s^1 * n ) / function(a) fd[18] = a end + + ("SUBDROP" * s^1 * n ) / function(a) fd[19] = a end + + ("DELIM" * s^1 * n * n ) / function(a,b) fd[20], fd[21] = a, b end + + ("AXISHEIGHT" * s^1 * n ) / function(a) fd[22] = a end + + (1-l)^0 +) + (1-c)^1)^0 + +local function scan_comment(str) + fd = { } + pattern:match(str) + return fd end -do - - -- On a rainy day I will rewrite this in lpeg ... - - local keys = { } - - function keys.FontName (data,line) data.fullname = line:strip() end - function keys.ItalicAngle (data,line) data.italicangle = tonumber (line) end - function keys.IsFixedPitch(data,line) data.isfixedpitch = toboolean(line,true) end - function keys.CharWidth (data,line) data.charwidth = tonumber (line) end - function keys.XHeight (data,line) data.xheight = tonumber (line) end - function keys.Descender (data,line) data.descender = tonumber (line) end - function keys.Ascender (data,line) data.ascender = tonumber (line) end - function keys.Comment (data,line) - -- Comment DesignSize 12 (pts) - -- Comment TFM designsize: 12 (in points) - line = line:lower() - local designsize = line:match("designsize[^%d]*(%d+)") - if designsize then data.designsize = tonumber(designsize) end - end +-- On a rainy day I will rewrite this in lpeg ... + +local keys = { } + +function keys.FontName (data,line) data.metadata.fullname = line:strip() end +function keys.ItalicAngle (data,line) data.metadata.italicangle = tonumber (line) end +function keys.IsFixedPitch(data,line) data.metadata.isfixedpitch = toboolean(line,true) end +function keys.CharWidth (data,line) data.metadata.charwidth = tonumber (line) end +function keys.XHeight (data,line) data.metadata.xheight = tonumber (line) end +function keys.Descender (data,line) data.metadata.descender = tonumber (line) end +function keys.Ascender (data,line) data.metadata.ascender = tonumber (line) end +function keys.Comment (data,line) + -- Comment DesignSize 12 (pts) + -- Comment TFM designsize: 12 (in points) + line = lower(line) + local designsize = match(line,"designsize[^%d]*(%d+)") + if designsize then data.metadata.designsize = tonumber(designsize) end +end - local function get_charmetrics(data,charmetrics,vector) - local characters = data.characters - local chr, str, ind = { }, "", 0 - for k,v in charmetrics:gmatch("([%a]+) +(.-) *;") do - if k == 'C' then - if str ~= "" then characters[str] = chr end - chr = { } - str = "" - v = tonumber(v) - if v < 0 then - ind = ind + 1 - else - ind = v - end - chr.index = ind - elseif k == 'WX' then - chr.width = v - elseif k == 'N' then - str = v - elseif k == 'B' then - local llx, lly, urx, ury = v:match("^ *(.-) +(.-) +(.-) +(.-)$") - chr.boundingbox = { tonumber(llx), tonumber(lly), tonumber(urx), tonumber(ury) } - elseif k == 'L' then - local plus, becomes = v:match("^(.-) +(.-)$") - if not chr.ligatures then chr.ligatures = { } end - chr.ligatures[plus] = becomes +local function get_charmetrics(data,charmetrics,vector) + local characters = data.characters + local chr, str, ind = { }, "", 0 + for k,v in gmatch(charmetrics,"([%a]+) +(.-) *;") do + if k == 'C' then + if str ~= "" then characters[str] = chr end + chr = { } + str = "" + v = tonumber(v) + if v < 0 then + ind = ind + 1 + else + ind = v end - end - if str ~= "" then - characters[str] = chr + chr.index = ind + elseif k == 'WX' then + chr.width = v + elseif k == 'N' then + str = v + elseif k == 'B' then + local llx, lly, urx, ury = match(v,"^ *(.-) +(.-) +(.-) +(.-)$") + chr.boundingbox = { tonumber(llx), tonumber(lly), tonumber(urx), tonumber(ury) } + elseif k == 'L' then + local plus, becomes = match(v,"^(.-) +(.-)$") + if not chr.ligatures then chr.ligatures = { } end + chr.ligatures[plus] = becomes end end + if str ~= "" then + characters[str] = chr + end +end - local function get_kernpairs(data,kernpairs) - local characters = data.characters - for one, two, value in kernpairs:gmatch("KPX +(.-) +(.-) +(.-)\n") do - local chr = characters[one] - if chr then - if not chr.kerns then chr.kerns = { } end - chr.kerns[two] = tonumber(value) - end +local function get_kernpairs(data,kernpairs) + local characters = data.characters + for one, two, value in gmatch(kernpairs,"KPX +(.-) +(.-) +(.-)\n") do + local chr = characters[one] + if chr then + if not chr.kerns then chr.kerns = { } end + chr.kerns[two] = tonumber(value) end end +end - local function get_variables(data,fontmetrics) - for key, rest in fontmetrics:gmatch("(%a+) *(.-)[\n\r]") do - if keys[key] then keys[key](data,rest) end - end +local function get_variables(data,fontmetrics) + for key, rest in gmatch(fontmetrics,"(%a+) *(.-)[\n\r]") do + if keys[key] then keys[key](data,rest) end end +end - local function get_indexes(data,filename) - local trace = fonts.trace - local pfbname = input.find_file(texmf.instance,file.removesuffix(file.basename(filename))..".pfb","pfb") or "" - if pfbname ~= "" then - data.luatex = data.luatex or { } - data.luatex.filename = pfbname - local pfbblob = fontforge.open(pfbname) - if pfbblob then - local characters = data.characters - local pfbdata = fontforge.to_table(pfbblob) - --~ print(table.serialize(pfbdata)) - if pfbdata then - local glyphs = pfbdata.glyphs - if glyphs then - if trace then - logs.report("load afm", string.format("getting index data from %s",pfbname)) - end - -- local offset = (glyphs[0] and glyphs[0] != .notdef) or 0 - for index, glyph in pairs(glyphs) do - local name = glyph.name - if name then - local char = characters[name] - if char then - if trace then - logs.report("load afm", string.format("glyph %s has index %s",name,index)) - end - char.index = index +local function get_indexes(data,filename) + local pfbfile = file.replacesuffix(filename,"pfb") + local pfbname = resolvers.find_file(pfbfile,"pfb") or "" + if pfbname == "" then + pfbname = resolvers.find_file(file.basename(pfbfile),"pfb") or "" + end + if pfbname ~= "" then + data.luatex.filename = pfbname + local pfbblob = fontloader.open(pfbname) + if pfbblob then + local characters = data.characters + local pfbdata = fontloader.to_table(pfbblob) + --~ print(table.serialize(pfbdata)) + if pfbdata then + local glyphs = pfbdata.glyphs + if glyphs then + if trace_loading then + logs.report("load afm","getting index data from %s",pfbname) + end + -- local offset = (glyphs[0] and glyphs[0] != .notdef) or 0 + for index, glyph in next, glyphs do + local name = glyph.name + if name then + local char = characters[name] + if char then + if trace_indexing then + logs.report("load afm","glyph %s has index %s",name,index) end + char.index = index end end - elseif trace then - logs.report("load afm", string.format("no glyph data in pfb file %s",pfbname)) end - elseif trace then - logs.report("load afm", string.format("no data in pfb file %s",pfbname)) + elseif trace_loading then + logs.report("load afm","no glyph data in pfb file %s",pfbname) end - elseif trace then - logs.report("load afm", string.format("invalid pfb file %s",pfbname)) + elseif trace_loading then + logs.report("load afm","no data in pfb file %s",pfbname) end - elseif trace then - logs.report("load afm", string.format("no pfb file for %s",filename)) + fontloader.close(pfbblob) + elseif trace_loading then + logs.report("load afm","invalid pfb file %s",pfbname) end + elseif trace_loading then + logs.report("load afm","no pfb file for %s",filename) end +end - function fonts.afm.read_afm(filename) - local ok, afmblob, size = input.loadbinfile(texmf.instance,filename) -- has logging - -- local ok, afmblob = true, file.readdata(filename) - if ok and afmblob then - local data = { - version = version or '0', - characters = { }, +function afm.read_afm(filename) + local ok, afmblob, size = resolvers.loadbinfile(filename) -- has logging +-- local ok, afmblob = true, file.readdata(filename) + if ok and afmblob then + local data = { + characters = { }, + metadata = { + version = version or '0', -- hm filename = file.removesuffix(file.basename(filename)) } - afmblob = afmblob:gsub("StartCharMetrics(.-)EndCharMetrics", function(charmetrics) - if fonts.trace then - logs.report("load afm", "loading char metrics") - end - get_charmetrics(data,charmetrics,vector) - return "" - end) - afmblob = afmblob:gsub("StartKernPairs(.-)EndKernPairs", function(kernpairs) - if fonts.trace then - logs.report("load afm", "loading kern pairs") - end - get_kernpairs(data,kernpairs) - return "" - end) - afmblob = afmblob:gsub("StartFontMetrics%s+([%d%.]+)(.-)EndFontMetrics", function(version,fontmetrics) - if fonts.trace then - logs.report("load afm", "loading variables") - end - data.afmversion = version - get_variables(data,fontmetrics) - data.fontdimens = fonts.afm.scan_comment(fontmetrics) -- todo: all lpeg, no time now - return "" - end) - get_indexes(data,filename) - return data - else - if fonts.trace then - logs.report("load afm", "no valid afm file " .. filename) + } + afmblob = afmblob:gsub("StartCharMetrics(.-)EndCharMetrics", function(charmetrics) + if trace_loading then + logs.report("load afm","loading char metrics") + end + get_charmetrics(data,charmetrics,vector) + return "" + end) + afmblob = afmblob:gsub("StartKernPairs(.-)EndKernPairs", function(kernpairs) + if trace_loading then + logs.report("load afm","loading kern pairs") + end + get_kernpairs(data,kernpairs) + return "" + end) + afmblob = afmblob:gsub("StartFontMetrics%s+([%d%.]+)(.-)EndFontMetrics", function(version,fontmetrics) + if trace_loading then + logs.report("load afm","loading variables") end - return nil + data.afmversion = version + get_variables(data,fontmetrics) + data.fontdimens = scan_comment(fontmetrics) -- todo: all lpeg, no time now + return "" + end) + data.luatex = { } + get_indexes(data,filename) + return data + else + if trace_loading then + logs.report("load afm","no valid afm file %s",filename) end + return nil end - end --[[ldx-- @@ -252,56 +259,84 @@ by adding ligatures and kern information to the afm derived data. That way we can set them faster when defining a font.</p> --ldx]]-- -function fonts.afm.load(filename) - local name = file.removesuffix(filename) - local data = containers.read(fonts.afm.cache(),name) - local size = lfs.attributes(name,"size") or 0 - if data and data.size ~= size then - data = nil - end - if not data then - local foundname = input.find_file(texmf.instance,filename,'afm') - if foundname and foundname ~= "" then - data = fonts.afm.read_afm(foundname) +function afm.load(filename) + -- hm, for some reasons not resolved yet + filename = resolvers.find_file(filename,'afm') or "" + if filename ~= "" then + local name = file.removesuffix(file.basename(filename)) + local data = containers.read(afm.cache(),name) + local size = lfs.attributes(filename,"size") or 0 + if not data or data.verbose ~= fonts.verbose or data.size ~= size then + logs.report("load afm", "reading %s",filename) + data = afm.read_afm(filename) if data then - fonts.afm.unify(data,filename) - if fonts.afm.enhance_data then - fonts.afm.add_ligatures(data,'ligatures') -- easier this way - fonts.afm.add_ligatures(data,'texligatures') -- easier this way - fonts.afm.add_kerns(data) -- faster this way + -- data.luatex = data.luatex or { } + logs.report("load afm", "unifying %s",filename) + afm.unify(data,filename) + if afm.enhance_data then + logs.report("load afm", "add ligatures") + afm.add_ligatures(data,'ligatures') -- easier this way + logs.report("load afm", "add tex-ligatures") + afm.add_ligatures(data,'texligatures') -- easier this way + logs.report("load afm", "add extra kerns") + afm.add_kerns(data) -- faster this way end - logs.report("load afm","file size: " .. size) data.size = size - logs.report("load afm","saving: in cache") - data = containers.write(fonts.afm.cache(), name, data) + data.verbose = fonts.verbose + logs.report("load afm","saving: %s in cache",name) + data = containers.write(afm.cache(), name, data) + data = containers.read(afm.cache(),name) end end + return data + else + return nil end - return data end -function fonts.afm.unify(data, filename) ---~ local unicode, unicodes, private = fonts.enc.load('unicode').hash, { }, 0x0F0000 - local unicode, unicodes, private = fonts.enc.load('unicode').hash, { }, fonts.private - for name, blob in pairs(data.characters) do - local code = unicode[name] -- or characters.name_to_unicode[name] +function afm.unify(data, filename) + local unicodevector = fonts.enc.load('unicode').hash + local glyphs, indices, unicodes, names = { }, { }, { }, { } + local verbose, private = fonts.verbose, fonts.private + for name, blob in next, data.characters do + local code = unicodevector[name] -- or characters.name_to_unicode[name] if not code then - local u = name:match("^uni(%x+)$") + local u = match(name,"^uni(%x+)$") code = u and tonumber(u,16) if not code then code = private private = private + 1 - logs.report("afm glyph", string.format("assigning private slot 0x%04X for unknown glyph name %s", code, name)) + logs.report("afm glyph", "assigning private slot U+%04X for unknown glyph name %s", code, name) end end - blob.unicode = code + local index = blob.index unicodes[name] = code + indices[code] = index + glyphs[index] = blob + names[name] = index + blob.name = name + if verbose then + local bu = blob.unicode + if not bu then + blob.unicode = code + elseif type(bu) == "table" then + bu[#bu+1] = code + else + blob.unicode = { bu, code } + end + else + blob.index = nil + end end - data.luatex = { - filename = file.basename(filename), - -- version = fonts.afm.version, - unicodes = unicodes - } + data.glyphs = glyphs + data.characters = nil + local luatex = data.luatex + luatex.filename = luatex.filename or file.removesuffix(file.basename(filename)) + luatex.unicodes = unicodes -- name to unicode + luatex.indices = indices -- unicode to index + luatex.marks = { } -- todo + luatex.names = names -- name to index + luatex.private = private end --[[ldx-- @@ -309,14 +344,15 @@ end and extra kerns. This saves quite some lookups later.</p> --ldx]]-- -function fonts.afm.add_ligatures(afmdata,ligatures) - local chars = afmdata.characters - for k,v in pairs(characters[ligatures]) do - local one = chars[k] +function afm.add_ligatures(afmdata,ligatures) + local glyphs, luatex = afmdata.glyphs, afmdata.luatex + local indices, unicodes, names = luatex.indices, luatex.unicodes, luatex.names + for k,v in next, characters[ligatures] do -- main characters table + local one = glyphs[names[k]] if one then - for _, b in pairs(v) do + for _, b in next, v do two, three = b[1], b[2] - if two and three and chars[two] and chars[three] then + if two and three and names[two] and names[three] then local ol = one[ligatures] if ol then if not ol[two] then -- was one.ligatures ... bug @@ -336,39 +372,55 @@ end them selectively.</p> --ldx]]-- -function fonts.afm.add_kerns(afmdata) - local chars = afmdata.characters - -- add complex with values of simplified when present +function afm.add_kerns(afmdata) + local glyphs = afmdata.glyphs + local names = afmdata.luatex.names + local uncomposed = characters.uncomposed local function do_it_left(what) - for _,v in pairs(chars) do - if v.kerns then - local k = { } - for complex,simple in pairs(characters.uncomposed[what]) do - local ks = k[simple] - if ks and not k[complex] then - k[complex] = ks + for index, glyph in next, glyphs do + local kerns = glyph.kerns + if kerns then + local extrakerns = glyph.extrakerns or { } + for complex, simple in next, uncomposed[what] do + if names[compex] then + local ks = kerns[simple] + if ks and not kerns[complex] then + extrakerns[complex] = ks + end end end - if not table.is_empty(k) then - v.extrakerns = k + if next(extrakerns) then + glyph.extrakerns = extrakerns end end end end - do_it_left("left") - do_it_left("both") - -- copy kerns from simple char to complex char unless set local function do_it_copy(what) - for complex,simple in pairs(characters.uncomposed[what]) do - local c = chars[complex] + for complex, simple in next, uncomposed[what] do + local c = glyphs[names[complex]] if c then -- optional - local s = chars[simple] - if s and s.kerns then - c.extrakerns = s.kerns -- ok ? no merge ? + local s = glyphs[names[simple]] + if s then + if not c.kerns then + c.extrakerns = s.kerns or { } + end + if s.extrakerns then + local extrakerns = c.extrakerns or { } + for k, v in next, s.extrakerns do + extrakerns[k] = v + end + if next(extrakerns) then + s.extrakerns = extrakerns + end + end end end end end + -- add complex with values of simplified when present + do_it_left("left") + do_it_left("both") + -- copy kerns from simple char to complex char unless set do_it_copy("both") do_it_copy("right") end @@ -380,101 +432,137 @@ end -- once we have otf sorted out (new format) we can try to make the afm -- cache similar to it (similar tables) -function fonts.afm.add_dimensions(data) -- we need to normalize afm to otf i.e. indexed table instead of name +function afm.add_dimensions(data) -- we need to normalize afm to otf i.e. indexed table instead of name if data then - for n, d in pairs(data.characters) do - local bb = d.boundingbox + for index, glyph in next, data.glyphs do + local bb = glyph.boundingbox if bb then local ht, dp = bb[4], -bb[2] - if ht ~= 0 then d.height = ht end - if dp ~= 0 then d.depth = dp end + if ht == 0 or ht < 0 then + -- no need to set it and no negative heights, nil == 0 + else + glyph.height = ht + end + if dp == 0 or dp < 0 then + -- no negative depths and no negative depths, nil == 0 + else + glyph.depth = dp + end end - d.name = n end end end -function fonts.afm.copy_to_tfm(data) - if data and data.characters then - local tfm = { characters = { }, parameters = { } } - local afmcharacters = data.characters - local characters, parameters = tfm.characters, tfm.parameters - if afmcharacters then - for k, v in pairs(afmcharacters) do - characters[v.unicode] = { description = v } +function afm.copy_to_tfm(data) + if data then + local glyphs = data.glyphs + if glyphs then + local metadata, luatex = data.metadata, data.luatex + local unicodes, indices = luatex.unicodes, luatex.indices + local characters, parameters, descriptions = { }, { }, { } + local tfm = { + characters = characters, + parameters = parameters, + descriptions = descriptions, + indices = indices, + unicodes = unicodes, + luatex = luatex, + } + for u, i in next, indices do + local d = glyphs[i] + characters[u] = { } + descriptions[u] = d end - end - tfm.encodingbytes = data.encodingbytes or 2 - tfm.fullname = data.fullname - tfm.filename = data.filename - tfm.name = tfm.fullname -- data.name or tfm.fullname - tfm.type = "real" - tfm.units = 1000 - tfm.stretch = stretch - tfm.slant = slant - tfm.direction = 0 - tfm.boundarychar_label = 0 - tfm.boundarychar = 65536 - --~ tfm.false_boundarychar = 65536 -- produces invalid tfm in luatex - tfm.designsize = (data.designsize or 10)*65536 - local spaceunits = 500 - tfm.spacer = "500 units" - if data.isfixedpitch then - if afmcharacters['space'] and afmcharacters['space'].width then - spaceunits, tfm.spacer = afmcharacters['space'].width, "space" - elseif afmcharacters['emdash'] and afmcharacters['emdash'].width then -- funny default - spaceunits, tfm.spacer = afmcharacters['emdash'].width, "emdash" - elseif data.charwidth then - spaceunits, tfm.spacer = data.charwidth, "charwidth" + tfm.encodingbytes = metadata.encodingbytes or 2 + tfm.fullname = metadata.fullname + tfm.filename = metadata.filename + tfm.name = tfm.fullname + tfm.psname = tfm.fullname + tfm.type = "real" + tfm.units = 1000 + tfm.stretch = stretch -- nil + tfm.slant = slant -- nil + tfm.direction = 0 + tfm.boundarychar_label = 0 + tfm.boundarychar = 65536 + --~ tfm.false_boundarychar = 65536 -- produces invalid tfm in luatex + tfm.designsize = (metadata.designsize or 10)*65536 + local spaceunits = 500 + tfm.spacer = "500 units" + -- same as otf + local endash, emdash = unicodes['space'], unicodes['emdash'] + if metadata.isfixedpitch then + if descriptions[endash] then + spaceunits, tfm.spacer = descriptions[endash].width, "space" + end + if not spaceunits and descriptions[emdash] then + spaceunits, tfm.spacer = descriptions[emdash].width, "emdash" + end + if not spaceunits and metadata.charwidth then + spaceunits, tfm.spacer = metadata.charwidth, "charwidth" + end + else + if descriptions[endash] then + spaceunits, tfm.spacer = descriptions[endash].width, "space" + end + -- if not spaceunits and descriptions[emdash] then + -- spaceunits, tfm.spacer = descriptions[emdash].width/2, "emdash/2" + -- end + if not spaceunits and metadata.charwidth then + spaceunits, tfm.spacer = metadata.charwidth, "charwidth" + end end - elseif afmcharacters['space'] and afmcharacters['space'].width then - spaceunits, tfm.spacer = afmcharacters['space'].width, "space" - elseif data.charwidth then - spaceunits, tfm.spacer = data.charwidth, "charwidth variable" - end - spaceunits = tonumber(spaceunits) - parameters.slant = 0 - parameters.space = spaceunits - parameters.space_stretch = 500 - parameters.space_shrink = 333 - parameters.x_height = 400 - parameters.quad = 1000 - parameters.extra_space = 0 - if spaceunits < 200 then - -- todo: warning - end - tfm.italicangle = data.italicangle - tfm.ascender = math.abs(data.ascender or 0) - tfm.descender = math.abs(data.descender or 0) - if data.italicangle then - parameters.slant = parameters.slant - math.round(math.tan(data.italicangle*math.pi/180)) - end - if data.isfixedpitch then - parameters.space_stretch = 0 - parameters.space_shrink = 0 - elseif fonts.afm.syncspace then - parameters.space_stretch = spaceunits/2 - parameters.space_shrink = spaceunits/3 - end - if data.xheight and data.xheight > 0 then - parameters.x_height = data.xheight - elseif afmcharacters['x'] and afmcharacters['x'].height then - parameters.x_height = afmcharacters['x'].height or 0 - end - local fd = data.fontdimens - if fd and fd[8] and fd[9] and fd[10] then -- math - for k,v in pairs(fd) do - parameters[k] = v + -- + spaceunits = tonumber(spaceunits) + parameters.slant = 0 + parameters.space = spaceunits + parameters.space_stretch = 500 + parameters.space_shrink = 333 + parameters.x_height = 400 + parameters.quad = 1000 + if spaceunits < 200 then + -- todo: warning + end + tfm.ascender = math.abs(metadata.ascender or 0) + tfm.descender = math.abs(metadata.descender or 0) + local italicangle = data.metadata.italicangle + if italicangle then + tfm.italicangle = italicangle + parameters.slant = parameters.slant - math.round(math.tan(italicangle*math.pi/180)) + end + if metadata.isfixedpitch then + parameters.space_stretch = 0 + parameters.space_shrink = 0 + elseif afm.syncspace then + parameters.space_stretch = spaceunits/2 + parameters.space_shrink = spaceunits/3 + end + parameters.extra_space = parameters.space_shrink + if metadata.xheight and metadata.xheight > 0 then + parameters.x_height = metadata.xheight + else + -- same as otf + local x = unicodes['x'] + if x then + local x = descriptions[x] + if x then + parameters.x_height = x.height + end + end + -- + end + local fd = data.fontdimens + if fd and fd[8] and fd[9] and fd[10] then -- math + for k,v in next, fd do + parameters[k] = v + end + end + if next(characters) then + return tfm end end - if table.is_empty(characters) then - return nil - else - return tfm - end - else - return nil end + return nil end --[[ldx-- @@ -484,86 +572,90 @@ to treat this fontformat like any other and handle features in a more configurable way.</p> --ldx]]-- -function fonts.afm.features.register(name,default) - fonts.afm.features.list[#fonts.afm.features.list+1] = name - fonts.afm.features.default[name] = default +function afm.features.register(name,default) + afm.features.list[#afm.features.list+1] = name + afm.features.default[name] = default end -function fonts.afm.set_features(tfmdata) +function afm.set_features(tfmdata) local shared = tfmdata.shared local afmdata = shared.afmdata - -- elsewhere: shared.features = fonts.define.check(shared.features,fonts.afm.features.default) local features = shared.features ---~ texio.write_nl(table.serialize(features)) if not table.is_empty(features) then local mode = tfmdata.mode or fonts.mode - local fi = fonts.initializers[mode] - if fi and fi.afm then - local function initialize(list) -- using tex lig and kerning + local initializers = fonts.initializers + local fi = initializers[mode] + local fiafm = fi and fi.afm + if fiafm then + local lists = { + fonts.triggers, + afm.features.list, + fonts.manipulators, + } + for l=1,3 do + local list = lists[l] if list then - for _, f in ipairs(list) do + for i=1,#list do + local f = list[i] local value = features[f] - if value and fi.afm[f] then -- brr - if fonts.afm.trace_features then - logs.report("define afm",string.format("initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown',tfmdata.name or 'unknown')) + if value and fiafm[f] then -- brr + if trace_features then + logs.report("define afm","initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown',tfmdata.name or 'unknown') end - fi.afm[f](tfmdata,value) + fiafm[f](tfmdata,value) mode = tfmdata.mode or fonts.mode - fi = fonts.initializers[mode] + fiafm = initializers[mode].afm end end end end - initialize(fonts.triggers) - initialize(fonts.afm.features.list) - initialize(fonts.manipulators) end local fm = fonts.methods[mode] - if fm and fm.afm then - local function register(list) -- node manipulations + local fmafm = fm and fm.afm + if fmfm then + local lists = { + afm.features.list, + } + local sp = shared.processors + for l=1,1 do + local list = lists[l] if list then - for _, f in ipairs(list) do - if features[f] and fm.afm[f] then -- brr - if not shared.processors then -- maybe also predefine - shared.processors = { fm.afm[f] } + for i=1,#list do + local f = list[i] + if features[f] and fmafm[f] then -- brr + if not sp then + sp = { fmafm[f] } + shared.processors = sp else - shared.processors[#shared.processors+1] = fm.afm[f] + sp[#sp+1] = fmafm[f] end end end end end - register(fonts.afm.features.list) end end end -function fonts.afm.check_features(specification) - local features, done = fonts.define.check(specification.features.normal,fonts.afm.features.default) +function afm.check_features(specification) + local features, done = fonts.define.check(specification.features.normal,afm.features.default) if done then specification.features.normal = features - fonts.tfm.hash_instance(specification,true) + tfm.hash_instance(specification,true) end end -function fonts.afm.afm_to_tfm(specification) +function afm.afm_to_tfm(specification) local afmname = specification.filename or specification.name - local encoding, filename = afmname:match("^(.-)%-(.*)$") -- context: encoding-name.* - if encoding and filename and fonts.enc.known[encoding] then - fonts.tfm.set_normal_feature(specification,'encoding',encoding) -- will go away - if fonts.trace then - logs.report("load afm", string.format("stripping encoding prefix from filename %s",afmname)) - end - afmname = filename - elseif specification.forced == "afm" then - if fonts.trace then - logs.report("load afm", string.format("forcing afm format for %s",afmname)) + if specification.forced == "afm" or specification.format == "afm" then -- move this one up + if trace_loading then + logs.report("load afm","forcing afm format for %s",afmname) end else - local tfmname = input.findbinfile(texmf.instance,afmname,"ofm") or "" + local tfmname = resolvers.findbinfile(afmname,"ofm") or "" if tfmname ~= "" then - if fonts.trace then - logs.report("load afm", string.format("fallback from afm to tfm for %s",afmname)) + if trace_loading then + logs.report("load afm","fallback from afm to tfm for %s",afmname) end afmname = "" end @@ -571,27 +663,27 @@ function fonts.afm.afm_to_tfm(specification) if afmname == "" then return nil else - fonts.afm.check_features(specification) + afm.check_features(specification) specification = fonts.define.resolve(specification) -- new, was forgotten local features = specification.features.normal local cache_id = specification.hash - local tfmdata = containers.read(fonts.tfm.cache(), cache_id) -- cache with features applied + local tfmdata = containers.read(tfm.cache(), cache_id) -- cache with features applied if not tfmdata then - local afmdata = fonts.afm.load(afmname) + local afmdata = afm.load(afmname) if not table.is_empty(afmdata) then - fonts.afm.add_dimensions(afmdata) - tfmdata = fonts.afm.copy_to_tfm(afmdata) + afm.add_dimensions(afmdata) + tfmdata = afm.copy_to_tfm(afmdata) if not table.is_empty(tfmdata) then tfmdata.shared = tfmdata.shared or { } tfmdata.unique = tfmdata.unique or { } tfmdata.shared.afmdata = afmdata tfmdata.shared.features = features - fonts.afm.set_features(tfmdata) + afm.set_features(tfmdata) end - elseif fonts.trace then - logs.report("load afm", string.format("no (valid) afm file found with name %s",afmname)) + elseif trace_loading then + logs.report("load afm","no (valid) afm file found with name %s",afmname) end - tfmdata = containers.write(fonts.tfm.cache(),cache_id,tfmdata) + tfmdata = containers.write(tfm.cache(),cache_id,tfmdata) end return tfmdata end @@ -605,9 +697,9 @@ those cases, but now that we can handle <l n='opentype'/> directly we no longer need this features.</p> --ldx]]-- -fonts.tfm.default_encoding = 'unicode' +tfm.default_encoding = 'unicode' -function fonts.tfm.set_normal_feature(specification,name,value) +function tfm.set_normal_feature(specification,name,value) if specification and name then specification.features = specification.features or { } specification.features.normal = specification.features.normal or { } @@ -615,15 +707,11 @@ function fonts.tfm.set_normal_feature(specification,name,value) end end -function fonts.tfm.read_from_afm(specification) ---~ local fullname = input.findbinfile(texmf.instance,specification.name,"afm") or "" ---~ if fullname ~= "" then ---~ specification.filename = fullname ---~ end - local tfmtable = fonts.afm.afm_to_tfm(specification) +function tfm.read_from_afm(specification) + local tfmtable = afm.afm_to_tfm(specification) if tfmtable then tfmtable.name = specification.name - tfmtable = fonts.tfm.scale(tfmtable, specification.size) + tfmtable = tfm.scale(tfmtable, specification.size) local afmdata = tfmtable.shared.afmdata local filename = afmdata and afmdata.luatex and afmdata.luatex.filename if not filename then @@ -631,8 +719,8 @@ function fonts.tfm.read_from_afm(specification) end if filename then tfmtable.encodingbytes = 2 - tfmtable.filename = input.findbinfile(texmf.instance,filename,"") or filename - tfmtable.fullname = afmdata.fontname or afmdata.fullname + tfmtable.filename = resolvers.findbinfile(filename,"") or filename + tfmtable.fullname = afmdata.metadata.fontname or afmdata.metadata.fullname tfmtable.format = 'type1' tfmtable.name = afmdata.luatex.filename or tfmtable.fullname end @@ -649,39 +737,56 @@ end those that make sense for this format.</p> --ldx]]-- -function fonts.afm.features.prepare_ligatures(tfmdata,ligatures,value) -- probably faulty / check index +function afm.features.prepare_ligatures(tfmdata,ligatures,value) if value then - local charlist = tfmdata.shared.afmdata.characters - for k,v in pairs(tfmdata.characters) do - local ac = charlist[v.name] - if ac then - local al = ac[ligatures] - if al then - local ligatures = { } - for k,v in pairs(al) do - ligatures[charlist[k].unicode] = { - char = charlist[v].unicode, + local afmdata = tfmdata.shared.afmdata + local luatex = afmdata.luatex + local unicodes = luatex.unicodes + local descriptions = tfmdata.descriptions + for u, chr in next, tfmdata.characters do + local d = descriptions[u] + local l = d[ligatures] + if l then + local ligatures = chr.ligatures + if not ligatures then + ligatures = { } + chr.ligatures = ligatures + end + for k, v in next, l do + local uk, uv = unicodes[k], unicodes[v] + if uk and uv then + ligatures[uk] = { + char = uv, type = 0 } end - v.ligatures = ligatures end end end end end -function fonts.afm.features.prepare_kerns(tfmdata,kerns,value) +function afm.features.prepare_kerns(tfmdata,kerns,value) if value then - local charlist = tfmdata.shared.afmdata.characters - for _, chr in pairs(tfmdata.characters) do - local newkerns = charlist[chr.description.name][kerns] + local afmdata = tfmdata.shared.afmdata + local luatex = afmdata.luatex + local unicodes = luatex.unicodes + local descriptions = tfmdata.descriptions + for u, chr in next, tfmdata.characters do + local d = descriptions[u] + local newkerns = d[kerns] if newkerns then - local t = chr.kerns or { } - for k,v in pairs(newkerns) do - t[charlist[k].unicode] = v + local kerns = chr.kerns + if not kerns then + kerns = { } + chr.kerns = kerns + end + for k,v in next, newkerns do + local uk = unicodes[k] + if uk then + kerns[uk] = v + end end - chr.kerns = t end end end @@ -689,14 +794,14 @@ end -- hm, register? -function fonts.initializers.base.afm.ligatures (tfmdata,value) fonts.afm.features.prepare_ligatures(tfmdata,'ligatures', value) end -function fonts.initializers.base.afm.texligatures(tfmdata,value) fonts.afm.features.prepare_ligatures(tfmdata,'texligatures',value) end -function fonts.initializers.base.afm.kerns (tfmdata,value) fonts.afm.features.prepare_kerns (tfmdata,'kerns', value) end -function fonts.initializers.base.afm.extrakerns (tfmdata,value) fonts.afm.features.prepare_kerns (tfmdata,'extrakerns', value) end +function fonts.initializers.base.afm.ligatures (tfmdata,value) afm.features.prepare_ligatures(tfmdata,'ligatures', value) end +function fonts.initializers.base.afm.texligatures(tfmdata,value) afm.features.prepare_ligatures(tfmdata,'texligatures',value) end +function fonts.initializers.base.afm.kerns (tfmdata,value) afm.features.prepare_kerns (tfmdata,'kerns', value) end +function fonts.initializers.base.afm.extrakerns (tfmdata,value) afm.features.prepare_kerns (tfmdata,'extrakerns', value) end -fonts.afm.features.register('liga',true) -fonts.afm.features.register('kerns',true) -fonts.afm.features.register('extrakerns') -- needed? +afm.features.register('liga',true) +afm.features.register('kerns',true) +afm.features.register('extrakerns') -- needed? fonts.initializers.node.afm.ligatures = fonts.initializers.base.afm.ligatures fonts.initializers.node.afm.texligatures = fonts.initializers.base.afm.texligatures @@ -708,11 +813,11 @@ fonts.initializers.node.afm.liga = fonts.initializers.base.afm.ligatures fonts.initializers.base.afm.tlig = fonts.initializers.base.afm.texligatures fonts.initializers.node.afm.tlig = fonts.initializers.base.afm.texligatures -fonts.initializers.base.afm.trep = fonts.tfm.replacements -fonts.initializers.node.afm.trep = fonts.tfm.replacements +fonts.initializers.base.afm.trep = tfm.replacements +fonts.initializers.node.afm.trep = tfm.replacements -fonts.afm.features.register('tlig',true) -- todo: also proper features for afm -fonts.afm.features.register('trep',true) -- todo: also proper features for afm +afm.features.register('tlig',true) -- todo: also proper features for afm +afm.features.register('trep',true) -- todo: also proper features for afm -- tfm features @@ -728,7 +833,7 @@ fonts.initializers.node.afm.compose = fonts.initializers.common.compose -- afm specific, encodings ...kind of obsolete -fonts.afm.features.register('encoding') +afm.features.register('encoding') fonts.initializers.base.afm.encoding = fonts.initializers.common.encoding fonts.initializers.node.afm.encoding = fonts.initializers.common.encoding @@ -739,7 +844,7 @@ fonts.initializers.base.afm.onum = fonts.initializers.common.oldstyle fonts.initializers.base.afm.smcp = fonts.initializers.common.smallcaps fonts.initializers.base.afm.fkcp = fonts.initializers.common.fakecaps -fonts.afm.features.register('onum',false) -fonts.afm.features.register('smcp',false) -fonts.afm.features.register('fkcp',false) +afm.features.register('onum',false) +afm.features.register('smcp',false) +afm.features.register('fkcp',false) |