diff options
Diffstat (limited to 'macros/luatex/generic/luaotfload/luaotfload-harf-plug.lua')
-rw-r--r-- | macros/luatex/generic/luaotfload/luaotfload-harf-plug.lua | 138 |
1 files changed, 88 insertions, 50 deletions
diff --git a/macros/luatex/generic/luaotfload/luaotfload-harf-plug.lua b/macros/luatex/generic/luaotfload/luaotfload-harf-plug.lua index c1d87de312..726aaaea7e 100644 --- a/macros/luatex/generic/luaotfload/luaotfload-harf-plug.lua +++ b/macros/luatex/generic/luaotfload/luaotfload-harf-plug.lua @@ -5,8 +5,8 @@ do -- block to avoid to many local variables error local ProvidesLuaModule = { name = "luaotfload-harf-plug", - version = "3.12", --TAGVERSION - date = "2020-02-02", --TAGDATE + version = "3.13", --TAGVERSION + date = "2020-05-01", --TAGDATE description = "luaotfload submodule / database", license = "GPL v2.0", author = "Khaled Hosny, Marcel Krüger", @@ -105,6 +105,8 @@ local fl_unsafe = hb.Buffer.GLYPH_FLAG_UNSAFE_TO_BREAK local startactual_p = "luaotfload_startactualtext" local endactual_p = "luaotfload_endactualtext" +local empty_table = {} + -- "Copy" properties as done by LuaTeX: Make old properties metatable local function copytable(old) local new = {} @@ -164,22 +166,11 @@ end local process --- Legacy TeX Input Method Disguised as Font Ligatures hack. --- --- Single replacements, keyed by character to replace. Handled separately --- because TeX ligaturing mechanism does not support one-to-one replacements. -local trep = { - [0x0022] = 0x201D, -- ["] - [0x0027] = 0x2019, -- ['] - [0x0060] = 0x2018, -- [`] -} - local function itemize(head, fontid, direction) local fontdata = font.getfont(fontid) local hbdata = fontdata and fontdata.hb local spec = fontdata and fontdata.specification local options = spec and spec.features.raw - local texlig = options and options.tlig local runs, codes = {}, {} local dirstack = {} @@ -189,20 +180,25 @@ local function itemize(head, fontid, direction) for n, id, subtype in direct.traverse(head) do local code = 0xFFFC -- OBJECT REPLACEMENT CHARACTER - local skip = false + local skip = lastskip + local props = properties[n] - if id == glyph_t then + if props and props.zwnj then + code = 0x200C + -- skip = false -- Not sure about this, but lastskip should be a bit faster + elseif id == glyph_t then if is_char(n) and getfont(n) == fontid then code = getchar(n) + skip = false else skip = true end elseif id == glue_t and subtype == spaceskip_t then code = 0x0020 -- SPACE - skip = lastskip elseif id == disc_t then if uses_font(n, fontid) then code = 0x00AD -- SOFT HYPHEN + skip = false else skip = true end @@ -217,17 +213,8 @@ local function itemize(head, fontid, direction) -- Pop the last direction from the stack. currdir = tableremove(dirstack) end - skip = lastskip elseif id == localpar_t then currdir = getdir(n) - skip = lastskip - end - - if not skip and texlig then - local replacement = trep[code] - if replacement then - code = replacement - end end codes[#codes + 1] = code @@ -277,7 +264,7 @@ local function makesub(run, codes, nodelist) if nodelist == 0 then -- FIXME: This shouldn't happen nodelist = nil end - nodelist, glyphs = shape(nodelist, nodelist, subrun) + nodelist, nodelist, glyphs = shape(nodelist, nodelist, subrun) return { glyphs = glyphs, run = subrun, head = nodelist } end @@ -300,7 +287,8 @@ local function printnodes(label, head, after) end -- Main shaping function that calls HarfBuzz, and does some post-processing of -- the output. -function shape(head, node, run) +function shape(head, firstnode, run) + local node = firstnode local codes = run.codes local offset = run.start local nodeindex = offset @@ -313,7 +301,7 @@ function shape(head, node, run) local fontdata = font.getfont(fontid) local hbdata = fontdata.hb - local spec = hbdata.spec + local spec = fontdata.specification local features = spec.hb_features local options = spec.features.raw local hbshared = hbdata.shared @@ -324,6 +312,9 @@ function shape(head, node, run) local shapers = options.shaper and { options.shaper } or {} local buf = hb.Buffer.new() + if buf.set_flags then + buf:set_flags(hbdata.buf_flags) + end buf:set_direction(dir) buf:set_script(script) buf:set_language(lang) @@ -334,6 +325,41 @@ function shape(head, node, run) local vscale = hbdata.vscale hbfont:set_scale(hscale, vscale) + do + features = table.merged(features) -- We don't want to modify the global features + local current_features = {} + local n = node + for i = offset-1, offset+len-2 do + local props = properties[n] or empty_table + if props then + local local_feat = props.glyph_features or empty_table + if local_feat then + for tag, value in next, current_features do + local loc = local_feat[tag] + loc = loc ~= nil and (tonumber(loc) or (loc and 1 or 0)) or nil + if value.value ~= loc then -- This includes loc == nil + value._end = i + features[#features + 1] = value + current_features[tag] = nil + end + end + for tag, value in next, local_feat do + if not current_features[tag] then + local feat = hb.Feature.new(tag) + feat.value = tonumber(value) or (value and 1 or 0) + feat.start = i + current_features[tag] = feat + end + end + end + end + n = getnext(n) + end + for _, feat in next, current_features do + features[#features + 1] = feat + end + end + if hb.shape_full(hbfont, buf, features, shapers) then -- The engine wants the glyphs in logical order, but HarfBuzz outputs them -- in visual order, so we reverse RTL buffers. @@ -372,7 +398,7 @@ function shape(head, node, run) local node = node for j = cluster,nextcluster-1 do local id = getid(node) - if id == glyph_t or id == glue_t then + if id == glyph_t or (id == glue_t and getsubtype(node) == spaceskip_t) then local code = codes[j + 1] hex = hex..to_utf16_hex(code) str = str..utf8.char(code) @@ -385,7 +411,8 @@ function shape(head, node, run) local discindex = nil local disc = node for j = cluster + 1, nextcluster do - if codes[j] == 0x00AD then + local props = properties[disc] + if (not (props and props.zwnj)) and getid(disc) == disc_t then discindex = j break end @@ -489,6 +516,9 @@ function shape(head, node, run) local predisc = getprev(disc) setnext(predisc, rep) setprev(rep, predisc) + if firstnode == startnode then + firstnode = disc + end if startnode == head then head = disc else @@ -519,10 +549,10 @@ function shape(head, node, run) end end end - return head, glyphs, run.len - len + return head, firstnode, glyphs, run.len - len end - return head, {}, 0 + return head, firstnode, {}, 0 end local function color_to_rgba(color) @@ -583,8 +613,10 @@ local function tonodes(head, node, run, glyphs) local dir = run.dir local fontid = run.font local fontdata = font.getfont(fontid) + local space = fontdata.parameters.space local characters = fontdata.characters local hbdata = fontdata.hb + local hfactor = (fontdata.extend or 1000) / 1000 local palette = hbdata.palette local hbshared = hbdata.shared local hbface = hbshared.face @@ -596,7 +628,6 @@ local function tonodes(head, node, run, glyphs) local lastprops local scale = hbdata.scale - local letterspace = hbdata.letterspace local haspng = hbshared.haspng local fonttype = hbshared.fonttype @@ -640,7 +671,8 @@ local function tonodes(head, node, run, glyphs) freenode(oldnode) nodeindex = nodeindex + 1 else - if id == glyph_t then + if lastprops and lastprops.zwnj and nodeindex == glyph.cluster + 1 then + elseif id == glyph_t then local done local fontglyph = fontglyphs[gid] local character = characters[char] @@ -749,8 +781,14 @@ local function tonodes(head, node, run, glyphs) -- glyph, keep the node char unchanged. Helps with primitives that -- take characters as input but actually work on glyphs, like -- `\rpcode`. - if character.commands or not oldcharacter - or character.index ~= oldcharacter.index then + if not oldcharacter then + if gid == 0 then + local new = copynode(node) + head, node = insertafter(head, node, new) + end + setchar(node, char) + elseif character.commands + or character.index ~= oldcharacter.index then setchar(node, char) end local xoffset = (rtl and -glyph.x_offset or glyph.x_offset) * scale @@ -795,8 +833,8 @@ local function tonodes(head, node, run, glyphs) if glyph.endactual then setprop(node, endactual_p, true) end - local x_advance = glyph.x_advance + letterspace - local width = fontglyph.width + local x_advance = glyph.x_advance + local width = fontglyph.width * hfactor if width ~= x_advance then -- The engine always uses the glyph width from the font, so we need -- to insert a kern node if the x advance is different. @@ -807,14 +845,18 @@ local function tonodes(head, node, run, glyphs) elseif id == glue_t and getsubtype(node) == spaceskip_t then -- If the glyph advance is different from the font space, then a -- substitution or positioning was applied to the space glyph changing - -- it from the default, so reset the glue using the new advance. + -- it from the default. We try to maintain as much as possible from the + -- original value because we assume that we want to keep spacefactors and + -- assume that we got mostly positioning applied. TODO: Handle the case that + -- we became a glyph in the process. -- We are intentionally not comparing with the existing glue width as -- spacing after the period is larger by default in TeX. - local width = (glyph.x_advance + letterspace) * scale - if fontdata.parameters.space ~= width then - setwidth(node, width) - setfield(node, "stretch", width / 2) - setfield(node, "shrink", width / 3) + local width = glyph.x_advance * scale + -- if space > width + 2 or width > space + 2 then + if space ~= width then + setwidth(node, getwidth(node) - space + width) + -- setfield(node, "stretch", width / 2) + -- setfield(node, "shrink", width / 3) end elseif id == kern_t and getsubtype(node) == italiccorr_t then -- If this is an italic correction node and the previous node is a @@ -844,19 +886,15 @@ local function shape_run(head, current, run) if not run.skip then -- Font loaded with our loader and an HarfBuzz face is present, do our -- shaping. - local fontid = run.font - local fontdata = font.getfont(fontid) - local options = fontdata.specification.features.raw - local glyphs, offset - head, glyphs, offset = shape(head, current, run) + head, current, glyphs, offset = shape(head, current, run) return offset, tonodes(head, current, run, glyphs) else return 0, head, run.after end end -function process(head, font, direction) +function process(head, font, _attr, direction) local newhead, current = head, head local runs = itemize(head, font, direction) |