diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/font-ctx.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/font-ctx.lua | 703 |
1 files changed, 460 insertions, 243 deletions
diff --git a/Master/texmf-dist/tex/context/base/font-ctx.lua b/Master/texmf-dist/tex/context/base/font-ctx.lua index 4b324a6db79..cb640b892e2 100644 --- a/Master/texmf-dist/tex/context/base/font-ctx.lua +++ b/Master/texmf-dist/tex/context/base/font-ctx.lua @@ -8,6 +8,11 @@ if not modules then modules = { } end modules ['font-ctx'] = { -- At some point I will clean up the code here so that at the tex end -- the table interface is used. +-- +-- Todo: make a proper 'next id' mechanism (register etc) or wait till 'true' +-- in virtual fonts indices is implemented. + +local context, commands = context, commands local texcount, texsetcount = tex.count, tex.setcount local format, gmatch, match, find, lower, gsub, byte = string.format, string.gmatch, string.match, string.find, string.lower, string.gsub, string.byte @@ -15,8 +20,9 @@ local concat, serialize, sort, fastcopy, mergedtable = table.concat, table.seria local sortedhash, sortedkeys, sequenced = table.sortedhash, table.sortedkeys, table.sequenced local settings_to_hash, hash_to_string = utilities.parsers.settings_to_hash, utilities.parsers.hash_to_string local formatcolumns = utilities.formatters.formatcolumns +local mergehashes = utilities.parsers.mergehashes -local tostring, next, type = tostring, next, type +local tostring, next, type, rawget = tostring, next, type, rawget local utfchar, utfbyte = utf.char, utf.byte local round = math.round @@ -30,6 +36,7 @@ local trace_mapfiles = false trackers.register("fonts.mapfiles", functio local trace_automode = false trackers.register("fonts.automode", function(v) trace_automode = v end) local report_features = logs.reporter("fonts","features") +local report_cummulative = logs.reporter("fonts","cummulative") local report_defining = logs.reporter("fonts","defining") local report_status = logs.reporter("fonts","status") local report_mapfiles = logs.reporter("fonts","mapfiles") @@ -47,13 +54,21 @@ local loggers = fonts.loggers local fontgoodies = fonts.goodies local helpers = fonts.helpers local hashes = fonts.hashes -local fontdata = hashes.identifiers local currentfont = font.current local texattribute = tex.attribute +local fontdata = hashes.identifiers +local characters = hashes.chardata +local descriptions = hashes.descriptions +local properties = hashes.properties +local resources = hashes.resources +local csnames = hashes.csnames +local marks = hashes.markdata +local lastmathids = hashes.lastmathids + local designsizefilename = fontgoodies.designsizes.filename -local otffeatures = fonts.constructors.newfeatures("otf") +local otffeatures = handlers.otf.features local registerotffeature = otffeatures.register local baseprocessors = otffeatures.processors.base local baseinitializers = otffeatures.initializers.base @@ -77,27 +92,30 @@ storage.register("fonts/numbers", numbers, "fonts.specifiers.contextnumbers") storage.register("fonts/merged", merged, "fonts.specifiers.contextmerged") storage.register("fonts/synonyms", synonyms, "fonts.specifiers.synonyms") -constructors.resolvevirtualtoo = true -- context specific (due to resolver) +-- inspect(setups) -local allocate, mark = utilities.storage.allocate, utilities.storage.mark - -local nulldata = { - name = "nullfont", - characters = { }, - descriptions = { }, - properties = { }, - parameters = { -- lmromanregular @ 12pt - slant = 0, -- 1 - space = 256377, -- 2 - space_stretch = 128188, -- 3 - space_shrink = 85459, -- 4 - x_height = 338952, -- 5 - quad = 786432, -- 6 - extra_space = 85459, -- 7 - }, -} +if environment.initex then + setmetatableindex(setups,function(t,k) + return type(k) == "number" and rawget(t,numbers[k]) or nil + end) +else + setmetatableindex(setups,function(t,k) + local v = type(k) == "number" and rawget(t,numbers[k]) + if v then + t[k] = v + return v + end + end) +end -constructors.enhanceparameters(nulldata.parameters) -- official copies for us +-- this will move elsewhere ... + +utilities.strings.formatters.add(string.formatters,"font:name", [["'"..file.basename(%s.properties.name).."'"]]) +utilities.strings.formatters.add(string.formatters,"font:features",[["'"..table.sequenced(%s," ",true).."'"]]) + +-- ... like font-sfm or so + +constructors.resolvevirtualtoo = true -- context specific (due to resolver) local limited = false @@ -114,7 +132,7 @@ end) function definers.resetnullfont() -- resetting is needed because tikz misuses nullfont - local parameters = nulldata.parameters + local parameters = fonts.nulldata.parameters -- parameters.slant = 0 -- 1 parameters.space = 0 -- 2 @@ -131,97 +149,6 @@ end commands.resetnullfont = definers.resetnullfont -setmetatableindex(fontdata, function(t,k) return nulldata end) - --- we might make an font-hsh.lua - -local chardata = allocate() -- chardata -local descriptions = allocate() -local parameters = allocate() -local properties = allocate() -local resources = allocate() -local quaddata = allocate() -- maybe also spacedata -local markdata = allocate() -local xheightdata = allocate() -local csnames = allocate() -- namedata -local italicsdata = allocate() - -hashes.characters = chardata -hashes.descriptions = descriptions -hashes.parameters = parameters -hashes.properties = properties -hashes.resources = resources -hashes.quads = quaddata -hashes.marks = markdata -hashes.xheights = xheightdata -hashes.csnames = csnames -hashes.italics = italicsdata - -setmetatableindex(chardata, function(t,k) - local characters = fontdata[k].characters - t[k] = characters - return characters -end) - -setmetatableindex(descriptions, function(t,k) - local descriptions = fontdata[k].descriptions - t[k] = descriptions - return descriptions -end) - -setmetatableindex(parameters, function(t,k) - local parameters = fontdata[k].parameters - t[k] = parameters - return parameters -end) - -setmetatableindex(properties, function(t,k) - local properties = fontdata[k].properties - t[k] = properties - return properties -end) - -setmetatableindex(resources, function(t,k) - local shared = fontdata[k].shared - local rawdata = shared and shared.rawdata - local resources = rawdata and rawdata.resources - t[k] = resources or false -- better than resolving each time - return resources -end) - -setmetatableindex(quaddata, function(t,k) - local parameters = parameters[k] - local quad = parameters and parameters.quad or 0 - t[k] = quad - return quad -end) - -setmetatableindex(markdata, function(t,k) - local resources = fontdata[k].resources or { } - local marks = resources.marks or { } - t[k] = marks - return marks -end) - -setmetatableindex(xheightdata, function(t,k) - local parameters = parameters[k] - local xheight = parameters and parameters.xheight or 0 - t[k] = xheight - return quad -end) - -setmetatableindex(italicsdata, function(t,k) -- is test ! - local properties = fontdata[k].properties - local hasitalics = properties and properties.hasitalics - if hasitalics then - hasitalics = chardata[k] -- convenient return - else - hasitalics = false - end - t[k] = hasitalics - return hasitalics -end) - -- this cannot be a feature initializer as there is no auto namespace -- so we never enter the loop then; we can store the defaults in the tma -- file (features.gpos.mkmk = 1 etc) @@ -259,7 +186,7 @@ local function checkedscript(tfmdata,resources,features) script = latn and "latn" or "dflt" end if trace_automode then - report_defining("auto script mode: using script '%s' in font '%s'",script,file.basename(tfmdata.properties.name)) + report_defining("auto script mode, using script %a in font %!font:name!",script,tfmdata) end features.script = script return script @@ -284,13 +211,15 @@ local function checkedmode(tfmdata,resources,features) if found then -- more than one lookup if trace_automode then - report_defining("forcing node mode in font %s for feature %s, script %s, language %s (multiple lookups)",file.basename(tfmdata.properties.name),feature,script,language) + report_defining("forcing mode %a, font %!font:name!, feature %a, script %a, language %a, %s", + "node",tfmdata,feature,script,language,"multiple lookups") end features.mode = "node" return "node" elseif needsnodemode[sequence.type] then if trace_automode then - report_defining("forcing node mode in font %s for feature %s, script %s, language %s (no base support)",file.basename(tfmdata.properties.name),feature,script,language) + report_defining("forcing mode %a, font %!font:name!, feature %a, script %a, language %a, %s", + "node",tfmdata,feature,script,language,"no base support") end features.mode = "node" return "node" @@ -314,7 +243,7 @@ definers.checkedmode = checkedmode local function modechecker(tfmdata,features,mode) -- we cannot adapt features as they are shared! if trace_features then - report_features(serialize(features,"used")) + report_features("fontname %!font:name!, features %!font:features!",tfmdata,features) end local rawdata = tfmdata.shared.rawdata local resources = rawdata and rawdata.resources @@ -327,7 +256,7 @@ local function modechecker(tfmdata,features,mode) -- we cannot adapt features as mode = checkedmode(tfmdata,resources,features) end else - report_features("missing resources for font''%s'",file.basename(tfmdata.properties.name)) + report_features("missing resources for font %!font:name!",tfmdata) end return mode end @@ -440,12 +369,23 @@ definers.registersplit("@", predefined,"virtual") local normalize_features = otffeatures.normalize -- should be general +local function definecontext(name,t) -- can be shared + local number = setups[name] and setups[name].number or 0 -- hm, numbers[name] + if number == 0 then + number = #numbers + 1 + numbers[number] = name + end + t.number = number + setups[name] = t + return number, t +end + local function presetcontext(name,parent,features) -- will go to con and shared if features == "" and find(parent,"=") then features = parent parent = "" end - if features == "" then + if not features or features == "" then features = { } elseif type(features) == "string" then features = normalize_features(settings_to_hash(features)) @@ -462,18 +402,22 @@ local function presetcontext(name,parent,features) -- will go to con and shared features[k] = v end end + else + -- just ignore an undefined one .. i.e. we can refer to not yet defined end end end -- these are auto set so in order to prevent redundant definitions -- we need to preset them (we hash the features and adding a default -- setting during initialization may result in a different hash) ---~ for k,v in next, triggers do ---~ if features[v] == nil then -- not false ! ---~ local vv = default_features[v] ---~ if vv then features[v] = vv end ---~ end ---~ end + -- + -- for k,v in next, triggers do + -- if features[v] == nil then -- not false ! + -- local vv = default_features[v] + -- if vv then features[v] = vv end + -- end + -- end + -- for feature,value in next, features do if value == nil then -- not false ! local default = default_features[feature] @@ -486,11 +430,12 @@ local function presetcontext(name,parent,features) -- will go to con and shared -- optimization) local t = { } -- can we avoid t ? for k,v in next, features do - if v then t[k] = v end +-- if v then t[k] = v end + t[k] = v end -- needed for dynamic features -- maybe number should always be renewed as we can redefine features - local number = (setups[name] and setups[name].number) or 0 -- hm, numbers[name] + local number = setups[name] and setups[name].number or 0 -- hm, numbers[name] if number == 0 then number = #numbers + 1 numbers[number] = name @@ -531,10 +476,10 @@ local function contextnumber(name) -- will be replaced end end -local function mergecontext(currentnumber,extraname,option) - local current = setups[numbers[currentnumber]] +local function mergecontext(currentnumber,extraname,option) -- number string number (used in scrp-ini local extra = setups[extraname] if extra then + local current = setups[numbers[currentnumber]] local mergedfeatures, mergedname = { }, nil if option < 0 then if current then @@ -567,6 +512,56 @@ local function mergecontext(currentnumber,extraname,option) end end +local extrasets = { } + +setmetatableindex(extrasets,function(t,k) + local v = mergehashes(setups,k) + t[k] = v + return v +end) + +local function mergecontextfeatures(currentname,extraname,how,mergedname) -- string string + local extra = setups[extraname] or extrasets[extraname] + if extra then + local current = setups[currentname] + local mergedfeatures = { } + if how == "+" then + if current then + for k, v in next, current do + mergedfeatures[k] = v + end + end + for k, v in next, extra do + mergedfeatures[k] = v + end + elseif how == "-" then + if current then + for k, v in next, current do + mergedfeatures[k] = v + end + end + for k, v in next, extra do + -- only boolean features + if v == true then + mergedfeatures[k] = false + end + end + else -- = + for k, v in next, extra do + mergedfeatures[k] = v + end + end + local number = #numbers + 1 + mergedfeatures.number = number + numbers[number] = mergedname + merged[number] = option + setups[mergedname] = mergedfeatures + return number + else + return numbers[currentname] or 0 + end +end + local function registercontext(fontnumber,extraname,option) local extra = setups[extraname] if extra then @@ -590,10 +585,29 @@ local function registercontext(fontnumber,extraname,option) end end +local function registercontextfeature(mergedname,extraname,how) + local extra = setups[extraname] + if extra then + local mergedfeatures = { } + for k, v in next, extra do + mergedfeatures[k] = v + end + local number = #numbers + 1 + mergedfeatures.number = number + numbers[number] = mergedname + merged[number] = how == "=" and 1 or 2 -- 1=replace, 2=combine + setups[mergedname] = mergedfeatures + return number -- contextnumber(mergedname) + else + return 0 + end +end + specifiers.presetcontext = presetcontext specifiers.contextnumber = contextnumber specifiers.mergecontext = mergecontext specifiers.registercontext = registercontext +specifiers.definecontext = definecontext -- we extend the hasher: @@ -602,7 +616,7 @@ constructors.hashmethods.virtual = function(list) local n = 0 for k, v in next, list do n = n + 1 - s[n] = k + s[n] = k -- no checking on k end if n > 0 then sort(s) @@ -616,83 +630,96 @@ end -- end of redefine -local cache = { } -- concat might be less efficient than nested tables - -local function withset(name,what) - local zero = texattribute[0] - local hash = zero .. "+" .. name .. "*" .. what - local done = cache[hash] - if not done then - done = mergecontext(zero,name,what) - cache[hash] = done - end - texattribute[0] = done -end - -local function withfnt(name,what) - local font = currentfont() - local hash = font .. "*" .. name .. "*" .. what - local done = cache[hash] - if not done then - done = registercontext(font,name,what) - cache[hash] = done - end - texattribute[0] = done -end +-- local withcache = { } -- concat might be less efficient than nested tables +-- +-- local function withset(name,what) +-- local zero = texattribute[0] +-- local hash = zero .. "+" .. name .. "*" .. what +-- local done = withcache[hash] +-- if not done then +-- done = mergecontext(zero,name,what) +-- withcache[hash] = done +-- end +-- texattribute[0] = done +-- end +-- +-- local function withfnt(name,what,font) +-- local font = font or currentfont() +-- local hash = font .. "*" .. name .. "*" .. what +-- local done = withcache[hash] +-- if not done then +-- done = registercontext(font,name,what) +-- withcache[hash] = done +-- end +-- texattribute[0] = done +-- end function specifiers.showcontext(name) return setups[name] or setups[numbers[name]] or setups[numbers[tonumber(name)]] or { } end --- todo: support a,b,c - -- we need a copy as we will add (fontclass) goodies to the features and -- that is bad for a shared table +-- local function splitcontext(features) -- presetcontext creates dummy here +-- return fastcopy(setups[features] or (presetcontext(features,"","") and setups[features])) +-- end + local function splitcontext(features) -- presetcontext creates dummy here - return fastcopy(setups[features] or (presetcontext(features,"","") and setups[features])) + local sf = setups[features] + if not sf then + local n -- number + if find(features,",") then + -- let's assume a combination which is not yet defined but just specified (as in math) + n, sf = presetcontext(features,features,"") + else + -- we've run into an unknown feature and or a direct spec so we create a dummy + n, sf = presetcontext(features,"","") + end + end + return fastcopy(sf) end ---~ local splitter = lpeg.splitat("=") - ---~ local function splitcontext(features) ---~ local setup = setups[features] ---~ if setup then ---~ return setup ---~ elseif find(features,",") then ---~ -- This is not that efficient but handy anyway for quick and dirty tests ---~ -- beware, due to the way of caching setups you can get the wrong results ---~ -- when components change. A safeguard is to nil the cache. ---~ local merge = nil ---~ for feature in gmatch(features,"[^, ]+") do ---~ if find(feature,"=") then ---~ local k, v = lpegmatch(splitter,feature) ---~ if k and v then ---~ if not merge then ---~ merge = { k = v } ---~ else ---~ merge[k] = v ---~ end ---~ end ---~ else ---~ local s = setups[feature] ---~ if not s then ---~ -- skip ---~ elseif not merge then ---~ merge = s ---~ else ---~ for k, v in next, s do ---~ merge[k] = v ---~ end ---~ end ---~ end ---~ end ---~ setup = merge and presetcontext(features,"",merge) and setups[features] ---~ -- actually we have to nil setups[features] in order to permit redefinitions ---~ setups[features] = nil ---~ end ---~ return setup or (presetcontext(features,"","") and setups[features]) -- creates dummy ---~ end +-- local splitter = lpeg.splitat("=") +-- +-- local function splitcontext(features) +-- local setup = setups[features] +-- if setup then +-- return setup +-- elseif find(features,",") then +-- -- This is not that efficient but handy anyway for quick and dirty tests +-- -- beware, due to the way of caching setups you can get the wrong results +-- -- when components change. A safeguard is to nil the cache. +-- local merge = nil +-- for feature in gmatch(features,"[^, ]+") do +-- if find(feature,"=") then +-- local k, v = lpegmatch(splitter,feature) +-- if k and v then +-- if not merge then +-- merge = { k = v } +-- else +-- merge[k] = v +-- end +-- end +-- else +-- local s = setups[feature] +-- if not s then +-- -- skip +-- elseif not merge then +-- merge = s +-- else +-- for k, v in next, s do +-- merge[k] = v +-- end +-- end +-- end +-- end +-- setup = merge and presetcontext(features,"",merge) and setups[features] +-- -- actually we have to nil setups[features] in order to permit redefinitions +-- setups[features] = nil +-- end +-- return setup or (presetcontext(features,"","") and setups[features]) -- creates dummy +-- end specifiers.splitcontext = splitcontext @@ -750,6 +777,18 @@ local scale_scaled = P("scaled") * Cc(4) * spaces * dimension -- value local sizepattern = spaces * (scale_at + scale_sa + scale_mo + scale_scaled + scale_none) local splitpattern = spaces * value * spaces * rest +function helpers.splitfontpattern(str) + local name, size = lpegmatch(splitpattern,str) + local kind, size = lpegmatch(sizepattern,size) + return name, kind, size +end + +function helpers.fontpatternhassize(str) + local name, size = lpegmatch(splitpattern,str) + local kind, size = lpegmatch(sizepattern,size) + return size or false +end + local specification -- still needed as local ? local getspecification = definers.getspecification @@ -761,6 +800,7 @@ local setdefaultfontname = context.fntsetdefname local setsomefontname = context.fntsetsomename local setemptyfontsize = context.fntsetnopsize local setsomefontsize = context.fntsetsomesize +local letvaluerelax = context.letvaluerelax function commands.definefont_one(str) statistics.starttiming(fonts) @@ -771,7 +811,7 @@ function commands.definefont_one(str) local fullname, size = lpegmatch(splitpattern,str) local lookup, name, sub, method, detail = getspecification(fullname) if not name then - report_defining("strange definition '%s'",str) + report_defining("strange definition %a",str) setdefaultfontname() elseif name == "unknown" then setdefaultfontname() @@ -814,7 +854,7 @@ end function commands.definefont_two(global,cs,str,size,inheritancemode,classfeatures,fontfeatures,classfallbacks,fontfallbacks, mathsize,textsize,relativeid,classgoodies,goodies,classdesignsize,fontdesignsize) if trace_defining then - report_defining("start stage two: %s (%s)",str,size) + report_defining("start stage two: %s (size %s)",str,size) end -- name is now resolved and size is scaled cf sa/mo local lookup, name, sub, method, detail = getspecification(str or "") @@ -823,7 +863,7 @@ function commands.definefont_two(global,cs,str,size,inheritancemode,classfeature local designname = designsizefilename(name,designsize,size) if designname and designname ~= "" then if trace_defining or trace_designsize then - report_defining("remapping name: %s, specification: %s, size: %s => designsize: %s",name,designsize,size,designname) + report_defining("remapping name %a, specification %a, size %a, designsize %a",name,designsize,size,designname) end -- we don't catch detail here local o_lookup, o_name, o_sub, o_method, o_detail = getspecification(designname) @@ -905,27 +945,28 @@ function commands.definefont_two(global,cs,str,size,inheritancemode,classfeature end end local tfmdata = definers.read(specification,size) -- id not yet known (size in spec?) + -- + local lastfontid = 0 if not tfmdata then - report_defining("unable to define %s as [%s]",name,nice_cs(cs)) - texsetcount("global","lastfontid",-1) - context.letvaluerelax(cs) -- otherwise the current definition takes the previous one + report_defining("unable to define %a as %a",name,nice_cs(cs)) + lastfontid = -1 + letvaluerelax(cs) -- otherwise the current definition takes the previous one elseif type(tfmdata) == "number" then if trace_defining then - report_defining("reusing %s with id %s as [%s] (features: %s/%s, fallbacks: %s/%s, goodies: %s/%s, designsize: %s/%s)", + report_defining("reusing %s, id %a, target %a, features %a / %a, fallbacks %a / %a, goodies %a / %a, designsize %a / %a", name,tfmdata,nice_cs(cs),classfeatures,fontfeatures,classfallbacks,fontfallbacks,classgoodies,goodies,classdesignsize,fontdesignsize) end csnames[tfmdata] = specification.cs tex.definefont(global,cs,tfmdata) -- resolved (when designsize is used): setsomefontsize(fontdata[tfmdata].parameters.size .. "sp") - texsetcount("global","lastfontid",tfmdata) + lastfontid = tfmdata else -- setting the extra characters will move elsewhere local characters = tfmdata.characters local parameters = tfmdata.parameters - -- we use char0 as signal + -- we use char0 as signal; cf the spec pdf can handle this (no char in slot) characters[0] = nil - -- cf the spec pdf can handle this (no char in slot) -- characters[0x00A0] = { width = parameters.space } -- 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 @@ -938,20 +979,27 @@ function commands.definefont_two(global,cs,str,size,inheritancemode,classfeature constructors.cleanuptable(tfmdata) constructors.finalize(tfmdata) if trace_defining then - report_defining("defining %s with id %s as [%s] (features: %s/%s, fallbacks: %s/%s)", + report_defining("defining %a, id %a, target %a, features %a / %a, fallbacks %a / %a", name,id,nice_cs(cs),classfeatures,fontfeatures,classfallbacks,fontfallbacks) end -- resolved (when designsize is used): setsomefontsize((tfmdata.parameters.size or 655360) .. "sp") - --~ if specification.fallbacks then - --~ fonts.collections.prepare(specification.fallbacks) - --~ end - texsetcount("global","lastfontid",id) + lastfontid = id end if trace_defining then report_defining("memory usage after: %s",statistics.memused()) report_defining("stop stage two") end + -- + texsetcount("global","lastfontid",lastfontid) + if not mathsize then + -- forget about it + elseif mathsize == 0 then + lastmathids[1] = lastfontid + else + lastmathids[mathsize] = lastfontid + end + -- statistics.stoptiming(fonts) end @@ -1030,17 +1078,17 @@ local calculatescale = constructors.calculatescale function constructors.calculatescale(tfmdata,scaledpoints,relativeid) local scaledpoints, delta = calculatescale(tfmdata,scaledpoints) ---~ if enable_auto_r_scale and relativeid then -- for the moment this is rather context specific ---~ local relativedata = fontdata[relativeid] ---~ local rfmdata = relativedata and relativedata.unscaled and relativedata.unscaled ---~ local id_x_height = rfmdata and rfmdata.parameters and rfmdata.parameters.x_height ---~ local tf_x_height = tfmdata and tfmdata.parameters and tfmdata.parameters.x_height ---~ if id_x_height and tf_x_height then ---~ local rscale = id_x_height/tf_x_height ---~ delta = rscale * delta ---~ scaledpoints = rscale * scaledpoints ---~ end ---~ end + -- if enable_auto_r_scale and relativeid then -- for the moment this is rather context specific + -- local relativedata = fontdata[relativeid] + -- local rfmdata = relativedata and relativedata.unscaled and relativedata.unscaled + -- local id_x_height = rfmdata and rfmdata.parameters and rfmdata.parameters.x_height + -- local tf_x_height = tfmdata and tfmdata.parameters and tfmdata.parameters.x_height + -- if id_x_height and tf_x_height then + -- local rscale = id_x_height/tf_x_height + -- delta = rscale * delta + -- scaledpoints = rscale * scaledpoints + -- end + -- end return scaledpoints, delta end @@ -1103,7 +1151,7 @@ function mappings.loadfile(name) name = file.addsuffix(name,"map") if not loaded[name] then if trace_mapfiles then - report_mapfiles("loading map file '%s'",name) + report_mapfiles("loading map file %a",name) end pdf.mapfile(name) loaded[name] = true @@ -1121,7 +1169,7 @@ function mappings.loadline(how,line) end if not loaded[how] then if trace_mapfiles then - report_mapfiles("processing map line '%s'",line) + report_mapfiles("processing map line %a",line) end pdf.mapline(how) loaded[how] = true @@ -1141,10 +1189,7 @@ mappings.reset() -- resets the default file local function nametoslot(name) local t = type(name) if t == "string" then - local tfmdata = fonts.hashes.identifiers[currentfont()] - local shared = tfmdata and tfmdata.shared - local fntdata = shared and shared.rawdata - return fntdata and fntdata.resources.unicodes[name] + return resources[true].unicodes[name] elseif t == "number" then return n end @@ -1162,7 +1207,7 @@ function loggers.reportdefinedfonts() local parameters = data.parameters or { } tn = tn + 1 t[tn] = { - format("%03i",id or 0), + format("%03i",id or 0), format("%09i",parameters.size or 0), properties.type or "real", properties.format or "unknown", @@ -1170,7 +1215,7 @@ function loggers.reportdefinedfonts() properties.psname or "", properties.fullname or "", } -report_status("%s: %s",properties.name,concat(sortedkeys(data)," ")) + report_status("%s: % t",properties.name,sortedkeys(data)) end formatcolumns(t," ") report_status() @@ -1320,15 +1365,10 @@ function commands.resetfontfeature() texattribute[0] = 0 end -function commands.addfs(tag) withset(tag, 1) end -function commands.subfs(tag) withset(tag,-1) end -function commands.addff(tag) withfnt(tag, 2) end -function commands.subff(tag) withfnt(tag,-2) end - --- function commands.addfontfeaturetoset (tag) withset(tag, 1) end --- function commands.subtractfontfeaturefromset (tag) withset(tag,-1) end --- function commands.addfontfeaturetofont (tag) withfnt(tag, 2) end --- function commands.subtractfontfeaturefromfont(tag) withfnt(tag,-2) end +-- function commands.addfs(tag) withset(tag, 1) end +-- function commands.subfs(tag) withset(tag,-1) end +-- function commands.addff(tag) withfnt(tag, 2) end -- on top of font features +-- function commands.subff(tag) withfnt(tag,-2) end -- on top of font features function commands.cleanfontname (name) context(names.cleanname(name)) end @@ -1347,7 +1387,7 @@ function commands.showchardata(n) end local chr = tfmdata.characters[n] if chr then - report_status("%s @ %s => U%05X => %s => %s",tfmdata.properties.fullname,tfmdata.parameters.size,n,utfchar(n),serialize(chr,false)) + report_status("%s @ %s => %U => %c => %s",tfmdata.properties.fullname,tfmdata.parameters.size,n,n,serialize(chr,false)) end end end @@ -1384,7 +1424,8 @@ function helpers.dimenfactor(unit,tfmdata) -- could be a method of a font instan elseif unit == "em" then return (tfmdata and tfmdata.parameters.em_width) or 655360 else - return dimenfactors[unit] or unit + local du = dimenfactors[unit] + return du and 1/du or tonumber(unit) or 1 end end @@ -1536,8 +1577,184 @@ end -- more interfacing: -commands.definefontfeature = fonts.specifiers.presetcontext +commands.definefontfeature = presetcontext + +local cache = { } + +local hows = { + ["+"] = "add", + ["-"] = "subtract", + ["="] = "replace", +} + +function commands.feature(how,parent,name,font) + if not how then + if trace_features and texattribute[0] ~= 0 then + report_cummulative("font %!font:name!, reset",fontdata[font or true]) + end + texattribute[0] = 0 + elseif how == true then + local hash = "feature > " .. parent + local done = cache[hash] + if trace_features and done then + report_cummulative("font %!font:name!, revive %a : %!font:features!",fontdata[font or true],parent,setups[numbers[done]]) + end + texattribute[0] = done or 0 + else + local full = parent .. how .. name + local hash = "feature > " .. full + local done = cache[hash] + if not done then + local n = setups[full] + if n then + -- already defined + else + n = mergecontextfeatures(parent,name,how,full) + end + done = registercontextfeature(hash,full,how) + cache[hash] = done + if trace_features then + report_cummulative("font %!font:name!, %s %a : %!font:features!",fontdata[font or true],hows[how],full,setups[numbers[done]]) + end + end + texattribute[0] = done + end +end function commands.featurelist(...) context(fonts.specifiers.contexttostring(...)) end + +function commands.registerlanguagefeatures() + local specifications = languages.data.specifications + for i=1,#specifications do + local specification = specifications[i] + local language = specification.opentype + if language then + local script = specification.opentypescript or specification.script + if script then + local context = specification.context + if type(context) == "table" then + for i=1,#context do + definecontext(context[i], { language = language, script = script}) + end + elseif type(context) == "string" then + definecontext(context, { language = language, script = script}) + end + end + end + end +end + +-- a fontkern plug: + +local copy_node = node.copy +local kern = nodes.pool.register(nodes.pool.kern()) + +node.set_attribute(kern,attributes.private('fontkern'),1) -- we can have several, attributes are shared + +nodes.injections.installnewkern(function(k) + local c = copy_node(kern) + c.kern = k + return c +end) + +directives.register("nodes.injections.fontkern", function(v) kern.subtype = v and 0 or 1 end) + +-- here + +local trace_analyzing = false trackers.register("otf.analyzing", function(v) trace_analyzing = v end) + +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register + +local analyzers = fonts.analyzers +local methods = analyzers.methods + +local unsetvalue = attributes.unsetvalue + +local traverse_by_id = node.traverse_id + +local a_color = attributes.private('color') +local a_colormodel = attributes.private('colormodel') +local a_state = attributes.private('state') +local m_color = attributes.list[a_color] or { } + +local glyph_code = nodes.nodecodes.glyph + +local states = analyzers.states + +local names = { + [states.init] = "font:1", + [states.medi] = "font:2", + [states.fina] = "font:3", + [states.isol] = "font:4", + [states.mark] = "font:5", + [states.rest] = "font:6", + [states.rphf] = "font:1", + [states.half] = "font:2", + [states.pref] = "font:3", + [states.blwf] = "font:4", + [states.pstf] = "font:5", +} + +local function markstates(head) + if head then + local model = head[a_colormodel] or 1 + for glyph in traverse_by_id(glyph_code,head) do + local a = glyph[a_state] + if a then + local name = names[a] + if name then + local color = m_color[name] + if color then + glyph[a_colormodel] = model + glyph[a_color] = color + end + end + end + end + end +end + +local function analyzeprocessor(head,font,attr) + local tfmdata = fontdata[font] + local script, language = otf.scriptandlanguage(tfmdata,attr) + local action = methods[script] + if not action then + return head, false + end + if type(action) == "function" then + local head, done = action(head,font,attr) + if done and trace_analyzing then + markstates(head) + end + return head, done + end + action = action[language] + if action then + local head, done = action(head,font,attr) + if done and trace_analyzing then + markstates(head) + end + return head, done + else + return head, false + end +end + +registerotffeature { -- adapts + name = "analyze", + processors = { + node = analyzeprocessor, + } +} + +function methods.nocolor(head,font,attr) + for n in traverse_by_id(glyph_code,head) do + if not font or n.font == font then + n[a_color] = unsetvalue + end + end + return head, true +end |