diff options
Diffstat (limited to 'macros/luatex/generic/luaotfload/luaotfload-harf-define.lua')
-rw-r--r-- | macros/luatex/generic/luaotfload/luaotfload-harf-define.lua | 164 |
1 files changed, 151 insertions, 13 deletions
diff --git a/macros/luatex/generic/luaotfload/luaotfload-harf-define.lua b/macros/luatex/generic/luaotfload/luaotfload-harf-define.lua index f128adbaae..d7ee7c11bb 100644 --- a/macros/luatex/generic/luaotfload/luaotfload-harf-define.lua +++ b/macros/luatex/generic/luaotfload/luaotfload-harf-define.lua @@ -5,8 +5,8 @@ do -- block to avoid to many local variables error assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { name = "luaotfload-harf-define", - version = "3.18", --TAGVERSION - date = "2021-05-21", --TAGDATE + version = "3.19", --TAGVERSION + date = "2022-02-24", --TAGDATE description = "luaotfload submodule / HarfBuzz font loading", license = "GPL v2.0", author = "Khaled Hosny, Marcel Krüger", @@ -21,14 +21,14 @@ local gsub = string.gsub local hb = luaotfload.harfbuzz local scriptlang_to_harfbuzz = require'luaotfload-scripts'.to_harfbuzz +local cff2_handler = require'luaotfload-harf-var-cff2' +local ttf_handler = require'luaotfload-harf-var-ttf' local harf_settings = luaotfload.harf or {} luaotfload.harf = harf_settings harf_settings.default_buf_flags = hb.Buffer.FLAGS_DEFAULT or 0 -local hbfonts = {} - local cfftag = hb.Tag.new("CFF ") local cff2tag = hb.Tag.new("CFF2") local os2tag = hb.Tag.new("OS/2") @@ -36,9 +36,15 @@ local posttag = hb.Tag.new("post") local glyftag = hb.Tag.new("glyf") local gpostag = hb.Tag.new("GPOS") +local italtag = hb.Tag.new("ital") +local wghttag = hb.Tag.new("wght") +local slnttag = hb.Tag.new("slnt") +local opsztag = hb.Tag.new("opsz") + local invalid_l = hb.Language.new() local invalid_s = hb.Script.new() +local tointeger = math.tointeger local floor = math.floor local function round(x) return floor(x + 0.5) @@ -71,27 +77,123 @@ local get_designsize do end local containers = luaotfload.fontloader.containers -local hbcacheversion = 1.2 -local facecache = containers.define("fonts", "hb", hbcacheversion, true) +local hbcacheversion = 1.4 +local fontcache = containers.define("fonts", "hb", hbcacheversion, true) +local facecache = {} + +local variable_pattern do + local l = lpeg or require'lpeg' + local white = l.S' \t'^0 + local number = l.C(l.S'+-'^-1 * (l.R'09'^1 * ('.' * l.R'09'^0)^-1 + '.' * l.R'09'^1)) + local name_or_tag = l.C(l.R('AZ', 'az')^1) + local pair = l.Ct(name_or_tag * white * '=' * white * (number + l.Cc(nil) * 'auto')) + variable_pattern = l.Ct(pair * (white * ',' * white * pair)^0) +end local function loadfont(spec) local path, sub = spec.resolved, spec.sub or 1 - local key = string.format("%s:%d", gsub(path, "[/\\]", ":"), sub) + local key = gsub(string.format("%s:%d:%s", path, sub, instance), "[/\\]", ":") local attributes = lfs.attributes(path) if not attributes then return end local size, date = attributes.size or 0, attributes.modification or 0 - local cached = containers.read(facecache, key) + local hbface = facecache[key] + if not hbface then + hbface = hb.Face.new(path, sub - 1) + facecache[key] = hbface + end + + local normalized + local varkey + if hbface.ot_var_has_data and hbface:ot_var_has_data() then + local design_coords + local instance = spec.features.raw.instance + local axis = spec.features.raw.axis + local assignments = axis and variable_pattern:match(axis) + if axis and not assignments and not instance then + instance, axis = axis, nil + end + if instance then + instance = instance:lower() + local instances = hbface:ot_var_named_instance_get_infos() + for i = 1, #instances do + local inst = instances[i] + if instance == hbface:get_name(inst.subfamily_name_id):lower() then + design_coords = {hbface:ot_var_named_instance_get_design_coords(inst.index)} + break + end + end + if not design_coords then + texio.write_nl'Warning (luaotfload): Unknown instance name ignored.' + end + end + local axes = hbface:ot_var_get_axis_infos() + design_coords = design_coords or lua.newtable(#axes, 0) + if assignments then + for i = 1, #assignments do + local found + local name = assignments[i][1] + local tag + if #name <= 4 then + tag = hb.Tag.new(name) + end + name = string.lower(name) + for j = 1, #axes do + local axis = axes[j] + if tag and tag == axis.tag then + found = axis + break + end + if name == hbface:get_name(axis.name_id):lower() then + found = axis + if not tag then break end + end + end + if found then + design_coords[found.axis_index] = assignments[i][2] + else + texio.write_nl'Warning (luaotfload): Unknown axis name ignored.' + end + end + end + for i = 1, #axes do + local axis = axes[i] + local index = axis.axis_index -- == i in practise + if not design_coords[index] then + local tag = axis.tag + if tag == italtag and spec.style then + design_coords[index] = spec.style == 'i' or spec.style == 'bi' and 1 or 0 + elseif tag == slnttag and spec.style then + design_coords[index] = spec.style == 'i' or spec.style == 'bi' and -5 or 0 + elseif tag == wghttag and spec.style then + design_coords[index] = (spec.style == 'b' or spec.style == 'bi') and 600 or 400 + elseif tag == opsztag and (spec.optsize or spec.size > 0) then + design_coords[index] = spec.optsize or spec.size / 65536 + else + design_coords[index] = axis.default_value + end + end + end + normalized = {hbface:ot_var_normalize_coords(table.unpack(design_coords))} + varkey = ':' .. table.concat(normalized, ':') + key = key .. varkey + else + varkey = '' + end + + local cached = containers.read(fontcache, key) local iscached = cached and cached.date == date and cached.size == size - local hbface = iscached and cached.face or hb.Face.new(path, sub - 1) local tags = hbface and hbface:get_table_tags() -- If the face has no table tags then it isn’t a valid SFNT font that -- HarfBuzz can handle. if not tags then return end local hbfont = iscached and cached.font or hb.Font.new(hbface) + if normalized then + hbfont:set_var_coords_normalized(table.unpack(normalized)) + end if not iscached then local upem = hbface:get_upem() @@ -174,7 +276,7 @@ local function loadfont(spec) local glyph = hbfont:get_nominal_glyph(uni) if glyph then characters[uni] = glyph - nominals[glyph] = uni + nominals[glyph] = tointeger(uni) end end @@ -224,12 +326,13 @@ local function loadfont(spec) nominals = nominals, unicodes = characters, psname = hbface:get_name(hb.ot.NAME_ID_POSTSCRIPT_NAME), - fullname = hbface:get_name(hb.ot.NAME_ID_FULL_NAME), + fullname = hbface:get_name(hb.ot.NAME_ID_FULL_NAME) .. varkey, haspng = hbface:ot_color_has_png(), loaded = {}, -- Cached loaded glyph data. + normalized = normalized, } - containers.write(facecache, key, cached) + containers.write(fontcache, key, cached) end cached.face = hbface cached.font = hbfont @@ -340,6 +443,7 @@ local function scalefont(data, spec) hscale = upem, vscale = upem, buf_flags = harf_settings.default_buf_flags, + obj_repl = characters[0xFFFC] and 0xD800 or 0xFFFC, }, specification = spec, shared = { @@ -349,6 +453,7 @@ local function scalefont(data, spec) resources = { unicodes = data.name_to_char, }, + streamprovider = data.normalized and (data.fonttype == 'opentype' and 1 or 3) or nil, } tfmdata.shared.processes = fonts.handlers.otf.setfeatures(tfmdata, features) fonts.constructors.applymanipulators("otf", tfmdata, features, false) @@ -391,7 +496,7 @@ fonts.readers.harf = function(spec) val = (val and '+' or '-')..key hb_features[#hb_features + 1] = hb.Feature.new(val) elseif tonumber(val) then - val = '+'..key..'='..tonumber(val) - 1 + val = '+'..key..'='..tonumber(val) hb_features[#hb_features + 1] = hb.Feature.new(val) end end @@ -409,3 +514,36 @@ luatexbase.add_to_callback('find_truetype_file', function(name) return find_file(name, 'truetype fonts') or name:gsub('^harfloaded:', '') end, 'luaotfload.harf.strip_prefix') + +local glyph_stream_data +local glyph_stream_mapping, glyph_stream_mapping_inverse +local extents_hbfont +local cb = luatexbase.remove_from_callback('glyph_stream_provider', 'luaotfload.glyph_stream') +luatexbase.add_to_callback('glyph_stream_provider', function(fid, cid, kind, ocid) + if cid == 0 then -- Always the first call for a font + glyph_stream_data, extents_hbfont = nil + collectgarbage() + local fontdir = font.getfont(fid) + if fontdir and fontdir.hb then + if kind == 3 then + glyph_stream_mapping = {[ocid] = cid} + glyph_stream_mapping_inverse = {[cid] = ocid} + extents_hbfont = fontdir.hb.shared.font + elseif kind == 2 then + glyph_stream_data = ttf_handler(fontdir.hb.shared.face, fontdir.hb.shared.font, glyph_stream_mapping, glyph_stream_mapping_inverse) + else + glyph_stream_data = cff2_handler(fontdir.hb.shared.face, fontdir.hb.shared.font) + end + end + end + if glyph_stream_data then + return glyph_stream_data(cid) + elseif extents_hbfont then + glyph_stream_mapping[ocid] = cid + glyph_stream_mapping_inverse[cid] = ocid + local extents = extents_hbfont:get_glyph_extents(ocid) + return extents.width, extents.x_bearing, extents.height, extents.y_bearing + else + return cb(fid, cid, kind, ocid) + end +end, 'luaotfload.harf.glyphstream') |