if not modules then modules = { } end modules ['font-ctx'] = { version = 1.001, comment = "companion to font-ini.tex", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } local texsprint, count = tex.sprint, tex.count local format, concat, gmatch, match, find, lower = string.format, table.concat, string.gmatch, string.match, string.find, string.lower local tostring, next = tostring, next local ctxcatcodes = tex.ctxcatcodes local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) local tfm = fonts.tfm local define = fonts.define local fontdata = fonts.ids local specify = define.specify specify.context_setups = specify.context_setups or { } specify.context_numbers = specify.context_numbers or { } specify.context_merged = specify.context_merged or { } specify.synonyms = specify.synonyms or { } local setups = specify.context_setups local numbers = specify.context_numbers local merged = specify.context_merged local synonyms = specify.synonyms local triggers = fonts.triggers --[[ldx--

So far we haven't really dealt with features (or whatever we want to pass along with the font definition. We distinguish the following situations:

situations:

name:xetex like specs name@virtual font spec name*context specification --ldx]]-- function specify.predefined(specification) local detail = specification.detail if detail ~= "" then -- detail = detail:gsub("["..define.splitsymbols.."].*$","") -- get rid of *whatever specs and such if define.methods[detail] then -- since these may be appended at the specification.features.vtf = { preset = detail } -- tex end by default end end return specification end define.register_split("@", specify.predefined) storage.register("fonts/setups" , define.specify.context_setups , "fonts.define.specify.context_setups" ) storage.register("fonts/numbers", define.specify.context_numbers, "fonts.define.specify.context_numbers") storage.register("fonts/merged", define.specify.context_merged, "fonts.define.specify.context_merged") storage.register("fonts/synonyms", define.specify.synonyms, "fonts.define.specify.synonyms") local normalize_meanings = fonts.otf.meanings.normalize local settings_to_hash = aux.settings_to_hash local default_features = fonts.otf.features.default local function preset_context(name,parent,features) -- currently otf only if features == "" then if find(parent,"=") then features = parent parent = "" end end local number = (setups[name] and setups[name].number) or 0 local t = (features == "" and { }) or normalize_meanings(settings_to_hash(features)) -- todo: synonyms, and not otf bound if parent ~= "" then for p in gmatch(parent,"[^, ]+") do local s = setups[p] if s then for k,v in next, s do if t[k] == nil then t[k] = v end end 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 type(t[v]) == "nil" then local vv = default_features[v] if vv then t[v] = vv end end end -- sparse 'm so that we get a better hash and less test (experimental -- optimization) local tt = { } -- maybe avoid tt for k,v in next, t do if v then tt[k] = v end end -- needed for dynamic features if number == 0 then number = #numbers + 1 numbers[number] = name end tt.number = number setups[name] = tt return number end local function context_number(name) -- will be replaced local t = setups[name] if not t then return 0 elseif t.auto then local lng = tonumber(tex.language) local tag = name .. ":" .. lng local s = setups[tag] if s then return s.number or 0 else local script, language = languages.association(lng) if t.script ~= script or t.language ~= language then local s = table.fastcopy(t) local n = #numbers + 1 setups[tag] = s numbers[n] = tag s.number = n s.script = script s.language = language return n else setups[tag] = t return t.number or 0 end end else return t.number or 0 end end local function merge_context(currentnumber,extraname,option) local current = setups[numbers[currentnumber]] local extra = setups[extraname] if extra then local mergedfeatures, mergedname = { }, nil if option < 0 then if current then for k, v in next, current do if not extra[k] then mergedfeatures[k] = v end end end mergedname = currentnumber .. "-" .. extraname else 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 mergedname = currentnumber .. "+" .. extraname end local number = #numbers + 1 mergedfeatures.number = number numbers[number] = mergedname merged[number] = option setups[mergedname] = mergedfeatures return number -- context_number(mergedname) else return currentnumber end end local function register_context(fontnumber,extraname,option) local extra = setups[extraname] if extra then local mergedfeatures, mergedname = { }, nil if option < 0 then mergedname = fontnumber .. "-" .. extraname else mergedname = fontnumber .. "+" .. extraname end for k, v in next, extra do mergedfeatures[k] = v end local number = #numbers + 1 mergedfeatures.number = number numbers[number] = mergedname merged[number] = option setups[mergedname] = mergedfeatures return number -- context_number(mergedname) else return 0 end end specify.preset_context = preset_context specify.context_number = context_number specify.merge_context = merge_context specify.register_context = register_context local current_font = font.current local tex_attribute = tex.attribute local cache = { } -- concat might be less efficient than nested tables function fonts.withset(name,what) local zero = tex_attribute[0] local hash = zero .. "+" .. name .. "*" .. what local done = cache[hash] if not done then done = merge_context(zero,name,what) cache[hash] = done end tex_attribute[0] = done end function fonts.withfnt(name,what) local font = current_font() local hash = font .. "*" .. name .. "*" .. what local done = cache[hash] if not done then done = register_context(font,name,what) cache[hash] = done end tex_attribute[0] = done end function specify.show_context(name) return setups[name] or setups[numbers[name]] or setups[numbers[tonumber(name)]] or { } end local function split_context(features) return setups[features] or (preset_context(features,"","") and setups[features]) end specify.split_context = split_context function specify.context_tostring(name,kind,separator,yes,no,strict,omit) -- not used return aux.hash_to_string(table.merged(fonts[kind].features.default or {},setups[name] or {}),separator,yes,no,strict,omit) end local splitter = lpeg.splitat(",") function specify.starred(features) -- no longer fallbacks here local detail = features.detail if detail and detail ~= "" then features.features.normal = split_context(detail) else features.features.normal = { } end return features end define.register_split('*',specify.starred) -- define (two steps) local P, C, Cc = lpeg.P, lpeg.C, lpeg.Cc local space = P(" ") local spaces = space^0 local value = C((1-space)^1) local rest = C(P(1)^0) local scale_none = Cc(0) local scale_at = P("at") * Cc(1) * spaces * value local scale_sa = P("sa") * Cc(2) * spaces * value local scale_mo = P("mo") * Cc(3) * spaces * value local scale_scaled = P("scaled") * Cc(4) * spaces * value local sizepattern = spaces * (scale_at + scale_sa + scale_mo + scale_scaled + scale_none) local splitpattern = spaces * value * spaces * rest local specification -- local get_specification = define.get_specification -- we can make helper macros which saves parsing (but normaly not -- that many calls, e.g. in mk a couple of 100 and in metafun 3500) function define.command_1(str) statistics.starttiming(fonts) local fullname, size = splitpattern:match(str) local lookup, name, sub, method, detail = get_specification(fullname) if not name then logs.report("define font","strange definition '%s'",str) texsprint(ctxcatcodes,"\\fcglet\\somefontname\\defaultfontfile") elseif name == "unknown" then texsprint(ctxcatcodes,"\\fcglet\\somefontname\\defaultfontfile") else texsprint(ctxcatcodes,format("\\fcxdef\\somefontname{%s}",name)) end -- we can also use a count for the size if size and size ~= "" then local mode, size = sizepattern:match(size) if size and mode then count.scaledfontmode = mode texsprint(ctxcatcodes,format("\\def\\somefontsize{%s}",size)) else count.scaledfontmode = 0 texsprint(ctxcatcodes,format("\\let\\somefontsize\\empty",size)) end elseif true then -- so we don't need to check in tex count.scaledfontmode = 2 --~ texsprint(ctxcatcodes,format("\\def\\somefontsize{*}",size)) texsprint(ctxcatcodes,format("\\let\\somefontsize\\empty",size)) else count.scaledfontmode = 0 texsprint(ctxcatcodes,format("\\let\\somefontsize\\empty",size)) end specification = define.makespecification(str,lookup,name,sub,method,detail,size) end local n = 0 function define.command_2(global,cs,str,size,classfeatures,fontfeatures,classfallbacks,fontfallbacks,mathsize,textsize) -- name is now resolved and size is scaled cf sa/mo local lookup, name, sub, method, detail = get_specification(str or "") -- asome settings can be overloaded if lookup and lookup ~= "" then specification.lookup = lookup end specification.name = name specification.size = size specification.sub = sub specification.mathsize = mathsize specification.textsize = textsize if detail and detail ~= "" then specification.method, specification.detail = method or "*", detail elseif specification.detail and specification.detail ~= "" then -- already set elseif fontfeatures and fontfeatures ~= "" then specification.method, specification.detail = "*", fontfeatures elseif classfeatures and classfeatures ~= "" then specification.method, specification.detail = "*", classfeatures end if trace_defining then logs.report("define font","memory usage before: %s",statistics.memused()) end if fontfallbacks and fontfallbacks ~= "" then specification.fallbacks = fontfallbacks elseif classfallbacks and classfallbacks ~= "" then specification.fallbacks = classfallbacks end local tfmdata = define.read(specification,size) -- id not yet known if not tfmdata then logs.report("define font","unable to define %s as \\%s",name,cs) elseif type(tfmdata) == "number" then if trace_defining then logs.report("define font","reusing %s with id %s as \\%s (features: %s/%s, fallbacks: %s/%s)",name,tfmdata,cs,classfeatures,fontfeatures,classfallbacks,fontfallbacks) end tex.definefont(global,cs,tfmdata) -- resolved (when designsize is used): texsprint(ctxcatcodes,format("\\def\\somefontsize{%isp}",fontdata[tfmdata].size)) else -- local t = os.clock(t) local id = font.define(tfmdata) -- print(name,os.clock()-t) tfmdata.id = id define.register(tfmdata,id) tex.definefont(global,cs,id) tfm.cleanup_table(tfmdata) if trace_defining then logs.report("define font","defining %s with id %s as \\%s (features: %s/%s, fallbacks: %s/%s)",name,id,cs,classfeatures,fontfeatures,classfallbacks,fontfallbacks) end -- resolved (when designsize is used): texsprint(ctxcatcodes,format("\\def\\somefontsize{%isp}",tfmdata.size)) --~ if specification.fallbacks then --~ fonts.collections.prepare(specification.fallbacks) --~ end end if trace_defining then logs.report("define font","memory usage after: %s",statistics.memused()) end statistics.stoptiming(fonts) end --~ table.insert(readers.sequence,1,'vtf') --~ function readers.vtf(specification) --~ if specification.features.vtf and specification.features.vtf.preset then --~ return tfm.make(specification) --~ else --~ return nil --~ end --~ end