diff options
author | Hans Hagen <pragma@wxs.nl> | 2013-05-20 02:00:00 +0200 |
---|---|---|
committer | Hans Hagen <pragma@wxs.nl> | 2013-05-20 02:00:00 +0200 |
commit | bd95a21d2b31a5fab1b4cc7c2b0334823fb3a3e9 (patch) | |
tree | 831128c411476f077eb7910d8c08f524d3ee43ec /tex/context/base/font-gds.lua | |
parent | 9a10021cd4cb23995ad3ffa915fc5b7f6890aaf8 (diff) | |
download | context-bd95a21d2b31a5fab1b4cc7c2b0334823fb3a3e9.tar.gz |
beta 2013.05.20 02:00
Diffstat (limited to 'tex/context/base/font-gds.lua')
-rw-r--r-- | tex/context/base/font-gds.lua | 1504 |
1 files changed, 752 insertions, 752 deletions
diff --git a/tex/context/base/font-gds.lua b/tex/context/base/font-gds.lua index 4eb57bfa4..6332f40b0 100644 --- a/tex/context/base/font-gds.lua +++ b/tex/context/base/font-gds.lua @@ -1,752 +1,752 @@ -if not modules then modules = { } end modules ['font-gds'] = {
- version = 1.000,
- comment = "companion to font-gds.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- depends on ctx
-
-local type, next, tonumber = type, next, tonumber
-local gmatch, format, lower, find, splitup = string.gmatch, string.format, string.lower, string.find, string.splitup
-local texsp = tex.sp
-
-local fonts, nodes, attributes, node = fonts, nodes, attributes, node
-
-local trace_goodies = false trackers.register("fonts.goodies", function(v) trace_goodies = v end)
-local report_goodies = logs.reporter("fonts","goodies")
-
-local allocate = utilities.storage.allocate
-
-local otf = fonts.handlers.otf
-local afm = fonts.handlers.afm
-local tfm = fonts.handlers.tfm
-
-local registerotffeature = otf.features.register
-local registerafmfeature = afm.features.register
-local registertfmfeature = tfm.features.register
-
-local fontgoodies = fonts.goodies or { }
-fonts.goodies = fontgoodies
-
-local typefaces = fonts.typefaces or allocate()
-fonts.typefaces = typefaces
-
-local data = fontgoodies.data or allocate()
-fontgoodies.data = data
-
-local list = fontgoodies.list or { }
-fontgoodies.list = list -- no allocate as we want to see what is there
-
-local addotffeature = otf.enhancers.addfeature
-
-local findfile = resolvers.findfile
-
-function fontgoodies.report(what,trace,goodies)
- if trace_goodies or trace then
- local whatever = goodies[what]
- if whatever then
- report_goodies("goodie %a found in %a",what,goodies.name)
- end
- end
-end
-
-local function loadgoodies(filename) -- maybe a merge is better
- local goodies = data[filename] -- we assume no suffix is given
- if goodies ~= nil then
- -- found or tagged unfound
- elseif type(filename) == "string" then
- local fullname = findfile(file.addsuffix(filename,"lfg")) or "" -- prefered suffix
- if fullname == "" then
- fullname = findfile(file.addsuffix(filename,"lua")) or "" -- fallback suffix
- end
- if fullname == "" then
- report_goodies("goodie file '%s.lfg' is not found",filename)
- data[filename] = false -- signal for not found
- else
- goodies = dofile(fullname) or false
- if not goodies then
- report_goodies("goodie file %a is invalid",fullname)
- return nil
- elseif trace_goodies then
- report_goodies("goodie file %a is loaded",fullname)
- end
- goodies.name = goodies.name or "no name"
- for name, fnc in next, list do
- fnc(goodies)
- end
- goodies.initialized = true
- data[filename] = goodies
- end
- end
- return goodies
-end
-
-function fontgoodies.register(name,fnc) -- will be a proper sequencer
- list[name] = fnc
-end
-
-fontgoodies.load = loadgoodies
-
--- register goodies file
-
-local function setgoodies(tfmdata,value)
- local goodies = tfmdata.goodies
- if not goodies then -- actually an error
- goodies = { }
- tfmdata.goodies = goodies
- end
- for filename in gmatch(value,"[^, ]+") do
- -- we need to check for duplicates
- local ok = loadgoodies(filename)
- if ok then
- if trace_goodies then
- report_goodies("assigning goodie %a",filename)
- end
- goodies[#goodies+1] = ok
- end
- end
-end
-
--- this will be split into good-* files and this file might become good-ini.lua
-
--- featuresets
-
-local function flattenedfeatures(t,tt)
- -- first set value dominates
- local tt = tt or { }
- for i=1,#t do
- local ti = t[i]
- if type(ti) == "table" then
- flattenedfeatures(ti,tt)
- elseif tt[ti] == nil then
- tt[ti] = true
- end
- end
- for k, v in next, t do
- if type(k) ~= "number" then -- not tonumber(k)
- if type(v) == "table" then
- flattenedfeatures(v,tt)
- elseif tt[k] == nil then
- tt[k] = v
- end
- end
- end
- return tt
-end
-
--- fonts.features.flattened = flattenedfeatures
-
-local function prepare_features(goodies,name,set)
- if set then
- local ff = flattenedfeatures(set)
- local fullname = goodies.name .. "::" .. name
- local n, s = fonts.specifiers.presetcontext(fullname,"",ff)
- goodies.featuresets[name] = s -- set
- if trace_goodies then
- report_goodies("feature set %a gets number %a and name %a",name,n,fullname)
- end
- return n
- end
-end
-
-fontgoodies.prepare_features = prepare_features
-
-local function initialize(goodies,tfmdata)
- local featuresets = goodies.featuresets
- local goodiesname = goodies.name
- if featuresets then
- if trace_goodies then
- report_goodies("checking featuresets in %a",goodies.name)
- end
- for name, set in next, featuresets do
- prepare_features(goodies,name,set)
- end
- end
-end
-
-fontgoodies.register("featureset",initialize)
-
-local function setfeatureset(tfmdata,set,features)
- local goodies = tfmdata.goodies -- shared ?
- if goodies then
- local properties = tfmdata.properties
- local what
- for i=1,#goodies do
- -- last one wins
- local g = goodies[i]
- what = g.featuresets and g.featuresets[set] or what
- end
- if what then
- for feature, value in next, what do
- if features[feature] == nil then
- features[feature] = value
- end
- end
- properties.mode = what.mode or properties.mode
- end
- end
-end
-
--- postprocessors (we could hash processor and share code)
-
-function fontgoodies.registerpostprocessor(tfmdata,f,prepend)
- local postprocessors = tfmdata.postprocessors
- if not postprocessors then
- tfmdata.postprocessors = { f }
- elseif prepend then
- table.insert(postprocessors,f,1)
- else
- table.insert(postprocessors,f)
- end
-end
-
-local function setpostprocessor(tfmdata,processor)
- local goodies = tfmdata.goodies
- if goodies and type(processor) == "string" then
- local found = { }
- local asked = utilities.parsers.settings_to_array(processor)
- for i=1,#goodies do
- local g = goodies[i]
- local p = g.postprocessors
- if p then
- for i=1,#asked do
- local a = asked[i]
- local f = p[a]
- if type(f) == "function" then
- found[a] = f
- end
- end
- end
- end
- local postprocessors = tfmdata.postprocessors or { }
- for i=1,#asked do
- local a = asked[i]
- local f = found[a]
- if f then
- postprocessors[#postprocessors+1] = f
- end
- end
- if #postprocessors > 0 then
- tfmdata.postprocessors = postprocessors
- end
- end
-end
-
--- colorschemes
-
-local colorschemes = fontgoodies.colorschemes or allocate { }
-fontgoodies.colorschemes = colorschemes
-colorschemes.data = colorschemes.data or { }
-
-local function setcolorscheme(tfmdata,scheme)
- if type(scheme) == "string" then
- local goodies = tfmdata.goodies
- -- todo : check for already defined in shared
- if goodies then
- local what
- for i=1,#goodies do
- -- last one counts
- local g = goodies[i]
- what = g.colorschemes and g.colorschemes[scheme] or what
- end
- if type(what) == "table" then
- -- this is font bound but we can share them if needed
- -- just as we could hash the conversions (per font)
- local hash = tfmdata.resources.unicodes
- local reverse = { }
- local characters = tfmdata.characters
- for i=1,#what do
- local w = what[i]
- for j=1,#w do
- local name = w[j]
- if name == "*" then
- -- inefficient but only used for tracing anyway
- for _, unicode in next, hash do
- reverse[unicode] = i
- end
- elseif type(name) == "number" then
- reverse[name] = i
- elseif find(name,":") then
- local start, stop = splitup(name,":")
- start = tonumber(start)
- stop = tonumber(stop)
- if start and stop then
- -- limited usage: we only deal with non reassigned
- -- maybe some day I'll also support the ones with a
- -- tounicode in this range
- for unicode=start,stop do
- if characters[unicode] then
- reverse[unicode] = i
- end
- end
- end
- else
- local unicode = hash[name]
- if unicode then
- reverse[unicode] = i
- end
- end
- end
- end
- tfmdata.properties.colorscheme = reverse
- return
- end
- end
- end
- tfmdata.properties.colorscheme = false
-end
-
-local fontdata = fonts.hashes.identifiers
-local setnodecolor = nodes.tracers.colors.set
-local traverse_id = node.traverse_id
-local a_colorscheme = attributes.private('colorscheme')
-local glyph = node.id("glyph")
-
-function colorschemes.coloring(head)
- local lastfont, lastscheme
- local done = false
- for n in traverse_id(glyph,head) do
- local a = n[a_colorscheme]
- if a then
- local f = n.font
- if f ~= lastfont then
- lastscheme, lastfont = fontdata[f].properties.colorscheme, f
- end
- if lastscheme then
- local sc = lastscheme[n.char]
- if sc then
- done = true
- setnodecolor(n,"colorscheme:"..a..":"..sc) -- slow
- end
- end
- end
- end
- return head, done
-end
-
-function colorschemes.enable()
- nodes.tasks.appendaction("processors","fonts","fonts.goodies.colorschemes.coloring")
- function colorschemes.enable() end
-end
-
-local function setextrafeatures(tfmdata)
- local goodies = tfmdata.goodies
- if goodies then
- for i=1,#goodies do
- local g = goodies[i]
- local f = g.features
- if f then
- for feature, specification in next, f do
- addotffeature(tfmdata.shared.rawdata,feature,specification)
- registerotffeature {
- name = feature,
- description = format("extra: %s",feature)
- }
- end
- end
- end
- end
-end
-
--- installation (collected to keep the overview) -- also for type 1
-
-registerotffeature {
- name = "goodies",
- description = "goodies on top of built in features",
- initializers = {
- position = 1,
- base = setgoodies,
- node = setgoodies,
- }
-}
-
-registerotffeature {
- name = "extrafeatures",
- description = "extra features",
- default = true,
- initializers = {
- position = 2,
- base = setextrafeatures,
- node = setextrafeatures,
- }
-}
-
-registerotffeature {
- name = "featureset",
- description = "goodie feature set",
- initializers = {
- position = 3,
- base = setfeatureset,
- node = setfeatureset,
- }
-}
-
-registerotffeature {
- name = "colorscheme",
- description = "goodie color scheme",
- initializers = {
- base = setcolorscheme,
- node = setcolorscheme,
- }
-}
-
-registerotffeature {
- name = "postprocessor",
- description = "goodie postprocessor",
- initializers = {
- base = setpostprocessor,
- node = setpostprocessor,
- }
-}
-
--- afm
-
-registerafmfeature {
- name = "goodies",
- description = "goodies on top of built in features",
- initializers = {
- position = 1,
- base = setgoodies,
- node = setgoodies,
- }
-}
-
--- tfm
-
-registertfmfeature {
- name = "goodies",
- description = "goodies on top of built in features",
- initializers = {
- position = 1,
- base = setgoodies,
- node = setgoodies,
- }
-}
-
--- experiment, we have to load the definitions immediately as they precede
--- the definition so they need to be initialized in the typescript
-
-local function initialize(goodies)
- local mathgoodies = goodies.mathematics
- if mathgoodies then
- local virtuals = mathgoodies.virtuals
- local mapfiles = mathgoodies.mapfiles
- local maplines = mathgoodies.maplines
- if virtuals then
- for name, specification in next, virtuals do
- -- beware, they are all constructed
- mathematics.makefont(name,specification,goodies)
- end
- end
- if mapfiles then
- for i=1,#mapfiles do
- fonts.mappings.loadfile(mapfiles[i]) -- todo: backend function
- end
- end
- if maplines then
- for i=1,#maplines do
- fonts.mappings.loadline(maplines[i]) -- todo: backend function
- end
- end
- end
-end
-
-fontgoodies.register("mathematics", initialize)
-
--- the following takes care of explicit file specifications
---
--- files = {
--- name = "antykwapoltawskiego",
--- list = {
--- ["AntPoltLtCond-Regular.otf"] = {
--- -- name = "antykwapoltawskiego",
--- style = "regular",
--- weight = "light",
--- width = "condensed",
--- },
--- },
--- }
-
--- math italics
-
--- it would be nice to have a \noitalics\font option
-
-local function initialize(tfmdata)
- local goodies = tfmdata.goodies
- if goodies then
- local shared = tfmdata.shared
- for i=1,#goodies do
- local mathgoodies = goodies[i].mathematics
- local mathitalics = mathgoodies and mathgoodies.italics
- if mathitalics then
- local properties = tfmdata.properties
- mathitalics = mathitalics[file.nameonly(properties.name)] or mathitalics
- if mathitalics then
- if trace_goodies then
- report_goodies("loading mathitalics for font %a",properties.name)
- end
- local corrections = mathitalics.corrections
- local defaultfactor = mathitalics.defaultfactor
- local disableengine = mathitalics.disableengine
- properties.hasitalics = true
- properties.mathitalic_defaultfactor = defaultfactor -- we inherit outer one anyway (name will change)
- if properties.mathitalics == nil then
- properties.mathitalics = disableengine
- end
- if corrections then
- -- As we want to set italic_correction (the context one) we need a
- -- postprocessor instead of messing with the (unscaled) descriptions.
- fontgoodies.registerpostprocessor(tfmdata, function(tfmdata) -- this is another tfmdata (a copy)
- -- better make a helper so that we have less code being defined
- local properties = tfmdata.properties
- local parameters = tfmdata.parameters
- local characters = tfmdata.characters
- properties.hasitalics = true
- properties.mathitalic_defaultfactor = defaultfactor
- properties.mathitalic_defaultvalue = defaultfactor * parameters.quad
- if properties.mathitalics == nil then
- properties.mathitalics = disableengine
- end
- if trace_goodies then
- report_goodies("assigning mathitalics for font %a",properties.name)
- end
- local mathitalics = properties.mathitalics
- local quad = parameters.quad
- local hfactor = parameters.hfactor
- for k, v in next, corrections do
- local c = characters[k]
- if v > -1 and v < 1 then
- v = v * quad
- else
- v = v * hfactor
- end
- c.italic_correction = v -- for context
- if mathitalics then
- c.italic = v -- for tex
- else
- c.italic = nil
- end
- end
- end)
- end
- return -- maybe not as these can accumulate
- end
- end
- end
- end
-end
-
-registerotffeature {
- name = "mathitalics",
- description = "additional math italic corrections",
- -- default = true,
- initializers = {
- base = initialize,
- node = initialize,
- }
-}
-
--- fontgoodies.register("mathitalics", initialize)
-
--- files
-
-local function initialize(goodies)
- local files = goodies.files
- if files then
- fonts.names.register(files)
- end
-end
-
-fontgoodies.register("files", initialize)
-
--- some day we will have a define command and then we can also do some
--- proper tracing
---
--- fonts.typefaces["antykwapoltawskiego-condensed"] = {
--- shortcut = "rm",
--- shape = "serif",
--- fontname = "antykwapoltawskiego",
--- normalweight = "light",
--- boldweight = "medium",
--- width = "condensed",
--- size = "default",
--- features = "default",
--- }
-
-local function initialize(goodies)
- local typefaces = goodies.typefaces
- if typefaces then
- local ft = fonts.typefaces
- for k, v in next, typefaces do
- ft[k] = v
- end
- end
-end
-
-fontgoodies.register("typefaces", initialize)
-
-local compositions = { }
-
-function fontgoodies.getcompositions(tfmdata)
- return compositions[file.nameonly(tfmdata.properties.filename or "")]
-end
-
-local function initialize(goodies)
- local gc = goodies.compositions
- if gc then
- for k, v in next, gc do
- compositions[k] = v
- end
- end
-end
-
-fontgoodies.register("compositions", initialize)
-
--- extra treatments (on top of defaults): \loadfontgoodies[mytreatments]
-
-local treatmentdata = fonts.treatments.data
-
-local function initialize(goodies)
- local treatments = goodies.treatments
- if treatments then
- for name, data in next, treatments do
- treatmentdata[name] = data -- always wins
- end
- end
-end
-
-fontgoodies.register("treatments", initialize)
-
-local filenames = fontgoodies.filenames or allocate()
-fontgoodies.filenames = filenames
-
-local filedata = filenames.data or allocate()
-filenames.data = filedata
-
-local function initialize(goodies) -- design sizes are registered global
- local fn = goodies.filenames
- if fn then
- for usedname, alternativenames in next, fn do
- filedata[usedname] = alternativenames
- end
- end
-end
-
-fontgoodies.register("filenames", initialize)
-
-function fontgoodies.filenames.resolve(name)
- local fd = filedata[name]
- if fd and findfile(name) == "" then
- for i=1,#fd do
- local fn = fd[i]
- if findfile(fn) ~= "" then
- return fn
- end
- end
- else
- -- no lookup, just use the regular mechanism
- end
- return name
-end
-
-local designsizes = fontgoodies.designsizes or allocate()
-fontgoodies.designsizes = designsizes
-
-local designdata = designsizes.data or allocate()
-designsizes.data = designdata
-
-local function initialize(goodies) -- design sizes are registered global
- local gd = goodies.designsizes
- if gd then
- for name, data in next, gd do
- local ranges = { }
- for size, file in next, data do
- if size ~= "default" then
- ranges[#ranges+1] = { texsp(size), file } -- also lower(file)
- end
- end
- table.sort(ranges,function(a,b) return a[1] < b[1] end)
- designdata[lower(name)] = { -- overloads, doesn't merge!
- default = data.default,
- ranges = ranges,
- }
- end
- end
-end
-
-fontgoodies.register("designsizes", initialize)
-
-function fontgoodies.designsizes.register(name,size,specification)
- local d = designdata[name]
- if not d then
- d = {
- ranges = { },
- default = nil, -- so we have no default set
- }
- designdata[name] = d
- end
- if size == "default" then
- d.default = specification
- else
- if type(size) == "string" then
- size = texsp(size)
- end
- local ranges = d.ranges
- ranges[#ranges+1] = { size, specification }
- end
-end
-
-function fontgoodies.designsizes.filename(name,spec,size) -- returns nil of no match
- if spec and spec ~= "" then
- local data = designdata[lower(name)]
- if data then
- if spec == "default" then
- return data.default
- elseif spec == "auto" then
- local ranges = data.ranges
- if ranges then
- for i=1,#ranges do
- local r = ranges[i]
- if r[1] >= size then -- todo: rounding so maybe size - 100
- return r[2]
- end
- end
- end
- return data.default or (ranges and ranges[#ranges][2])
- end
- end
- end
-end
-
--- The following file (husayni.lfg) is the experimental setup that we used
--- for Idris font. For the moment we don't store this in the cache and quite
--- probably these files sit in one of the paths:
---
--- tex/context/fonts/goodies
--- tex/fonts/goodies/context
--- tex/fonts/data/foundry/collection
---
--- see lfg files in distribution
-
--- interface
-
-commands.loadfontgoodies = fontgoodies.load
-commands.enablefontcolorschemes = colorschemes.enable
-
--- weird place ... depends on math
-
-local function finalize(tfmdata,feature,value)
- mathematics.overloaddimensions(tfmdata,tfmdata,value)
-end
-
-registerotffeature {
- name = "mathdimensions",
- description = "manipulate math dimensions",
- -- default = true,
- manipulators = {
- base = finalize,
- node = finalize,
- }
-}
+if not modules then modules = { } end modules ['font-gds'] = { + version = 1.000, + comment = "companion to font-gds.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- depends on ctx + +local type, next, tonumber = type, next, tonumber +local gmatch, format, lower, find, splitup = string.gmatch, string.format, string.lower, string.find, string.splitup +local texsp = tex.sp + +local fonts, nodes, attributes, node = fonts, nodes, attributes, node + +local trace_goodies = false trackers.register("fonts.goodies", function(v) trace_goodies = v end) +local report_goodies = logs.reporter("fonts","goodies") + +local allocate = utilities.storage.allocate + +local otf = fonts.handlers.otf +local afm = fonts.handlers.afm +local tfm = fonts.handlers.tfm + +local registerotffeature = otf.features.register +local registerafmfeature = afm.features.register +local registertfmfeature = tfm.features.register + +local fontgoodies = fonts.goodies or { } +fonts.goodies = fontgoodies + +local typefaces = fonts.typefaces or allocate() +fonts.typefaces = typefaces + +local data = fontgoodies.data or allocate() +fontgoodies.data = data + +local list = fontgoodies.list or { } +fontgoodies.list = list -- no allocate as we want to see what is there + +local addotffeature = otf.enhancers.addfeature + +local findfile = resolvers.findfile + +function fontgoodies.report(what,trace,goodies) + if trace_goodies or trace then + local whatever = goodies[what] + if whatever then + report_goodies("goodie %a found in %a",what,goodies.name) + end + end +end + +local function loadgoodies(filename) -- maybe a merge is better + local goodies = data[filename] -- we assume no suffix is given + if goodies ~= nil then + -- found or tagged unfound + elseif type(filename) == "string" then + local fullname = findfile(file.addsuffix(filename,"lfg")) or "" -- prefered suffix + if fullname == "" then + fullname = findfile(file.addsuffix(filename,"lua")) or "" -- fallback suffix + end + if fullname == "" then + report_goodies("goodie file '%s.lfg' is not found",filename) + data[filename] = false -- signal for not found + else + goodies = dofile(fullname) or false + if not goodies then + report_goodies("goodie file %a is invalid",fullname) + return nil + elseif trace_goodies then + report_goodies("goodie file %a is loaded",fullname) + end + goodies.name = goodies.name or "no name" + for name, fnc in next, list do + fnc(goodies) + end + goodies.initialized = true + data[filename] = goodies + end + end + return goodies +end + +function fontgoodies.register(name,fnc) -- will be a proper sequencer + list[name] = fnc +end + +fontgoodies.load = loadgoodies + +-- register goodies file + +local function setgoodies(tfmdata,value) + local goodies = tfmdata.goodies + if not goodies then -- actually an error + goodies = { } + tfmdata.goodies = goodies + end + for filename in gmatch(value,"[^, ]+") do + -- we need to check for duplicates + local ok = loadgoodies(filename) + if ok then + if trace_goodies then + report_goodies("assigning goodie %a",filename) + end + goodies[#goodies+1] = ok + end + end +end + +-- this will be split into good-* files and this file might become good-ini.lua + +-- featuresets + +local function flattenedfeatures(t,tt) + -- first set value dominates + local tt = tt or { } + for i=1,#t do + local ti = t[i] + if type(ti) == "table" then + flattenedfeatures(ti,tt) + elseif tt[ti] == nil then + tt[ti] = true + end + end + for k, v in next, t do + if type(k) ~= "number" then -- not tonumber(k) + if type(v) == "table" then + flattenedfeatures(v,tt) + elseif tt[k] == nil then + tt[k] = v + end + end + end + return tt +end + +-- fonts.features.flattened = flattenedfeatures + +local function prepare_features(goodies,name,set) + if set then + local ff = flattenedfeatures(set) + local fullname = goodies.name .. "::" .. name + local n, s = fonts.specifiers.presetcontext(fullname,"",ff) + goodies.featuresets[name] = s -- set + if trace_goodies then + report_goodies("feature set %a gets number %a and name %a",name,n,fullname) + end + return n + end +end + +fontgoodies.prepare_features = prepare_features + +local function initialize(goodies,tfmdata) + local featuresets = goodies.featuresets + local goodiesname = goodies.name + if featuresets then + if trace_goodies then + report_goodies("checking featuresets in %a",goodies.name) + end + for name, set in next, featuresets do + prepare_features(goodies,name,set) + end + end +end + +fontgoodies.register("featureset",initialize) + +local function setfeatureset(tfmdata,set,features) + local goodies = tfmdata.goodies -- shared ? + if goodies then + local properties = tfmdata.properties + local what + for i=1,#goodies do + -- last one wins + local g = goodies[i] + what = g.featuresets and g.featuresets[set] or what + end + if what then + for feature, value in next, what do + if features[feature] == nil then + features[feature] = value + end + end + properties.mode = what.mode or properties.mode + end + end +end + +-- postprocessors (we could hash processor and share code) + +function fontgoodies.registerpostprocessor(tfmdata,f,prepend) + local postprocessors = tfmdata.postprocessors + if not postprocessors then + tfmdata.postprocessors = { f } + elseif prepend then + table.insert(postprocessors,f,1) + else + table.insert(postprocessors,f) + end +end + +local function setpostprocessor(tfmdata,processor) + local goodies = tfmdata.goodies + if goodies and type(processor) == "string" then + local found = { } + local asked = utilities.parsers.settings_to_array(processor) + for i=1,#goodies do + local g = goodies[i] + local p = g.postprocessors + if p then + for i=1,#asked do + local a = asked[i] + local f = p[a] + if type(f) == "function" then + found[a] = f + end + end + end + end + local postprocessors = tfmdata.postprocessors or { } + for i=1,#asked do + local a = asked[i] + local f = found[a] + if f then + postprocessors[#postprocessors+1] = f + end + end + if #postprocessors > 0 then + tfmdata.postprocessors = postprocessors + end + end +end + +-- colorschemes + +local colorschemes = fontgoodies.colorschemes or allocate { } +fontgoodies.colorschemes = colorschemes +colorschemes.data = colorschemes.data or { } + +local function setcolorscheme(tfmdata,scheme) + if type(scheme) == "string" then + local goodies = tfmdata.goodies + -- todo : check for already defined in shared + if goodies then + local what + for i=1,#goodies do + -- last one counts + local g = goodies[i] + what = g.colorschemes and g.colorschemes[scheme] or what + end + if type(what) == "table" then + -- this is font bound but we can share them if needed + -- just as we could hash the conversions (per font) + local hash = tfmdata.resources.unicodes + local reverse = { } + local characters = tfmdata.characters + for i=1,#what do + local w = what[i] + for j=1,#w do + local name = w[j] + if name == "*" then + -- inefficient but only used for tracing anyway + for _, unicode in next, hash do + reverse[unicode] = i + end + elseif type(name) == "number" then + reverse[name] = i + elseif find(name,":") then + local start, stop = splitup(name,":") + start = tonumber(start) + stop = tonumber(stop) + if start and stop then + -- limited usage: we only deal with non reassigned + -- maybe some day I'll also support the ones with a + -- tounicode in this range + for unicode=start,stop do + if characters[unicode] then + reverse[unicode] = i + end + end + end + else + local unicode = hash[name] + if unicode then + reverse[unicode] = i + end + end + end + end + tfmdata.properties.colorscheme = reverse + return + end + end + end + tfmdata.properties.colorscheme = false +end + +local fontdata = fonts.hashes.identifiers +local setnodecolor = nodes.tracers.colors.set +local traverse_id = node.traverse_id +local a_colorscheme = attributes.private('colorscheme') +local glyph = node.id("glyph") + +function colorschemes.coloring(head) + local lastfont, lastscheme + local done = false + for n in traverse_id(glyph,head) do + local a = n[a_colorscheme] + if a then + local f = n.font + if f ~= lastfont then + lastscheme, lastfont = fontdata[f].properties.colorscheme, f + end + if lastscheme then + local sc = lastscheme[n.char] + if sc then + done = true + setnodecolor(n,"colorscheme:"..a..":"..sc) -- slow + end + end + end + end + return head, done +end + +function colorschemes.enable() + nodes.tasks.appendaction("processors","fonts","fonts.goodies.colorschemes.coloring") + function colorschemes.enable() end +end + +local function setextrafeatures(tfmdata) + local goodies = tfmdata.goodies + if goodies then + for i=1,#goodies do + local g = goodies[i] + local f = g.features + if f then + for feature, specification in next, f do + addotffeature(tfmdata.shared.rawdata,feature,specification) + registerotffeature { + name = feature, + description = format("extra: %s",feature) + } + end + end + end + end +end + +-- installation (collected to keep the overview) -- also for type 1 + +registerotffeature { + name = "goodies", + description = "goodies on top of built in features", + initializers = { + position = 1, + base = setgoodies, + node = setgoodies, + } +} + +registerotffeature { + name = "extrafeatures", + description = "extra features", + default = true, + initializers = { + position = 2, + base = setextrafeatures, + node = setextrafeatures, + } +} + +registerotffeature { + name = "featureset", + description = "goodie feature set", + initializers = { + position = 3, + base = setfeatureset, + node = setfeatureset, + } +} + +registerotffeature { + name = "colorscheme", + description = "goodie color scheme", + initializers = { + base = setcolorscheme, + node = setcolorscheme, + } +} + +registerotffeature { + name = "postprocessor", + description = "goodie postprocessor", + initializers = { + base = setpostprocessor, + node = setpostprocessor, + } +} + +-- afm + +registerafmfeature { + name = "goodies", + description = "goodies on top of built in features", + initializers = { + position = 1, + base = setgoodies, + node = setgoodies, + } +} + +-- tfm + +registertfmfeature { + name = "goodies", + description = "goodies on top of built in features", + initializers = { + position = 1, + base = setgoodies, + node = setgoodies, + } +} + +-- experiment, we have to load the definitions immediately as they precede +-- the definition so they need to be initialized in the typescript + +local function initialize(goodies) + local mathgoodies = goodies.mathematics + if mathgoodies then + local virtuals = mathgoodies.virtuals + local mapfiles = mathgoodies.mapfiles + local maplines = mathgoodies.maplines + if virtuals then + for name, specification in next, virtuals do + -- beware, they are all constructed + mathematics.makefont(name,specification,goodies) + end + end + if mapfiles then + for i=1,#mapfiles do + fonts.mappings.loadfile(mapfiles[i]) -- todo: backend function + end + end + if maplines then + for i=1,#maplines do + fonts.mappings.loadline(maplines[i]) -- todo: backend function + end + end + end +end + +fontgoodies.register("mathematics", initialize) + +-- the following takes care of explicit file specifications +-- +-- files = { +-- name = "antykwapoltawskiego", +-- list = { +-- ["AntPoltLtCond-Regular.otf"] = { +-- -- name = "antykwapoltawskiego", +-- style = "regular", +-- weight = "light", +-- width = "condensed", +-- }, +-- }, +-- } + +-- math italics + +-- it would be nice to have a \noitalics\font option + +local function initialize(tfmdata) + local goodies = tfmdata.goodies + if goodies then + local shared = tfmdata.shared + for i=1,#goodies do + local mathgoodies = goodies[i].mathematics + local mathitalics = mathgoodies and mathgoodies.italics + if mathitalics then + local properties = tfmdata.properties + mathitalics = mathitalics[file.nameonly(properties.name)] or mathitalics + if mathitalics then + if trace_goodies then + report_goodies("loading mathitalics for font %a",properties.name) + end + local corrections = mathitalics.corrections + local defaultfactor = mathitalics.defaultfactor + local disableengine = mathitalics.disableengine + properties.hasitalics = true + properties.mathitalic_defaultfactor = defaultfactor -- we inherit outer one anyway (name will change) + if properties.mathitalics == nil then + properties.mathitalics = disableengine + end + if corrections then + -- As we want to set italic_correction (the context one) we need a + -- postprocessor instead of messing with the (unscaled) descriptions. + fontgoodies.registerpostprocessor(tfmdata, function(tfmdata) -- this is another tfmdata (a copy) + -- better make a helper so that we have less code being defined + local properties = tfmdata.properties + local parameters = tfmdata.parameters + local characters = tfmdata.characters + properties.hasitalics = true + properties.mathitalic_defaultfactor = defaultfactor + properties.mathitalic_defaultvalue = defaultfactor * parameters.quad + if properties.mathitalics == nil then + properties.mathitalics = disableengine + end + if trace_goodies then + report_goodies("assigning mathitalics for font %a",properties.name) + end + local mathitalics = properties.mathitalics + local quad = parameters.quad + local hfactor = parameters.hfactor + for k, v in next, corrections do + local c = characters[k] + if v > -1 and v < 1 then + v = v * quad + else + v = v * hfactor + end + c.italic_correction = v -- for context + if mathitalics then + c.italic = v -- for tex + else + c.italic = nil + end + end + end) + end + return -- maybe not as these can accumulate + end + end + end + end +end + +registerotffeature { + name = "mathitalics", + description = "additional math italic corrections", + -- default = true, + initializers = { + base = initialize, + node = initialize, + } +} + +-- fontgoodies.register("mathitalics", initialize) + +-- files + +local function initialize(goodies) + local files = goodies.files + if files then + fonts.names.register(files) + end +end + +fontgoodies.register("files", initialize) + +-- some day we will have a define command and then we can also do some +-- proper tracing +-- +-- fonts.typefaces["antykwapoltawskiego-condensed"] = { +-- shortcut = "rm", +-- shape = "serif", +-- fontname = "antykwapoltawskiego", +-- normalweight = "light", +-- boldweight = "medium", +-- width = "condensed", +-- size = "default", +-- features = "default", +-- } + +local function initialize(goodies) + local typefaces = goodies.typefaces + if typefaces then + local ft = fonts.typefaces + for k, v in next, typefaces do + ft[k] = v + end + end +end + +fontgoodies.register("typefaces", initialize) + +local compositions = { } + +function fontgoodies.getcompositions(tfmdata) + return compositions[file.nameonly(tfmdata.properties.filename or "")] +end + +local function initialize(goodies) + local gc = goodies.compositions + if gc then + for k, v in next, gc do + compositions[k] = v + end + end +end + +fontgoodies.register("compositions", initialize) + +-- extra treatments (on top of defaults): \loadfontgoodies[mytreatments] + +local treatmentdata = fonts.treatments.data + +local function initialize(goodies) + local treatments = goodies.treatments + if treatments then + for name, data in next, treatments do + treatmentdata[name] = data -- always wins + end + end +end + +fontgoodies.register("treatments", initialize) + +local filenames = fontgoodies.filenames or allocate() +fontgoodies.filenames = filenames + +local filedata = filenames.data or allocate() +filenames.data = filedata + +local function initialize(goodies) -- design sizes are registered global + local fn = goodies.filenames + if fn then + for usedname, alternativenames in next, fn do + filedata[usedname] = alternativenames + end + end +end + +fontgoodies.register("filenames", initialize) + +function fontgoodies.filenames.resolve(name) + local fd = filedata[name] + if fd and findfile(name) == "" then + for i=1,#fd do + local fn = fd[i] + if findfile(fn) ~= "" then + return fn + end + end + else + -- no lookup, just use the regular mechanism + end + return name +end + +local designsizes = fontgoodies.designsizes or allocate() +fontgoodies.designsizes = designsizes + +local designdata = designsizes.data or allocate() +designsizes.data = designdata + +local function initialize(goodies) -- design sizes are registered global + local gd = goodies.designsizes + if gd then + for name, data in next, gd do + local ranges = { } + for size, file in next, data do + if size ~= "default" then + ranges[#ranges+1] = { texsp(size), file } -- also lower(file) + end + end + table.sort(ranges,function(a,b) return a[1] < b[1] end) + designdata[lower(name)] = { -- overloads, doesn't merge! + default = data.default, + ranges = ranges, + } + end + end +end + +fontgoodies.register("designsizes", initialize) + +function fontgoodies.designsizes.register(name,size,specification) + local d = designdata[name] + if not d then + d = { + ranges = { }, + default = nil, -- so we have no default set + } + designdata[name] = d + end + if size == "default" then + d.default = specification + else + if type(size) == "string" then + size = texsp(size) + end + local ranges = d.ranges + ranges[#ranges+1] = { size, specification } + end +end + +function fontgoodies.designsizes.filename(name,spec,size) -- returns nil of no match + if spec and spec ~= "" then + local data = designdata[lower(name)] + if data then + if spec == "default" then + return data.default + elseif spec == "auto" then + local ranges = data.ranges + if ranges then + for i=1,#ranges do + local r = ranges[i] + if r[1] >= size then -- todo: rounding so maybe size - 100 + return r[2] + end + end + end + return data.default or (ranges and ranges[#ranges][2]) + end + end + end +end + +-- The following file (husayni.lfg) is the experimental setup that we used +-- for Idris font. For the moment we don't store this in the cache and quite +-- probably these files sit in one of the paths: +-- +-- tex/context/fonts/goodies +-- tex/fonts/goodies/context +-- tex/fonts/data/foundry/collection +-- +-- see lfg files in distribution + +-- interface + +commands.loadfontgoodies = fontgoodies.load +commands.enablefontcolorschemes = colorschemes.enable + +-- weird place ... depends on math + +local function finalize(tfmdata,feature,value) + mathematics.overloaddimensions(tfmdata,tfmdata,value) +end + +registerotffeature { + name = "mathdimensions", + description = "manipulate math dimensions", + -- default = true, + manipulators = { + base = finalize, + node = finalize, + } +} |