diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkiv/font-ctx.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/mkiv/font-ctx.lua | 585 |
1 files changed, 149 insertions, 436 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkiv/font-ctx.lua b/Master/texmf-dist/tex/context/base/mkiv/font-ctx.lua index 6847a2b8d30..87885f64f70 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/font-ctx.lua +++ b/Master/texmf-dist/tex/context/base/mkiv/font-ctx.lua @@ -12,23 +12,21 @@ if not modules then modules = { } end modules ['font-ctx'] = { -- Todo: make a proper 'next id' mechanism (register etc) or wait till 'true' -- in virtual fonts indices is implemented. -local tostring, next, type, rawget, tonumber = tostring, next, type, rawget, tonumber +local context, commands = context, commands local format, gmatch, match, find, lower, upper, gsub, byte, topattern = string.format, string.gmatch, string.match, string.find, string.lower, string.upper, string.gsub, string.byte, string.topattern local concat, serialize, sort, fastcopy, mergedtable = table.concat, table.serialize, table.sort, table.fastcopy, table.merged local sortedhash, sortedkeys, sequenced = table.sortedhash, table.sortedkeys, table.sequenced -local parsers = utilities.parsers -local settings_to_hash, hash_to_string, settings_to_array = parsers.settings_to_hash, parsers.hash_to_string, parsers.settings_to_array +local settings_to_hash, hash_to_string, settings_to_array = utilities.parsers.settings_to_hash, utilities.parsers.hash_to_string, utilities.parsers.settings_to_array local formatcolumns = utilities.formatters.formatcolumns local mergehashes = utilities.parsers.mergehashes local formatters = string.formatters local basename = file.basename +local tostring, next, type, rawget, tonumber = tostring, next, type, rawget, tonumber local utfchar, utfbyte = utf.char, utf.byte local round = math.round -local context, commands = context, commands - local P, S, C, Cc, Cf, Cg, Ct, lpegmatch = lpeg.P, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Ct, lpeg.match local trace_features = false trackers.register("fonts.features", function(v) trace_features = v end) @@ -68,8 +66,6 @@ local hashes = fonts.hashes local currentfont = font.current local definefont = font.define -local getprivateslot = helpers.getprivateslot - local cleanname = names.cleanname local encodings = fonts.encodings @@ -79,12 +75,11 @@ local aglunicodes = nil -- delayed loading local nuts = nodes.nuts local tonut = nuts.tonut -local nextchar = nuts.traversers.char - local getattr = nuts.getattr local setattr = nuts.setattr local getprop = nuts.getprop local setprop = nuts.setprop +local getfont = nuts.getfont local setsubtype = nuts.setsubtype local texgetattribute = tex.getattribute @@ -163,8 +158,17 @@ helpers.name = getfontname local addformatter = utilities.strings.formatters.add -addformatter(formatters,"font:name", [["'"..fontname(%s).."'"]], { fontname = helpers.name }) -addformatter(formatters,"font:features",[["'"..sequenced(%s," ",true).."'"]],{ sequenced = table.sequenced }) +if LUAVERSION < 5.2 then + + addformatter(formatters,"font:name", [["'"..fontname(%s).."'"]], "local fontname = fonts.helpers.name") + addformatter(formatters,"font:features",[["'"..sequenced(%s," ",true).."'"]],"local sequenced = table.sequenced") + +else + + addformatter(formatters,"font:name", [["'"..fontname(%s).."'"]], { fontname = helpers.name }) + addformatter(formatters,"font:features",[["'"..sequenced(%s," ",true).."'"]],{ sequenced = table.sequenced }) + +end -- ... like font-sfm or so @@ -468,40 +472,36 @@ registerotffeature { -- }, -- } -do - - local beforecopyingcharacters = sequencers.new { - name = "beforecopyingcharacters", - arguments = "target,original", - } +local beforecopyingcharacters = sequencers.new { + name = "beforecopyingcharacters", + arguments = "target,original", +} - appendgroup(beforecopyingcharacters,"before") -- user - appendgroup(beforecopyingcharacters,"system") -- private - appendgroup(beforecopyingcharacters,"after" ) -- user +appendgroup(beforecopyingcharacters,"before") -- user +appendgroup(beforecopyingcharacters,"system") -- private +appendgroup(beforecopyingcharacters,"after" ) -- user - function constructors.beforecopyingcharacters(original,target) - local runner = beforecopyingcharacters.runner - if runner then - runner(original,target) - end +function constructors.beforecopyingcharacters(original,target) + local runner = beforecopyingcharacters.runner + if runner then + runner(original,target) end +end - local aftercopyingcharacters = sequencers.new { - name = "aftercopyingcharacters", - arguments = "target,original", - } +local aftercopyingcharacters = sequencers.new { + name = "aftercopyingcharacters", + arguments = "target,original", +} - appendgroup(aftercopyingcharacters,"before") -- user - appendgroup(aftercopyingcharacters,"system") -- private - appendgroup(aftercopyingcharacters,"after" ) -- user +appendgroup(aftercopyingcharacters,"before") -- user +appendgroup(aftercopyingcharacters,"system") -- private +appendgroup(aftercopyingcharacters,"after" ) -- user - function constructors.aftercopyingcharacters(original,target) - local runner = aftercopyingcharacters.runner - if runner then - runner(original,target) - end +function constructors.aftercopyingcharacters(original,target) + local runner = aftercopyingcharacters.runner + if runner then + runner(original,target) end - end --[[ldx-- @@ -566,12 +566,6 @@ local function definecontext(name,t) -- can be shared return number, t end --- {a,b,c} as table (so we don' need to parse again when it gets applied) --- we will update this ... when we have foo={a,b,c} then we can keep the table - --- \definefontfeature[demo][a={b,c}] --- \definefontfeature[demo][a={b=12,c={34,35}}] - local function presetcontext(name,parent,features) -- will go to con and shared if features == "" and find(parent,"=",1,true) then features = parent @@ -581,16 +575,6 @@ local function presetcontext(name,parent,features) -- will go to con and shared features = { } elseif type(features) == "string" then features = normalize_features(settings_to_hash(features)) - -- if type(value) == "string" and find(value,"[=:]") then - -- local t = settings_to_hash_colon_too(value) -- clashes with foo=file:bar - for key, value in next, features do - if type(value) == "string" and find(value,"[=]") then - local t = settings_to_hash(value) - if next(t) then - features[key] = sequenced(normalize_features(t,true),",") - end - end - end else features = normalize_features(features) end @@ -600,8 +584,8 @@ local function presetcontext(name,parent,features) -- will go to con and shared local s = setups[p] if s then for k, v in next, s do - -- no, as then we cannot overload: e.g. math,mathextra - -- reverted, so we only take from parent when not set +-- no, as then we cannot overload: e.g. math,mathextra +-- reverted, so we only take from parent when not set if features[k] == nil then features[k] = v end @@ -793,8 +777,7 @@ end local function registercontext(fontnumber,extraname,option) local extra = setups[extraname] if extra then - local mergedfeatures = { } - local mergedname = nil + local mergedfeatures, mergedname = { }, nil if option < 0 then mergedname = fontnumber .. "-" .. extraname else @@ -1014,33 +997,25 @@ definers.registersplit(":",colonized,"direct") -- define (two steps) -local sizepattern, splitpattern, specialscale do - - ----- space = P(" ") - ----- spaces = space^0 - local leftparent = (P"(") - local rightparent = (P")") - local leftbrace = (P"{") - local rightbrace = (P"}") - local withinparents = leftparent * (1-rightparent)^0 * rightparent - local withinbraces = leftbrace * (1-rightbrace )^0 * rightbrace - local value = C((withinparents + withinbraces + (1-space))^1) - local dimension = C((space/"" + P(1))^1) - local rest = C(P(1)^0) - local scale_none = Cc(0) - local scale_at = (P("at") +P("@")) * Cc(1) * spaces * dimension -- dimension - local scale_sa = P("sa") * Cc(2) * spaces * dimension -- number - local scale_mo = P("mo") * Cc(3) * spaces * dimension -- number - local scale_scaled = P("scaled") * Cc(4) * spaces * dimension -- number - local scale_ht = P("ht") * Cc(5) * spaces * dimension -- dimension - local scale_cp = P("cp") * Cc(6) * spaces * dimension -- dimension - - specialscale = { [5] = "ht", [6] = "cp" } - - sizepattern = spaces * (scale_at + scale_sa + scale_mo + scale_ht + scale_cp + scale_scaled + scale_none) - splitpattern = spaces * value * spaces * rest - -end +----- space = P(" ") +----- spaces = space^0 +local leftparent = (P"(") +local rightparent = (P")") +local value = C((leftparent * (1-rightparent)^0 * rightparent + (1-space))^1) +local dimension = C((space/"" + P(1))^1) +local rest = C(P(1)^0) +local scale_none = Cc(0) +local scale_at = (P("at") +P("@")) * Cc(1) * spaces * dimension -- dimension +local scale_sa = P("sa") * Cc(2) * spaces * dimension -- number +local scale_mo = P("mo") * Cc(3) * spaces * dimension -- number +local scale_scaled = P("scaled") * Cc(4) * spaces * dimension -- number +local scale_ht = P("ht") * Cc(5) * spaces * dimension -- dimension +local scale_cp = P("cp") * Cc(6) * spaces * dimension -- dimension + +local specialscale = { [5] = "ht", [6] = "cp" } + +local sizepattern = spaces * (scale_at + scale_sa + scale_mo + scale_ht + scale_cp + scale_scaled + scale_none) +local splitpattern = spaces * value * spaces * rest function helpers.splitfontpattern(str) local name, size = lpegmatch(splitpattern,str) @@ -1324,6 +1299,7 @@ do -- else too many locals local busy = false scanners.definefont_two = function() + local global = scanboolean() -- \ifx\fontclass\empty\s!false\else\s!true\fi local cs = scanstring () -- {#csname}% local str = scanstring () -- \somefontfile @@ -1435,7 +1411,6 @@ do -- else too many locals specification.fallbacks = fontfallbacks end end - -- local tfmdata = definers.read(specification,size) -- id not yet known (size in spec?) -- local lastfontid = 0 @@ -1451,15 +1426,15 @@ do -- else too many locals -- characters[0x2007] = { width = characters[0x0030] and characters[0x0030].width or parameters.space } -- figure -- characters[0x2008] = { width = characters[0x002E] and characters[0x002E].width or parameters.space } -- period -- - local fallbacks = specification.fallbacks or "" + local fallbacks = specification.fallbacks local mathsize = (mathsize == 1 or mathsize == 2 or mathsize == 3) and mathsize or nil -- can be unset so we test 1 2 3 - if fallbacks ~= "" and mathsize and not busy then + if fallbacks and fallbacks ~= "" and mathsize and not busy then busy = true -- We need this ugly hack in order to resolve fontnames (at the \TEX end). Originally -- math was done in Lua after loading (plugged into aftercopying). -- -- After tl 2017 I'll also do text fallbacks this way (although backups there are done - -- in a completely different way.) + -- in a completely different way. if trace_defining then report_defining("defining %a, id %a, target %a, features %a / %a, fallbacks %a / %a, step %a", name,id,nice_cs(cs),classfeatures,fontfeatures,classfallbacks,fontfallbacks,1) @@ -1850,7 +1825,7 @@ function mappings.loadfile(name) if trace_mapfiles then report_mapfiles("loading map file %a",name) end - lpdf.setmapfile(name) + pdf.mapfile(name) loaded[name] = true end end @@ -1868,15 +1843,17 @@ function mappings.loadline(how,line) if trace_mapfiles then report_mapfiles("processing map line %a",line) end - lpdf.setmapline(how) + pdf.mapline(how) loaded[how] = true end end function mappings.reset() - lpdf.setmapfile("") -- tricky ... backend related + pdf.mapfile("") end +mappings.reset() -- resets the default file + implement { name = "loadmapfile", actions = mappings.loadfile, @@ -1899,36 +1876,19 @@ implement { -- => commands -local pattern = P("P") - * (lpeg.patterns.hexdigit^4 / function(s) return tonumber(s,16) end) - * P(-1) - -local function nametoslot(name) -- also supports PXXXXX (4+ positions) +local function nametoslot(name) local t = type(name) if t == "string" then - local unic = unicodes[true] - local slot = unic[name] - if slot then - return slot - end - -- - local slot = unic[gsub(name,"_"," ")] or unic[gsub(name,"_","-")] or - unic[gsub(name,"-"," ")] or unic[gsub(name,"-","_")] or - unic[gsub(name," ","_")] or unic[gsub(name," ","-")] + local slot = unicodes[true][name] if slot then return slot end - -- if not aglunicodes then aglunicodes = encodings.agl.unicodes end local char = characters[true] local slot = aglunicodes[name] - if slot and char[slot] then - return slot - end - local slot = lpegmatch(pattern,name) - if slot and char[slot] then + if char[slot] then return slot end -- not in font @@ -1941,13 +1901,14 @@ local function nametoslot(name) -- also supports PXXXXX (4+ positions) end end + local found = { } local function descriptiontoslot(name) local t = type(name) if t == "string" then -- slow - local list = sortedkeys(chardata) -- can be a cache with weak tables + local list = sortedkeys(chardata) local slot = found[name] local char = characters[true] if slot then @@ -2010,17 +1971,13 @@ local function descriptiontoslot(name) end end -local function indextoslot(font,index) - if not index then - index = font - font = true - end - local r = resources[font] +local function indextoslot(index) + local r = resources[true] if r then local indices = r.indices if not indices then indices = { } - local c = characters[font] + local c = characters[true] for unicode, data in next, c do local di = data.index if di then @@ -2440,12 +2397,9 @@ do return f and (f.gpos[n] or f.gsub[n]) end - local ctx_doifelse = commands.doifelse - local ctx_doif = commands.doif - implement { name = "doifelsecurrentfonthasfeature", - actions = { constructors.currentfonthasfeature, ctx_doifelse }, + actions = { constructors.currentfonthasfeature, commands.doifelse }, arguments = "string" } @@ -2489,23 +2443,10 @@ do implement { name = "definefontfeature", arguments = "3 strings", - actions = presetcontext, + actions = presetcontext } implement { - name = "doifelsefontfeature", - arguments = "string", - actions = function(name) ctx_doifelse(contextnumber(name) > 1) end, - } - - implement { - name = "doifunknownfontfeature", - arguments = "string", - actions = function(name) ctx_doif(contextnumber(name) == 0) end, - } - - - implement { name = "adaptfontfeature", arguments = "2 strings", actions = adaptcontext @@ -2661,6 +2602,8 @@ do local unsetvalue = attributes.unsetvalue + local traverse_char = nuts.traverse_char + local a_color = attributes.private('color') local a_colormodel = attributes.private('colormodel') local a_state = attributes.private('state') @@ -2684,14 +2627,11 @@ do [states.pstf] = "font:5", } - -- todo: traversers - -- todo: check attr_list so that we can use the same .. helper: setcolorattr - local function markstates(head) if head then head = tonut(head) local model = getattr(head,a_colormodel) or 1 - for glyph in nextchar, head do + for glyph in traverse_char(head) do local a = getprop(glyph,a_state) if a then local name = colornames[a] @@ -2742,8 +2682,8 @@ do function methods.nocolor(head,font,attr) - for n, c, f in nextchar, head do - if not font or f == font then + for n in traverse_char(head) do + if not font or getfont(n) == font then setattr(n,a_color,unsetvalue) end end @@ -2768,13 +2708,9 @@ implement { arguments = "string", } -local sharedstorage = storage.shared +local list = storage.shared.bodyfontsizes or { } -local list = sharedstorage.bodyfontsizes or { } -local unknown = sharedstorage.unknownbodyfontsizes or { } - -sharedstorage.bodyfontsizes = list -sharedstorage.unknownbodyfontsizes = unknown +storage.shared.bodyfontsizes = list implement { name = "registerbodyfontsize", @@ -2784,17 +2720,6 @@ implement { end } -interfaces.implement { - name = "registerunknownbodysize", - arguments = "string", - actions = function(size) - if not unknown[size] then - interfaces.showmessage("fonts",14,size) - end - unknown[size] = true - end, -} - implement { name = "getbodyfontsizes", arguments = "string", @@ -2948,12 +2873,6 @@ end -- for the font manual -statistics.register("body font sizes", function() - if next(unknown) then - return formatters["defined: % t, undefined: % t"](sortedkeys(list),sortedkeys(unknown)) - end -end) - statistics.register("used fonts",function() if trace_usage then local filename = file.nameonly(environment.jobname) .. "-fonts-usage.lua" @@ -3099,285 +3018,79 @@ end -- for the moment here (and not in font-con.lua): -do - - local identical = table.identical - local copy = table.copy - local fontdata = fonts.hashes.identifiers - local addcharacters = font.addcharacters - - -- This helper is mostly meant to add last-resort (virtual) characters - -- or runtime generated fonts (so we forget about features and such). It - -- will probably take a while before it get used. - - local trace_adding = false - local report_adding = logs.reporter("fonts","add characters") - - trackers.register("fonts.addcharacters",function(v) trace_adding = v end) - - if addcharacters then - - function fonts.constructors.addcharacters(id,list) - local newchar = list.characters - if newchar then - local data = fontdata[id] - local newfont = list.fonts - local oldchar = data.characters - local oldfont = data.fonts - addcharacters(id, { - characters = newchar, - fonts = newfont, - nomath = not data.properties.hasmath, - }) - -- this is just for tracing, as the assignment only uses the fonts list - -- and doesn't store it otherwise - if newfont then - if oldfont then - local oldn = #oldfont - local newn = #newfont - for n=1,newn do - local ok = false - local nf = newfont[n] - for o=1,oldn do - if identical(nf,oldfont[o]) then - ok = true - break - end - end - if not ok then - oldn = oldn + 1 - oldfont[oldn] = newfont[i] +local identical = table.identical +local copy = table.copy +local fontdata = fonts.hashes.identifiers +local addcharacters = font.addcharacters + +-- This helper is mostly meant to add last-resort (virtual) characters +-- or runtime generated fonts (so we forget about features and such). It +-- will probably take a while before it get used. + +local trace_adding = false +local report_adding = logs.reporter("fonts","add characters") + +trackers.register("fonts.addcharacters",function(v) trace_adding = v end) + +if addcharacters then + + function fonts.constructors.addcharacters(id,list) + local newchar = list.characters + if newchar then + local data = fontdata[id] + local newfont = list.fonts + local oldchar = data.characters + local oldfont = data.fonts + addcharacters(id, { + characters = newchar, + fonts = newfont, + nomath = not data.properties.hasmath, + }) + -- this is just for tracing, as the assignment only uses the fonts list + -- and doesn't store it otherwise + if newfont then + if oldfont then + local oldn = #oldfont + local newn = #newfont + for n=1,newn do + local ok = false + local nf = newfont[n] + for o=1,oldn do + if identical(nf,oldfont[o]) then + ok = true + break end end - else - data.fonts = newfont - end - end - -- this is because we need to know what goes on and also might - -- want to access character data - for u, c in next, newchar do - if trace_adding then - report_adding("adding character %U to font %!font:name!",u,id) + if not ok then + oldn = oldn + 1 + oldfont[oldn] = newfont[i] + end end - oldchar[u] = c - end - end - end - - else - function fonts.constructors.addcharacters(id,list) - report_adding("adding characters to %!font:name! is not yet supported",id) - end - end - - implement { - name = "addfontpath", - arguments = "string", - actions = function(list) - names.addruntimepath(settings_to_array(list)) - end - } - -end - --- moved here - -do - - local family_font = node.family_font - local new_glyph = nodes.pool.glyph - local fontproperties = fonts.hashes.properties - - local function getprivateslot(id,name) - if not name then - name = id - id = currentfont() - end - local properties = fontproperties[id] - local privates = properties and properties.privates - return privates and privates[name] - end - - local function getprivatenode(tfmdata,name) - if type(tfmdata) == "number" then - tfmdata = fontdata[tfmdata] - end - local properties = tfmdata.properties - local font = properties.id - local slot = getprivateslot(font,name) - if slot then - -- todo: set current attribibutes - local char = tfmdata.characters[slot] - local tonode = char.tonode - if tonode then - return tonode(font,char) - else - return new_glyph(font,slot) - end - end - end - - local function getprivatecharornode(tfmdata,name) - if type(tfmdata) == "number" then - tfmdata = fontdata[tfmdata] - end - local properties = tfmdata.properties - local font = properties.id - local slot = getprivateslot(font,name) - if slot then - -- todo: set current attributes - local char = tfmdata.characters[slot] - local tonode = char.tonode - if tonode then - return "node", tonode(tfmdata,char) - else - return "char", slot - end - end - end - - helpers.getprivateslot = getprivateslot - helpers.getprivatenode = getprivatenode - helpers.getprivatecharornode = getprivatecharornode - - implement { - name = "getprivatechar", - arguments = "string", - actions = function(name) - local p = getprivateslot(name) - if p then - context(utfchar(p)) - end - end - } - - implement { - name = "getprivatemathchar", - arguments = "string", - actions = function(name) - local p = getprivateslot(family_font(0),name) - if p then - context(utfchar(p)) - end - end - } - - implement { - name = "getprivateslot", - arguments = "string", - actions = function(name) - local p = getprivateslot(name) - if p then - context(p) - end - end - } - -end - --- handy, for now here: - -function fonts.helpers.collectanchors(tfmdata) - - local resources = tfmdata.resources -- todo: use shared - - if not resources or resources.anchors then - return resources.anchors - end - - local anchors = { } - - local function set(unicode,target,class,anchor) - local a = anchors[unicode] - if not a then - anchors[unicode] = { [target] = { anchor } } - return - end - local t = a[target] - if not t then - a[target] = { anchor } - return - end - local x = anchor[1] - local y = anchor[2] - for k, v in next, t do - if v[1] == x and v[2] == y then - return - end - end - t[#t+1] = anchor - end - - local function getanchors(steps,target) - for i=1,#steps do - local step = steps[i] - local coverage = step.coverage - for unicode, data in next, coverage do - local class = data[1] - local anchor = data[2] - if anchor[1] ~= 0 or anchor[2] ~= 0 then - set(unicode,target,class,anchor) - end - end - end - end - - local function getcursives(steps) - for i=1,#steps do - local step = steps[i] - local coverage = step.coverage - for unicode, data in next, coverage do - local class = data[1] - local en = data[2] - local ex = data[3] - if en then - set(unicode,"entry",class,en) - end - if ex then - set(unicode,"exit", class,ex) + else + data.fonts = newfont end end - end - end - - local function collect(list) - if list then - for i=1,#list do - local entry = list[i] - local steps = entry.steps - local kind = entry.type - if kind == "gpos_mark2mark" then - getanchors(steps,"mark") - elseif kind == "gpos_mark2base" then - getanchors(steps,"base") - elseif kind == "gpos_mark2ligature" then - getanchors(steps,"ligature") - elseif kind == "gpos_cursive" then - getcursives(steps) + -- this is because we need to know what goes on and also might + -- want to access character data + for u, c in next, newchar do + if trace_adding then + report_adding("adding character %U to font %!font:name!",u,id) end + oldchar[u] = c end end end - collect(resources.sequences) - collect(resources.sublookups) - - local function sorter(a,b) - if a[1] == b[1] then - return a[2] < b[2] - else - return a[1] < b[1] - end +else + function fonts.constructors.addcharacters(id,list) + report_adding("adding characters to %!font:name! is not yet supported",id) end +end - for unicode, old in next, anchors do - for target, list in next, old do - sort(list,sorter) - end +implement { + name = "addfontpath", + arguments = "string", + actions = function(list) + names.addruntimepath(settings_to_array(list)) end - - resources.anchors = anchors - - return anchors - -end +} |