Processed Words
++ +
From 85b7bc695629926641c7cb752fd478adfdf374f3 Mon Sep 17 00:00:00 2001
From: Marius We save positional information in the main utility table. Not only
+can we store much more information in This module implements a couple of cleanup methods. We need these
+in order to meet the This is a prelude to integrated bibliography support. This file just loads
+bibtex files and converts them to xml so that the we access the content
+in a convenient way. Actually handling the data takes place elsewhere. The code defined here may move to the big character table. The next three tables can for instance be be used to enhance
+kerning tables that lack kerning pairs for these special characters.
+Of course they may come in handy elsewhere too The following function is used in the indexing code, where
+we need some sort of default fallback mapping. Only characters with a code smaller than 128 make sense,
+anything larger is encoding dependent. An interesting complication
+is that a character can be in an encoding twice but is hashed
+once. This module implements some methods and creates additional datastructured
+from the big character table that we use for all kind of purposes:
+ We assume that at this point At this point we assume that the big data table is loaded. From this
+table we derive a few more. The Instead of using a Setting the lccodes is also done in a loop over the data table. Next comes a whole series of helper methods. These are (will be) part
+of the official This converts a string (if given) into a number. Checking for valid characters. Get a table entry happens by number. Keep in mind that the unicodeslot
+can be different (not likely). A couple of convenience methods. Beware, these are not that fast due
+to the checking. Requesting lower and uppercase codes: Categories play an important role, so here are some checkers. When a sequence of This module implements methods for collapsing and expanding We implement these manipulations as filters. One can run multiple filters
+over a string. It only makes sense to collapse at runtime, since we don't expect
+source code to depend on collapsing. In order to deal with 8-bit output, we need to find a way to
+go from This leaves us problems with characters that are specific to
+ We can remap some chars that tex input files are sensitive for to
+a private area (while writing to a utility file) and revert then
+to their original slot when we read in such a file. Instead of
+reverting, we can (when we resolve characters to glyphs) map them
+to their right glyph there. For this purpose we can use the private planes 0x0F0000 and
+0x100000. We get a more efficient variant of this when we integrate
+replacements in collapser. This more or less renders the previous
+private code redundant. The following code is equivalent but the
+first snippet uses the relocated dollars. The next variant has lazy token collecting, on a 140 page mk.tex this saves
+about .25 seconds, which is understandable because we have no graphmes and
+not collecting tokens is not only faster but also saves garbage collecting.
+ Next we implement some commands that are used in the user interface. A few helpers (used to be The next code is an adaptation of code from Wolfgang Schuster
+as posted on the mailing list. This version supports nested
+braces and unbraced integers as scripts. We could consider
+spaces as terminals for them but first let collect a bunch
+of input then. This module implements a bunch of conversions. Some are more
+efficient than their Some code may move to a module in the language namespace. We save multi-pass information in the main utility table. This is a
+bit of a mess because we support old and new methods. A utility file has always been part of Variables are saved using in the previously defined table and passed
+onto .. .. Once we found ourselves defining similar cache constructs
+several times, containers were introduced. Containers are used
+to collect tables in memory and reuse them when possible based
+on (unique) hashes (to be provided by the calling function). Caching to disk is disabled by default. Version numbers are
+stored in the saved table which makes it possible to change the
+table structures without bothering about the disk cache. Examples of usage can be found in the font related code. This file is used when we want the input handlers to behave like
+ How about just forgetting about them? If you wondered abou tsome of the previous mappings, how about
+the next bunch: This module deals with caching data. It sets up the paths and
+implements loaders and savers for tables. Best is to set the
+following variable. When not set, the usual paths will be
+checked. Personally I prefer the (users) temporary path. Currently we do no locking when we write files. This is no real
+problem because most caching involves fonts and the chance of them
+being written at the same time is small. We also need to extend
+luatools with a recache feature. Some code may look a bit obscure but this has to do with the
+fact that we also use this code for testing and much code evolved
+in the transition from The following code still has traces of intermediate font support
+where we handles font encodings. Eventually font encoding goes
+away. We start with the basic reader which we give a name similar to the
+built in We cache files. Caching is taken care of in the loader. We cheat a bit
+by adding ligatures and kern information to the afm derived data. That
+way we can set them faster when defining a font. These helpers extend the basic table with extra ligatures, texligatures
+and extra kerns. This saves quite some lookups later. We keep the extra kerns in separate kerning tables so that we can use
+them selectively. The copying routine looks messy (and is indeed a bit messy). Originally we had features kind of hard coded for As soon as we could intercept the Here comes the implementation of a few features. We only implement
+those that make sense for this format. 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:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Processed Words
+
+
+
+
+
+
+
+
+
+
+ Scratch Variables
+
+
+
+
+
+ index
+ dimen
+ count
+ toks
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Internal Variables
+
+
+
+
+
+
+
+
+
+ ConTeXt Variables
+
+
+
+
+
+
+
+
+
+
+
+ Error
+ File
+ Line
+
+
+
+
+
+
+
+
+
+name:xetex like specs
+name@virtual font spec
+name*context specification
+
+--ldx]]--
+
+function specify.predefined(specification)
+ local detail = specification.detail
+ if detail ~= "" then
+ -- detail = gsub(detail,"["..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 == "" and find(parent,"=") then
+ features = parent
+ parent = ""
+ end
+ if features == "" then
+ features = { }
+ elseif type(features) == "string" then
+ features = normalize_meanings(settings_to_hash(features))
+ else
+ features = normalize_meanings(features)
+ end
+ -- 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 features[k] == nil then
+ features[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 features[v] == nil then -- not false !
+ local vv = default_features[v]
+ if vv then features[v] = vv end
+ end
+ end
+ -- sparse 'm so that we get a better hash and less test (experimental
+ -- optimization)
+ local t = { } -- can we avoid t ?
+ for k,v in next, features do
+ if v then t[k] = v end
+ end
+ -- needed for dynamic features
+ local number = (setups[name] and setups[name].number) or 0
+ if number == 0 then
+ number = #numbers + 1
+ numbers[number] = name
+ end
+ t.number = number
+ setups[name] = t
+ return number, t
+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 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") * Cc(1) * spaces * dimension -- value
+local scale_sa = P("sa") * Cc(2) * spaces * dimension -- value
+local scale_mo = P("mo") * Cc(3) * spaces * dimension -- value
+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
+
+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 = lpegmatch(splitpattern,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,"\\fcxdef\\somefontname{",name,"}")
+ end
+ -- we can also use a count for the size
+ if size and size ~= "" then
+ local mode, size = lpegmatch(sizepattern,size)
+ if size and mode then
+ count.scaledfontmode = mode
+ texsprint(ctxcatcodes,"\\def\\somefontsize{",size,"}")
+ else
+ count.scaledfontmode = 0
+ texsprint(ctxcatcodes,"\\let\\somefontsize\\empty")
+ end
+ elseif true then
+ -- so we don't need to check in tex
+ count.scaledfontmode = 2
+ texsprint(ctxcatcodes,"\\let\\somefontsize\\empty")
+ else
+ count.scaledfontmode = 0
+ texsprint(ctxcatcodes,"\\let\\somefontsize\\empty")
+ end
+ specification = define.makespecification(str,lookup,name,sub,method,detail,size)
+end
+
+local n = 0
+
+-- we can also move rscale to here (more consistent)
+
+function define.command_2(global,cs,str,size,classfeatures,fontfeatures,classfallbacks,fontfallbacks,mathsize,textsize,relativeid)
+ if trace_defining then
+ logs.report("define font","memory usage before: %s",statistics.memused())
+ end
+ -- 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
+ if relativeid and relativeid ~= "" then -- experimental hook
+ local id = tonumber(relativeid) or 0
+ specification.relativeid = id > 0 and id
+ end
+ specification.name = name
+ specification.size = size
+ specification.sub = (sub and sub ~= "" and sub) or specification.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 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)
+ texsetcount("global","lastfontid",-1)
+ 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))
+ texsetcount("global","lastfontid",tfmdata)
+ 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
+ texsetcount("global","lastfontid",id)
+ end
+ if trace_defining then
+ logs.report("define font","memory usage after: %s",statistics.memused())
+ end
+ statistics.stoptiming(fonts)
+end
+
+local enable_auto_r_scale = false
+
+experiments.register("fonts.autorscale", function(v)
+ enable_auto_r_scale = v
+end)
+
+local calculate_scale = fonts.tfm.calculate_scale
+
+function fonts.tfm.calculate_scale(tfmtable, scaledpoints, relativeid)
+ local scaledpoints, delta, units = calculate_scale(tfmtable, scaledpoints, relativeid)
+ if enable_auto_r_scale and relativeid then -- for the moment this is rather context specific
+ local relativedata = fontdata[relativeid]
+ local id_x_height = relativedata and relativedata.parameters and relativedata.parameters.x_height
+ local tf_x_height = id_x_height and tfmtable.parameters and tfmtable.parameters.x_height * delta
+ if tf_x_height then
+ scaledpoints = (id_x_height/tf_x_height) * scaledpoints
+ delta = scaledpoints/units
+ end
+ end
+ return scaledpoints, delta, units
+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
+
+-- we need a place for this .. outside the generic scope
+
+local dimenfactors = number.dimenfactors
+
+function fonts.dimenfactor(unit,tfmdata)
+ if unit == "ex" then
+ return (tfmdata and tfmdata.parameters.x_height) or 655360
+ elseif unit == "em" then
+ return (tfmdata and tfmdata.parameters.em_height) or 655360
+ else
+ return dimenfactors[unit] or unit
+ end
+end
+
+function fonts.cleanname(name)
+ texsprint(ctxcatcodes,fonts.names.cleanname(name))
+end
+
+local p, f = 1, "%0.1fpt" -- normally this value is changed only once
+
+local stripper = lpeg.patterns.strip_zeros
+
+function fonts.nbfs(amount,precision)
+ if precision ~= p then
+ p = precision
+ f = "%0." .. p .. "fpt"
+ end
+ texsprint(ctxcatcodes,lpegmatch(stripper,format(f,amount/65536)))
+end
+
+-- for the moment here, this will become a chain of extras that is
+-- hooked into the ctx registration (or scaler or ...)
+
+function fonts.set_digit_width(font)
+ local tfmtable = fontdata[font]
+ local parameters = tfmtable.parameters
+ local width = parameters.digitwidth
+ if not width then
+ width = round(parameters.quad/2) -- maybe tex.scale
+ local characters = tfmtable.characters
+ for i=48,57 do
+ local wd = round(characters[i].width)
+ if wd > width then
+ width = wd
+ end
+ end
+ parameters.digitwidth = width
+ end
+ return width
+end
+
+fonts.get_digit_width = fonts.set_digit_width
+
+-- soon to be obsolete:
+
+local loaded = { -- prevent loading (happens in cont-sys files)
+ ["original-base.map" ] = true,
+ ["original-ams-base.map" ] = true,
+ ["original-ams-euler.map"] = true,
+ ["original-public-lm.map"] = true,
+}
+
+function fonts.map.loadfile(name)
+ name = file.addsuffix(name,"map")
+ if not loaded[name] then
+ pdf.mapfile(name)
+ loaded[name] = true
+ end
+end
+
+local loaded = { -- prevent double loading
+}
+
+function fonts.map.loadline(how,line)
+ if line then
+ how = how .. " " .. line
+ elseif how == "" then
+ how = "= " .. line
+ end
+ if not loaded[how] then
+ pdf.mapline(how)
+ loaded[how] = true
+ end
+end
+
+function fonts.map.reset()
+ pdf.mapfile("")
+end
+
+fonts.map.reset() -- resets the default file
+
+-- we need an 'do after the banner hook'
+
+-- pdf.mapfile("mkiv-base.map") -- loads the default file
+
+local nounicode = byte("?")
+
+local function name_to_slot(name) -- maybe some day rawdata
+ local tfmdata = fonts.ids[font.current()]
+ local shared = tfmdata and tfmdata.shared
+ local fntdata = shared and shared.otfdata or shared.afmdata
+ if fntdata then
+ local unicode = fntdata.luatex.unicodes[name]
+ if not unicode then
+ return nounicode
+ elseif type(unicode) == "number" then
+ return unicode
+ else -- multiple unicodes
+ return unicode[1]
+ end
+ end
+ return nounicode
+end
+
+fonts.name_to_slot = name_to_slot
+
+function fonts.char(n) -- todo: afm en tfm
+ if type(n) == "string" then
+ n = name_to_slot(n)
+ end
+ if type(n) == "number" then
+ texsprint(ctxcatcodes,format("\\char%s ",n))
+ end
+end
+
+-- moved from ini:
+
+fonts.color = { } -- dummy in ini
+
+local attribute = attributes.private('color')
+local mapping = (attributes and attributes.list[attribute]) or { }
+
+local set_attribute = node.set_attribute
+local unset_attribute = node.unset_attribute
+
+function fonts.color.set(n,c)
+ local mc = mapping[c]
+ if not mc then
+ unset_attribute(n,attribute)
+ else
+ set_attribute(n,attribute,mc)
+ end
+end
+
+function fonts.color.reset(n)
+ unset_attribute(n,attribute)
+end
+
+-- this will become obsolete:
+
+fonts.otf.name_to_slot = name_to_slot
+fonts.afm.name_to_slot = name_to_slot
+
+fonts.otf.char = fonts.char
+fonts.afm.char = fonts.char
+
+-- this will change ...
+
+function fonts.show_char_data(n)
+ local tfmdata = fonts.ids[font.current()]
+ if tfmdata then
+ if type(n) == "string" then
+ n = utf.byte(n)
+ end
+ local chr = tfmdata.characters[n]
+ if chr then
+ write_nl(format("%s @ %s => U%04X => %s => ",tfmdata.fullname,tfmdata.size,n,utf.char(n)) .. serialize(chr,false))
+ end
+ end
+end
+
+function fonts.show_font_parameters()
+ local tfmdata = fonts.ids[font.current()]
+ if tfmdata then
+ local parameters, mathconstants = tfmdata.parameters, tfmdata.MathConstants
+ local hasparameters, hasmathconstants = parameters and next(parameters), mathconstants and next(mathconstants)
+ if hasparameters then
+ write_nl(format("%s @ %s => parameters => ",tfmdata.fullname,tfmdata.size) .. serialize(parameters,false))
+ end
+ if hasmathconstants then
+ write_nl(format("%s @ %s => math constants => ",tfmdata.fullname,tfmdata.size) .. serialize(mathconstants,false))
+ end
+ if not hasparameters and not hasmathconstants then
+ write_nl(format("%s @ %s => no parameters and/or mathconstants",tfmdata.fullname,tfmdata.size))
+ end
+ end
+end
diff --git a/tex/context/base/font-def.lua b/tex/context/base/font-def.lua
new file mode 100644
index 000000000..c3b10162c
--- /dev/null
+++ b/tex/context/base/font-def.lua
@@ -0,0 +1,651 @@
+if not modules then modules = { } end modules ['font-def'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+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 lpegmatch = lpeg.match
+
+local trace_defining = false trackers .register("fonts.defining", function(v) trace_defining = v end)
+local directive_embedall = false directives.register("fonts.embedall", function(v) directive_embedall = v end)
+
+trackers.register("fonts.loading", "fonts.defining", "otf.loading", "afm.loading", "tfm.loading")
+trackers.register("fonts.all", "fonts.*", "otf.*", "afm.*", "tfm.*")
+
+--[[ldx--
+Here we deal with defining fonts. We do so by intercepting the
+default loader that only handles
We hardly gain anything when we cache the final (pre scaled)
+
We can prefix a font specification by
The following function split the font specification into components +and prepares a table that will move along as we proceed.
+--ldx]]-- + +-- beware, we discard additional specs +-- +-- method:name method:name(sub) method:name(sub)*spec method:name*spec +-- name name(sub) name(sub)*spec name*spec +-- name@spec*oeps + +local splitter, specifiers = nil, "" + +local P, C, S, Cc = lpeg.P, lpeg.C, lpeg.S, lpeg.Cc + +local left = P("(") +local right = P(")") +local colon = P(":") +local space = P(" ") + +define.defaultlookup = "file" + +local prefixpattern = P(false) + +function define.add_specifier(symbol) + specifiers = specifiers .. symbol + local method = S(specifiers) + local lookup = C(prefixpattern) * colon + local sub = left * C(P(1-left-right-method)^1) * right + local specification = C(method) * C(P(1)^1) + local name = C((1-sub-specification)^1) + splitter = P((lookup + Cc("")) * name * (sub + Cc("")) * (specification + Cc(""))) +end + +function define.add_lookup(str,default) + prefixpattern = prefixpattern + P(str) +end + +define.add_lookup("file") +define.add_lookup("name") +define.add_lookup("spec") + +function define.get_specification(str) + return lpegmatch(splitter,str) +end + +function define.register_split(symbol,action) + define.add_specifier(symbol) + define.specify[symbol] = action +end + +function define.makespecification(specification, lookup, name, sub, method, detail, size) + size = size or 655360 + if trace_defining then + logs.report("define font","%s -> lookup: %s, name: %s, sub: %s, method: %s, detail: %s", + specification, (lookup ~= "" and lookup) or "[file]", (name ~= "" and name) or "-", + (sub ~= "" and sub) or "-", (method ~= "" and method) or "-", (detail ~= "" and detail) or "-") + end +--~ if specification.lookup then +--~ lookup = specification.lookup -- can come from xetex [] syntax +--~ specification.lookup = nil +--~ end + if not lookup or lookup == "" then + lookup = define.defaultlookup + end + local t = { + lookup = lookup, -- forced type + specification = specification, -- full specification + size = size, -- size in scaled points or -1000*n + name = name, -- font or filename + sub = sub, -- subfont (eg in ttc) + method = method, -- specification method + detail = detail, -- specification + resolved = "", -- resolved font name + forced = "", -- forced loader + features = { }, -- preprocessed features + } + return t +end + +function define.analyze(specification, size) + -- can be optimized with locals + local lookup, name, sub, method, detail = define.get_specification(specification or "") + return define.makespecification(specification, lookup, name, sub, method, detail, size) +end + +--[[ldx-- +A unique hash value is generated by:
+--ldx]]-- + +local sortedhashkeys = table.sortedhashkeys + +function tfm.hash_features(specification) + local features = specification.features + if features then + local t = { } + local normal = features.normal + if normal and next(normal) then + local f = sortedhashkeys(normal) + for i=1,#f do + local v = f[i] + if v ~= "number" and v ~= "features" then -- i need to figure this out, features + t[#t+1] = v .. '=' .. tostring(normal[v]) + end + end + end + local vtf = features.vtf + if vtf and next(vtf) then + local f = sortedhashkeys(vtf) + for i=1,#f do + local v = f[i] + t[#t+1] = v .. '=' .. tostring(vtf[v]) + end + end +--~ if specification.mathsize then +--~ t[#t+1] = "mathsize=" .. specification.mathsize +--~ end + if #t > 0 then + return concat(t,"+") + end + end + return "unknown" +end + +fonts.designsizes = { } + +--[[ldx-- +In principle we can share tfm tables when we are in node for a font, but then
+we need to define a font switch as an id/attr switch which is no fun, so in that
+case users can best use dynamic features ... so, we will not use that speedup. Okay,
+when we get rid of base mode we can optimize even further by sharing, but then we
+loose our testcases for
We can resolve the filename using the next function:
+--ldx]]-- + +define.resolvers = resolvers + +function define.resolvers.file(specification) + specification.forced = file.extname(specification.name) + specification.name = file.removesuffix(specification.name) +end + +function define.resolvers.name(specification) + local resolve = fonts.names.resolve + if resolve then + specification.resolved, specification.sub = fonts.names.resolve(specification.name,specification.sub) + if specification.resolved then + specification.forced = file.extname(specification.resolved) + specification.name = file.removesuffix(specification.resolved) + end + else + define.resolvers.file(specification) + end +end + +function define.resolvers.spec(specification) + local resolvespec = fonts.names.resolvespec + if resolvespec then + specification.resolved, specification.sub = fonts.names.resolvespec(specification.name,specification.sub) + if specification.resolved then + specification.forced = file.extname(specification.resolved) + specification.name = file.removesuffix(specification.resolved) + end + else + define.resolvers.name(specification) + end +end + +function define.resolve(specification) + if not specification.resolved or specification.resolved == "" then -- resolved itself not per se in mapping hash + local r = define.resolvers[specification.lookup] + if r then + r(specification) + end + end + if specification.forced == "" then + specification.forced = nil + else + specification.forced = specification.forced + end + specification.hash = lower(specification.name .. ' @ ' .. tfm.hash_features(specification)) + if specification.sub and specification.sub ~= "" then + specification.hash = specification.sub .. ' @ ' .. specification.hash + end + return specification +end + +--[[ldx-- +The main read function either uses a forced reader (as determined by +a lookup) or tries to resolve the name using the list of readers.
+ +We need to cache when possible. We do cache raw tfm data (from
Watch out, here we do load a font, but we don't prepare the +specification yet.
+--ldx]]-- + +function tfm.read(specification) + local hash = tfm.hash_instance(specification) + local tfmtable = tfm.fonts[hash] -- hashes by size ! + if not tfmtable then + local forced = specification.forced or "" + if forced ~= "" then + tfmtable = readers[lower(forced)](specification) + if not tfmtable then + logs.report("define font","forced type %s of %s not found",forced,specification.name) + end + else + for s=1,#sequence do -- reader sequence + local reader = sequence[s] + if readers[reader] then -- not really needed + if trace_defining then + logs.report("define font","trying (reader sequence driven) type %s for %s with file %s",reader,specification.name,specification.filename or "unknown") + end + tfmtable = readers[reader](specification) + if tfmtable then + break + else + specification.filename = nil + end + end + end + end + if tfmtable then + if directive_embedall then + tfmtable.embedding = "full" + elseif tfmtable.filename and fonts.dontembed[tfmtable.filename] then + tfmtable.embedding = "no" + else + tfmtable.embedding = "subset" + end + tfm.fonts[hash] = tfmtable + fonts.designsizes[specification.hash] = tfmtable.designsize -- we only know this for sure after loading once + --~ tfmtable.mode = specification.features.normal.mode or "base" + end + end + if not tfmtable then + logs.report("define font","font with name %s is not found",specification.name) + end + return tfmtable +end + +--[[ldx-- +For virtual fonts we need a slightly different approach:
+--ldx]]-- + +function tfm.read_and_define(name,size) -- no id + local specification = define.analyze(name,size) + local method = specification.method + if method and define.specify[method] then + specification = define.specify[method](specification) + end + specification = define.resolve(specification) + local hash = tfm.hash_instance(specification) + local id = define.registered(hash) + if not id then + local fontdata = tfm.read(specification) + if fontdata then + fontdata.hash = hash + id = font.define(fontdata) + define.register(fontdata,id) + tfm.cleanup_table(fontdata) + else + id = 0 -- signal + end + end + return fonts.ids[id], id +end + +--[[ldx-- +Next follow the readers. This code was written while
We need to check for default features. For this we provide +a helper function.
+--ldx]]-- + +function define.check(features,defaults) -- nb adapts features ! + local done = false + if features and next(features) then + for k,v in next, defaults do + if features[k] == nil then + features[k], done = v, true + end + end + else + features, done = table.fastcopy(defaults), true + end + return features, done -- done signals a change +end + +--[[ldx-- +So far the specifyers. Now comes the real definer. Here we cache +based on id's. Here we also intercept the virtual font handler. Since +it evolved stepwise I may rewrite this bit (combine code).
+ +In the previously defined reader (the one resulting in aWe overload both the
Because encodings are going to disappear, we don't bother defining +them in tables. But we may do so some day, for consistency.
+--ldx]]-- + +fonts.enc = fonts.enc or { } +fonts.enc.version = 1.03 +fonts.enc.cache = containers.define("fonts", "enc", fonts.enc.version, true) + +fonts.enc.known = { -- sort of obsolete + texnansi = true, + ec = true, + qx = true, + t5 = true, + t2a = true, + t2b = true, + t2c = true, + unicode = true +} + +function fonts.enc.is_known(encoding) + return containers.is_valid(fonts.enc.cache(),encoding) +end + +--[[ldx-- +An encoding file looks like this:
+ +Beware! The generic encoding files don't always apply to the ones that
+ship with fonts. This has to do with the fact that names follow (slightly)
+different standards. However, the fonts where this applies to (for instance
+Latin Modern or
There is no unicode encoding but for practical purposed we define +one.
+--ldx]]-- + +-- maybe make this a function: + +function fonts.enc.make_unicode_vector() + local vector, hash = { }, { } + for code, v in next, characters.data do + local name = v.adobename + if name then + vector[code], hash[name] = name, code + else + vector[code] = '.notdef' + end + end + for name, code in next, characters.synonyms do + vector[code], hash[name] = name, code + end + return containers.write(fonts.enc.cache(), 'unicode', { name='unicode', tag='unicode', vector=vector, hash=hash }) +end diff --git a/tex/context/base/font-enh.lua b/tex/context/base/font-enh.lua new file mode 100644 index 000000000..fc70c04c5 --- /dev/null +++ b/tex/context/base/font-enh.lua @@ -0,0 +1,201 @@ +if not modules then modules = { } end modules ['font-enh'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local next, match = next, string.match + +local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) + +-- tfmdata has also fast access to indices and unicodes +-- to be checked: otf -> tfm -> tfmscaled +-- +-- watch out: no negative depths and negative eights permitted in regular fonts + +--[[ldx-- +Here we only implement a few helper functions.
+--ldx]]-- + +local tfm = fonts.tfm + +--[[ldx-- +The next function encapsulates the standard
When we implement functions that deal with features, most of them +will depend of the font format. Here we define the few that are kind +of neutral.
+--ldx]]-- + +fonts.triggers = fonts.triggers or { } +fonts.initializers = fonts.initializers or { } +fonts.methods = fonts.methods or { } +fonts.initializers.common = fonts.initializers.common or { } + +local initializers = fonts.initializers +local methods = fonts.methods + +--[[ldx-- +This feature will remove inter-digit kerns.
+--ldx]]-- + +table.insert(fonts.triggers,"equaldigits") + +function initializers.common.equaldigits(tfmdata,value) + if value then + local chr = tfmdata.characters + for i = utfbyte('0'), utfbyte('9') do + local c = chr[i] + if c then + c.kerns = nil + end + end + end +end + +--[[ldx-- +This feature will give all glyphs an equal height and/or depth. Valid
+values are
This is very experimental code!
+--ldx]]-- + +fonts.fallbacks = fonts.fallbacks or { } + +local vf = fonts.vf +local tfm = fonts.tfm + +vf.aux.combine.commands["enable-tracing"] = function(g,v) + trace_combining = true +end + +vf.aux.combine.commands["disable-tracing"] = function(g,v) + trace_combining = false +end + +vf.aux.combine.commands["set-tracing"] = function(g,v) + if v[2] == nil then + trace_combining = true + else + trace_combining = v[2] + end +end + +function vf.aux.combine.initialize_trace() + if trace_combining then + return "special", "pdf: .8 0 0 rg .8 0 0 RG", "pdf: 0 .8 0 rg 0 .8 0 RG", "pdf: 0 0 .8 rg 0 0 .8 RG", "pdf: 0 g 0 G" + else + return "comment", "", "", "", "" + end +end + +vf.aux.combine.force_fallback = false + +vf.aux.combine.commands["fake-character"] = function(g,v) -- g, nr, fallback_id + local index, fallback = v[2], v[3] + if vf.aux.combine.force_fallback or not g.characters[index] then + if fonts.fallbacks[fallback] then + g.characters[index], g.descriptions[index] = fonts.fallbacks[fallback](g) + end + end +end + +fonts.fallbacks['textcent'] = function (g) + local c = ("c"):byte() + local t = table.fastcopy(g.characters[c]) + local a = - tan(rad(g.italicangle or 0)) + local special, red, green, blue, black = vf.aux.combine.initialize_trace() + local quad = g.parameters.quad + if a == 0 then + t.commands = { + {"push"}, {"slot", 1, c}, {"pop"}, + {"right", .5*t.width}, + {"down", .2*t.height}, + {special, green}, + {"rule", 1.4*t.height, .02*quad}, + {special, black}, + } + else + t.commands = { + {"push"}, + {"right", .5*t.width-.025*quad}, + {"down", .2*t.height}, + {"special",format("pdf: q 1 0 %s 1 0 0 cm",a)}, + {special, green}, + {"rule", 1.4*t.height, .025*quad}, + {special, black}, + {"special","pdf: Q"}, + {"pop"}, + {"slot", 1, c} -- last else problems with cm + } + end + -- somehow the width is messed up now + -- todo: set height + t.height = 1.2*t.height + t.depth = 0.2*t.height + g.virtualized = true + local d = g.descriptions + return t, d and d[c] +end + +fonts.fallbacks['texteuro'] = function (g) + local c = ("C"):byte() + local t = table.fastcopy(g.characters[c]) + local d = cos(rad(90+(g.italicangle))) + local special, red, green, blue, black = vf.aux.combine.initialize_trace() + local quad = g.parameters.quad + t.width = 1.05*t.width + t.commands = { + {"right", .05*t.width}, + {"push"}, {"slot", 1, c}, {"pop"}, + {"right", .5*t.width*d}, + {"down", -.5*t.height}, + {special, green}, + {"rule", .05*quad, .4*quad}, + {special, black}, + } + g.virtualized = true + return t, g.descriptions[c] +end + +-- maybe store llx etc instead of bbox in tfm blob / more efficient + +vf.aux.combine.force_composed = false + +local push, pop = { "push" }, { "pop" } + +local cache = { } -- we could make these weak + +function vf.aux.compose_characters(g) -- todo: scaling depends on call location + -- this assumes that slot 1 is self, there will be a proper self some day + local chars, descs = g.characters, g.descriptions + local X = ("X"):byte() + local xchar = chars[X] + local xdesc = descs[X] + if xchar and xdesc then + local scale = g.factor or 1 + local cap_lly = scale*xdesc.boundingbox[4] + local ita_cor = cos(rad(90+(g.italicangle or 0))) + local force = vf.aux.combine.force_composed + local fallbacks = characters.fallbacks + local special, red, green, blue, black + if trace_combining then + special, red, green, blue, black = vf.aux.combine.initialize_trace() + red, green, blue, black = { special, red }, { special, green }, { special, blue }, { special, black } + end + local done = false + for i,c in next, characters.data do + if force or not chars[i] then + local s = c.specials + if s and s[1] == 'char' then + local chr = s[2] + local charschr = chars[chr] + if charschr then + local cc = c.category + if cc == 'll' or cc == 'lu' or cc == 'lt' then + local acc = s[3] + local t = { } + for k, v in next, charschr do + if k ~= "commands" then + t[k] = v + end + end + local charsacc = chars[acc] +--~ local ca = charsacc.category +--~ if ca == "mn" then +--~ -- mark nonspacing +--~ elseif ca == "ms" then +--~ -- mark spacing combining +--~ elseif ca == "me" then +--~ -- mark enclosing +--~ else + if not charsacc then + acc = fallbacks[acc] + charsacc = acc and chars[acc] + end + if charsacc then + local chr_t = cache[chr] + if not cht_t then + chr_t = {"slot", 1, chr} + cache[chr] = chr_t + end + local acc_t = cache[acc] + if not acc_t then + acc_t = {"slot", 1, acc} + cache[acc] = acc_t + end + local cb = descs[chr].boundingbox + local ab = descs[acc].boundingbox + if cb and ab then + -- can be sped up for scale == 1 + local c_llx, c_lly, c_urx, c_ury = scale*cb[1], scale*cb[2], scale*cb[3], scale*cb[4] + local a_llx, a_lly, a_urx, a_ury = scale*ab[1], scale*ab[2], scale*ab[3], scale*ab[4] + local dx = (c_urx - a_urx - a_llx + c_llx)/2 + local dd = (c_urx - c_llx)*ita_cor + if a_ury < 0 then + if trace_combining then + t.commands = { + push, + {"right", dx-dd}, + red, + acc_t, + black, + pop, + chr_t, + } + else + t.commands = { + push, + {"right", dx-dd}, + acc_t, + pop, + chr_t, + } + end + elseif c_ury > a_lly then + local dy = cap_lly-a_lly + if trace_combining then + t.commands = { + push, + {"right", dx+dd}, + {"down", -dy}, + green, + acc_t, + black, + pop, + chr_t, + } + else + t.commands = { + push, + {"right", dx+dd}, + {"down", -dy}, + acc_t, + pop, + chr_t, + } + end + else + if trace_combining then + t.commands = { + {"push"}, + {"right", dx+dd}, + blue, + acc_t, + black, + {"pop"}, + chr_t, + } + else + t.commands = { + {"push"}, + {"right", dx+dd}, + acc_t, + {"pop"}, + chr_t, + } + end + end + done = true + end + end + chars[i] = t + local d = { } + for k, v in next, descs[chr] do + d[k] = v + end + d.name = c.adobename or "unknown" + -- d.unicode = i + descs[i] = d + end + end + end + end + end + if done then + g.virtualized = true + end + end +end + +vf.aux.combine.commands["complete-composed-characters"] = function(g,v) + vf.aux.compose_characters(g) +end + +--~ {'special', 'pdf: q ' .. s .. ' 0 0 '.. s .. ' 0 0 cm'}, +--~ {'special', 'pdf: q 1 0 0 1 ' .. -w .. ' ' .. -h .. ' cm'}, +--~ -- {'special', 'pdf: /Fm\XX\space Do'}, +--~ {'special', 'pdf: Q'}, +--~ {'special', 'pdf: Q'}, + +-- for documentation purposes we provide: + +fonts.define.methods.install("fallback", { -- todo: auto-fallback with loop over data.characters + { "fake-character", 0x00A2, 'textcent' }, + { "fake-character", 0x20AC, 'texteuro' } +}) + +vf.aux.combine.commands["enable-force"] = function(g,v) + vf.aux.combine.force_composed = true + vf.aux.combine.force_fallback = true +end +vf.aux.combine.commands["disable-force"] = function(g,v) + vf.aux.combine.force_composed = false + vf.aux.combine.force_fallback = false +end + +fonts.define.methods.install("demo-2", { + { "enable-tracing" }, + { "enable-force" }, + { "initialize" }, + { "include-method", "fallback" }, + { "complete-composed-characters" }, + { "disable-tracing" }, + { "disable-force" }, +}) + +fonts.define.methods.install("demo-3", { + { "enable-tracing" }, + { "initialize" }, + { "complete-composed-characters" }, + { "disable-tracing" }, +}) diff --git a/tex/context/base/font-gds.lua b/tex/context/base/font-gds.lua new file mode 100644 index 000000000..e3db8c816 --- /dev/null +++ b/tex/context/base/font-gds.lua @@ -0,0 +1,294 @@ +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" +} + +local type, next = type, next +local gmatch = string.gmatch + +local trace_goodies = false trackers.register("fonts.goodies", function(v) trace_goodies = v end) + +-- goodies=name,colorscheme=,featureset= +-- +-- goodies=auto + +-- goodies + +fonts.goodies = fonts.goodies or { } +fonts.goodies.data = fonts.goodies.data or { } +fonts.goodies.list = fonts.goodies.list or { } + +local data = fonts.goodies.data +local list = fonts.goodies.list + +function fonts.goodies.report(what,trace,goodies) + if trace_goodies or trace then + local whatever = goodies[what] + if whatever then + logs.report("fonts", "goodie '%s' found in '%s'",what,goodies.name) + end + end +end + +local function getgoodies(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 = resolvers.find_file(file.addsuffix(filename,"lfg")) or "" -- prefered suffix + if fullname == "" then + fullname = resolvers.find_file(file.addsuffix(filename,"lua")) or "" -- fallback suffix + end + if fullname == "" then + logs.report("fonts", "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 + logs.report("fonts", "goodie file '%s' is invalid",fullname) + return nil + elseif trace_goodies then + logs.report("fonts", "goodie file '%s' 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 fonts.goodies.register(name,fnc) + list[name] = fnc +end + +fonts.goodies.get = getgoodies + +-- register goodies file + +local preset_context = fonts.define.specify.preset_context + +function fonts.initializers.common.goodies(tfmdata,value) + local goodies = tfmdata.goodies or { } -- future versions might store goodies in the cached instance + for filename in gmatch(value,"[^, ]+") do + -- we need to check for duplicates + local ok = getgoodies(filename) + if ok then + goodies[#goodies+1] = ok + end + end + tfmdata.goodies = goodies -- shared ? +end + +-- featuresets + +local function flattened(t,tt) + -- first set value dominates + local tt = tt or { } + for i=1,#t do + local ti = t[i] + if type(ti) == "table" then + flattened(ti,tt) + elseif tt[ti] == nil then + tt[ti] = true + end + end + for k, v in next, t do + if type(k) ~= "number" then + if type(v) == "table" then + flattened(v,tt) + elseif tt[k] == nil then + tt[k] = v + end + end + end + return tt +end + +fonts.flattened_features = flattened + +function fonts.goodies.prepare_features(goodies,name,set) + if set then + local ff = fonts.flattened_features(set) + local fullname = goodies.name .. "::" .. name + local n, s = preset_context(fullname,"",ff) + goodies.featuresets[name] = s -- set + if trace_goodies then + logs.report("fonts", "feature set '%s' gets number %s and name '%s'",name,n,fullname) + end + return n + end +end + +local function initialize(goodies,tfmdata) + local featuresets = goodies.featuresets + local goodiesname = goodies.name + if featuresets then + if trace_goodies then + logs.report("fonts", "checking featuresets in '%s'",goodies.name) + end + for name, set in next, featuresets do + fonts.goodies.prepare_features(goodies,name,set) + end + end +end + +fonts.goodies.register("featureset",initialize) + +function fonts.initializers.common.featureset(tfmdata,set) + local goodies = tfmdata.goodies -- shared ? + if goodies then + local features = tfmdata.shared.features + local what + for i=1,#goodies do + -- last one counts + 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 + tfmdata.mode = features.mode or tfmdata.mode + end + end +end + +-- colorschemes + +fonts.goodies.colorschemes = fonts.goodies.colorschemes or { } +fonts.goodies.colorschemes.data = fonts.goodies.colorschemes.data or { } + +local colorschemes = fonts.goodies.colorschemes + +function fonts.initializers.common.colorscheme(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 what then + -- this is font bound but we can share them if needed + -- just as we could hash the conversions (per font) + local hash, reverse = tfmdata.luatex.unicodes, { } + for i=1,#what do + local w = what[i] + for j=1,#w do + local name = w[j] + local unicode = hash[name] + if unicode then + reverse[unicode] = i + end + end + end + tfmdata.colorscheme = reverse + return + end + end + end + tfmdata.colorscheme = false +end + +local fontdata = fonts.ids +local fcs = fonts.color.set +local has_attribute = node.has_attribute +local traverse_id = node.traverse_id +local a_colorscheme = attributes.private('colorscheme') +local glyph = node.id("glyph") + +function fonts.goodies.colorschemes.coloring(head) + local lastfont, lastscheme + for n in traverse_id(glyph,head) do + local a = has_attribute(n,a_colorscheme) + if a then + local f = n.font + if f ~= lastfont then + lastscheme, lastfont = fontdata[f].colorscheme, f + end + if lastscheme then + local sc = lastscheme[n.char] + if sc then + fcs(n,"colorscheme:"..a..":"..sc) -- slow + end + end + end + end +end + +function fonts.goodies.colorschemes.enable() + tasks.appendaction("processors","fonts","fonts.goodies.colorschemes.coloring") + function fonts.goodies.colorschemes.enable() end +end + +-- installation (collected to keep the overview) + +fonts.otf.tables.features['goodies'] = 'Goodies on top of built in features' +fonts.otf.tables.features['featurset'] = 'Goodie Feature Set' +fonts.otf.tables.features['colorscheme'] = 'Goodie Color Scheme' + +fonts.otf.features.register('goodies') +fonts.otf.features.register('featureset') +fonts.otf.features.register('colorscheme') + +table.insert(fonts.triggers, 1, "goodies") +table.insert(fonts.triggers, 2, "featureset") -- insert after +table.insert(fonts.triggers, "colorscheme") + +fonts.initializers.base.otf.goodies = fonts.initializers.common.goodies +fonts.initializers.node.otf.goodies = fonts.initializers.common.goodies + +fonts.initializers.base.otf.featureset = fonts.initializers.common.featureset +fonts.initializers.node.otf.featureset = fonts.initializers.common.featureset + +fonts.initializers.base.otf.colorscheme = fonts.initializers.common.colorscheme +fonts.initializers.node.otf.colorscheme = fonts.initializers.common.colorscheme + +-- 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 + local virtuals = mathgoodies and mathgoodies.virtuals + local mapfiles = mathgoodies and mathgoodies.mapfiles + local maplines = mathgoodies and mathgoodies.maplines + if virtuals then + for name, specification in next, virtuals do + mathematics.make_font(name,specification) + end + end + if mapfiles then + for i=1,#mapfiles do + fonts.map.loadfile(mapfiles[i]) -- todo: backend function + end + end + if maplines then + for i=1,#maplines do + fonts.map.loadline(maplines[i]) -- todo: backend function + end + end +end + +fonts.goodies.register("mathematics", initialize) + +-- 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 diff --git a/tex/context/base/font-gds.mkiv b/tex/context/base/font-gds.mkiv new file mode 100644 index 000000000..e36116283 --- /dev/null +++ b/tex/context/base/font-gds.mkiv @@ -0,0 +1,83 @@ +%D \module +%D [ file=font-gds, +%D version=2009.10.14, +%D title=\CONTEXT\ Font Support, +%D subtitle=Colorschemes, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Font Support / Colorschemes} + +% \registerctxluafile{font-gds}{1.001} + +\unprotect + +\def\loadfontgoodies[#1]% + {\ctxlua{fonts.goodies.get("#1")}} + + +% this will become colorgroups and move to font-col or so + +\definecolor[colorscheme:1:1][s=.75] +\definecolor[colorscheme:1:2][r=.75] +\definecolor[colorscheme:1:3][g=.75] +\definecolor[colorscheme:1:4][b=.75] +\definecolor[colorscheme:1:5][c=.75] +\definecolor[colorscheme:1:6][m=.75] +\definecolor[colorscheme:1:7][y=.75] + +\definecolor[colorscheme:2:7][s=.75] +\definecolor[colorscheme:2:6][r=.75] +\definecolor[colorscheme:2:5][g=.75] +\definecolor[colorscheme:2:4][b=.75] +\definecolor[colorscheme:2:3][c=.75] +\definecolor[colorscheme:2:2][m=.75] +\definecolor[colorscheme:2:1][y=.75] + +\definesystemattribute[colorscheme] \chardef\colorschemeattribute \dogetattributeid{colorscheme} + +\def\setfontcolorscheme + {\ctxlua{fonts.goodies.colorschemes.enable()}% + \xdef\setfontcolorscheme[##1]{\attribute\colorschemeattribute##1\relax}% + \setfontcolorscheme} + +\edef\resetfontcolorscheme{\attribute\colorschemeattribute\attributeunsetvalue} + +\protect \endinput + +% \definefontfeature[husayni-colored][goodies=husayni,colorscheme=default,featureset=default] +% +% \definedfont[husayni*husayni-colored at 36pt] +% +% \starttext \pardir TRT \textdir TRT +% +% \setfontcolorscheme[1] +% +% اَلْحَمْدُ لِلّٰهِ حَمْدَ مُعْتَرِفٍ بِحَمْدِهٖ، مُغْتَرِفٌ مِنْ بِحَارِ +% مَجْدِهٖ، بِلِسَانِ ٱلثَّنَاۤءِ شَاكِرًا، وَلِحُسْنِ اٰلاۤئِهٖ نَاشِرًا؛ +% اَلَّذِيْ خَلَقَ ٱلْمَوْتَ وَٱلْحَيٰوةَ، وَٱلْخَيْرَ وَٱلشَّرَّ، +% وَٱلنَّفْعَ وَٱلضَّرَّ، وَٱلسُّكُوْنَ وَٱلْحَرَكَةَ، وَٱلْأَرْوَاحَ +% وَٱلْأَجْسَامَ، وَٱلذِّكْرَ وَٱلنِّسْيَانَ. +% +% \setfontcolorscheme[2] +% +% اَلْحَمْدُ لِلّٰهِ حَمْدَ مُعْتَرِفٍ بِحَمْدِهٖ، مُغْتَرِفٌ مِنْ بِحَارِ +% مَجْدِهٖ، بِلِسَانِ ٱلثَّنَاۤءِ شَاكِرًا، وَلِحُسْنِ اٰلاۤئِهٖ نَاشِرًا؛ +% اَلَّذِيْ خَلَقَ ٱلْمَوْتَ وَٱلْحَيٰوةَ، وَٱلْخَيْرَ وَٱلشَّرَّ، +% وَٱلنَّفْعَ وَٱلضَّرَّ، وَٱلسُّكُوْنَ وَٱلْحَرَكَةَ، وَٱلْأَرْوَاحَ +% وَٱلْأَجْسَامَ، وَٱلذِّكْرَ وَٱلنِّسْيَانَ. +% +% \resetfontcolorscheme +% +% اَلْحَمْدُ لِلّٰهِ حَمْدَ مُعْتَرِفٍ بِحَمْدِهٖ، مُغْتَرِفٌ مِنْ بِحَارِ +% مَجْدِهٖ، بِلِسَانِ ٱلثَّنَاۤءِ شَاكِرًا، وَلِحُسْنِ اٰلاۤئِهٖ نَاشِرًا؛ +% اَلَّذِيْ خَلَقَ ٱلْمَوْتَ وَٱلْحَيٰوةَ، وَٱلْخَيْرَ وَٱلشَّرَّ، +% وَٱلنَّفْعَ وَٱلضَّرَّ، وَٱلسُّكُوْنَ وَٱلْحَرَكَةَ، وَٱلْأَرْوَاحَ +% وَٱلْأَجْسَامَ، وَٱلذِّكْرَ وَٱلنِّسْيَانَ. +% +% \stoptext diff --git a/tex/context/base/font-heb.mkii b/tex/context/base/font-heb.mkii new file mode 100644 index 000000000..3ec0395f3 --- /dev/null +++ b/tex/context/base/font-heb.mkii @@ -0,0 +1,5 @@ +\unprotected \writestatus\m!systems{load ARABTEX as a module instead} + +\usemodule[arabtex] + +\endinput diff --git a/tex/context/base/font-ini.lua b/tex/context/base/font-ini.lua new file mode 100644 index 000000000..e45149781 --- /dev/null +++ b/tex/context/base/font-ini.lua @@ -0,0 +1,95 @@ +if not modules then modules = { } end modules ['font-ini'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +--[[ldx-- +Not much is happening here.
+--ldx]]-- + +local utf = unicode.utf8 +local format, serialize = string.format, table.serialize +local write_nl = texio.write_nl + +if not fontloader then fontloader = fontforge end + +fontloader.totable = fontloader.to_table + +-- vtf comes first +-- fix comes last + +fonts = fonts or { } + +fonts.ids = fonts.ids or { } fonts.identifiers = fonts.ids -- aka fontdata +fonts.chr = fonts.chr or { } fonts.characters = fonts.chr -- aka chardata +fonts.qua = fonts.qua or { } fonts.quads = fonts.qua -- aka quaddata + +fonts.tfm = fonts.tfm or { } + +fonts.mode = 'base' +fonts.private = 0xF0000 -- 0x10FFFF +fonts.verbose = false -- more verbose cache tables + +fonts.ids[0] = { -- nullfont + characters = { }, + descriptions = { }, + name = "nullfont", +} + +fonts.chr[0] = { } + +fonts.methods = fonts.methods or { + base = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } }, + node = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } }, +} + +fonts.initializers = fonts.initializers or { + base = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } }, + node = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } } +} + +fonts.triggers = fonts.triggers or { + 'mode', + 'language', + 'script', + 'strategy', +} + +fonts.processors = fonts.processors or { +} + +fonts.manipulators = fonts.manipulators or { +} + +fonts.define = fonts.define or { } +fonts.define.specify = fonts.define.specify or { } +fonts.define.specify.synonyms = fonts.define.specify.synonyms or { } + +-- tracing + +if not fonts.color then + + fonts.color = { + set = function() end, + reset = function() end, + } + +end + +-- format identification + +fonts.formats = { } + +function fonts.fontformat(filename,default) + local extname = file.extname(filename) + local format = fonts.formats[extname] + if format then + return format + else + logs.report("fonts define","unable to detemine font format for '%s'",filename) + return default + end +end diff --git a/tex/context/base/font-ini.mkii b/tex/context/base/font-ini.mkii new file mode 100644 index 000000000..89fbb5d07 --- /dev/null +++ b/tex/context/base/font-ini.mkii @@ -0,0 +1,4573 @@ +%D \module +%D [ file=font-ini, +%D version=1998.09.11, % (second) +%D version=2001.02.20, % (third) +%D title=\CONTEXT\ Font Macros, +%D subtitle=Initialization, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Font Macros / Initialization} + +\unprotect + +% \def\fontrange#1% +% {\dofontrange{#1 =\bodyfontsize}} +% +% \def\dofontrange#1% +% {\dodofontrange#1 \relax}% \fontstringA +% +% \def\dodofontrange#1 #2 % +% {\ifdim\bodyfontsize#2% +% #1\expandafter\gobbleuntilrelax +% \else +% \expandafter\dodofontrange +% \fi} +% +% \definefont +% [crap] +% [\fontrange +% {Regular <10pt +% RegularBold <12pt +% RegularSlanted <15pt +% Regular} sa 1] +% +% may be better: +% +% \definefontrange +% [crap] +% [Regular <10pt +% RegularBold <12pt +% RegularSlanted <15pt] +% [Regular sa 1] +% +% +% \dostepwiserecurse{2}{15}{1} +% {{\switchtobodyfont[\recurselevel pt]\crap test}\endgraf} + +% adapted, else wrong interlinespace + +\def\setfontparameters + {\synchronizefontsfalse + \the\everybodyfont + \synchronizefontstrue} + +% handy + +\newcounter\pushedfont + +\def\savefont + {\edef\savedfont{\the\font}% + \pushmacro\savedfont + \pushmacro\currentregime + \pushmacro\charactermapping + \pushmacro\characterencoding} + +\def\restorefont + {\popmacro\characterencoding + \popmacro\charactermapping + \popmacro\currentregime + \popmacro\savedfont + \savedfont} + +\def\pushcurrentfont + {\edef\popcurrentfont + {\noexpand\def\noexpand\fontbody{\fontbody}% + \noexpand\def\noexpand\fontstyle{\fontstyle}% + \noexpand\dosetcurrentfontalternative{\fontalternative}% + \noexpand\dosetcurrentfontsize{\fontsize}% + \noexpand\synchronizefont}} + +% \definetypeface[one][rm][serif][computer-roman][default] +% \definetypeface[two][rm][serif][computer-roman][default][rscale=.9] +% +% {\one \bf test \two test} +% {\one \bf test \pushcurrentfont \two \popcurrentfont test} + +%D \macros +%D {definedfont,startfont,doiffontcharelse} +%D +%D A couple of relatively new macros: + +\newevery \everydefinedfont \relax + +\def\dodefinedfont[#1]% + {\iffirstargument\definefont[\string\thedefinedfont][#1]\fi + \csname\string\thedefinedfont\endcsname % can be \relax + \the\everydefinedfont} % hm, redundant + +\unexpanded\def\definedfont + {\dosingleempty\dodefinedfont} + +\unexpanded\def\startfont + {\bgroup\definedfont} + +\def\stopfont + {\egroup} + +\def\doiffontcharelse#1#2% + {\bgroup + \definedfont[#1]% + \iffontchar\font#2\relax + \egroup\expandafter\firstoftwoarguments + \else + \egroup\expandafter\secondoftwoarguments + \fi} + +%%% message 14 added + +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved + +%D This module is one of the oldest modules of \CONTEXT. The +%D macros below evolved out of the \PLAIN\ \TEX\ macros and +%D therefore use a similar naming scheme (\type{\rm}, +%D \type{\bf}, etc). This module grew out of our needs. We +%D started with the \PLAIN\ \TEX\ definitions, generalized the +%D underlaying macros, and extended those to a level at which +%D probably no one will ever recognize them. +%D +%D In 2001 we ran into a couple of projects where more than +%D one combined set of fonts was involved in a document. To +%D make definitions more readable, as well as to overcome the +%D problem of ever growing file name lists, and also because +%D we needed to scale fonts relative to each other, the low +%D level implementation was partly rewritten. Global +%D font assignments, relative scaling, font classes and alike +%D were added then. At the same time some macros were made a +%D bit more readable, and math support was extended to the +%D larger sizes. +%D +%D One important characteristic of the font mechanism presented +%D here is the postponing of font loading. This makes it +%D possible to distribute \type{fmt} files without bothering +%D about the specific breed of \type{tfm} files. +%D +%D Another feature implemented here is the massive switching +%D from roman to {\ss sans serif}, {\tt teletype} or else. This +%D means one doesn't have to take care of all kind of relations +%D between fonts. +%D +%D \page[bigpreference] + +%D \macros +%D {rm,ss,tt,hw,cg} +%D +%D Fonts are defined in separate files. When we define a font, +%D we distinguish between several styles. In most cases we will +%D use: +%D +%D \startlinecorrection +%D \starttable[|l||] +%D \HL +%D \NC roman regular serif \NC \type{\rm} \NC\FR +%D \NC sansserif sans support \NC \type{\ss} \NC\MR +%D \NC type teletype mono \NC \type{\tt} \NC\LR +%D \HL +%D \stoptable +%D \stoplinecorrection +%D +%D The number of styles is not limited to these three. When +%D using Lucida Bright we can for instance also define: +%D +%D \startlinecorrection +%D \starttable[|l||] +%D \HL +%D \NC handwritten \NC \type{\hw} \NC\FR +%D \NC calligraphic \NC \type{\cg} \NC\LR +%D \HL +%D \stoptable +%D \stoplinecorrection +%D +%D Anyone who feels the need, can define additional ones, like +%D +%D \startlinecorrection +%D \starttable[|l||] +%D \HL +%D \NC faxfont \NC \type{\ff} \NC\FR +%D \NC blackboard \NC \type{\bb} \NC\LR +%D \HL +%D \stoptable +%D \stoplinecorrection +%D +%D Or even +%D +%D \startlinecorrection +%D \starttable[|l||] +%D \HL +%D \NC hebrew \NC \type{\hb} \NC\SR +%D \HL +%D \stoptable +%D \stoplinecorrection +%D +%D Styles are grouped in font sets. At the moment there are +%D three main sets defined: +%D +%D \startlinecorrection +%D \starttable[|l|l||] +%D \HL +%D \NC Computer Modern Roman \NC Knuth \NC \type{cmr} \NC\FR +%D \NC Lucida Bright \NC Bigelow \& Holmes \NC \type{lbr} \NC\MR +%D \NC Standard Postscript Fonts \NC Adobe \NC \type{pos} \NC\LR +%D \HL +%D \stoptable +%D \stoplinecorrection +%D +%D There are also some Computer Modern Roman alternatives: +%D +%D \startlinecorrection +%D \starttable[|l|l||] +%D \HL +%D \NC Computer Modern Roman \NC Knuth \& Sauter \NC \type{sau} \NC\FR +%D \NC Euler fonts \NC Zapf \NC \type{eul} \NC\MR +%D \NC Computer Modern Concrete \NC Knuth \& Zapf \NC \type{con} \NC\LR +%D \HL +%D \stoptable +%D \stoplinecorrection +%D +%D All these definitions are ordered in files with names like +%D \type{font-cmr} and \type{font-pos}, where the last three +%D characters specify the name as known to \CONTEXT. +%D +%D Within such a font set (\type{cmr}) and style (\type{\rm}) +%D we can define a number of text font alternatives: +%D +%D \startlinecorrection +%D \starttable[|l||] +%D \HL +%D \NC typeface \NC \type{\tf} \NC\FR +%D \NC boldface \NC \type{\bf} \NC\MR +%D \NC slanted \NC \type{\sl} \NC\MR +%D \NC italic \NC \type{\it} \NC\MR +%D \NC boldslanted \NC \type{\bs} \NC\MR +%D \NC bolditalic \NC \type{\bi} \NC\MR +%D \NC smallcaps \NC \type{\sc} \NC\LR +%D \HL +%D \stoptable +%D \stoplinecorrection + +%D For old stylish Frans Goddijn we have: +%D +%D \startlinecorrection +%D \starttable[|l||] +%D \HL +%D \NC oldstyle \NC \type{\os} \NC\SR +%D \HL +%D \stoptable +%D \stoplinecorrection +%D +%D The availability of these alternatives depends on the +%D completeness of a font family and of course the definitions +%D in the font files. +%D +%D But let's not forget math. In addition to the previous \TEX\ +%D families (the mysterious \type{\fam}'s) we've got some more: +%D +%D \startlinecorrection +%D \starttable[|l||] +%D \HL +%D \NC Math Roman \NC \type{\mr} \NC\FR +%D \NC Math Italic \NC \type{\mi} \NC\MR +%D \NC Math Symbol \NC \type{\sy} \NC\MR +%D \NC Math Extra \NC \type{\ex} \NC\MR +%D \NC Math A \NC \type{\ma} \NC\MR +%D \NC Math B \NC \type{\mb} \NC\MR +%D \NC Math C \NC \type{\mc} \NC\LR +%D \HL +%D \stoptable +%D \stoplinecorrection +%D +%D Users can call for specific fonts in many ways. Switches to +%D other typefaces, like the switch from normal to bold, are as +%D intuitive as possible, which means that all dependant fonts +%D also switch. One can imagine that this takes quite some +%D processing time. +%D +%D Internally fonts are stored as combination of size, style +%D and alternative, e.g. \type{12pt}+\type{\ss}+\type{\bf}. +%D Users are not confronted with sizes, but use the style or +%D style+alternative to activate them. +%D +%D During the definition of a bodyfont one can also declare the +%D available larger alternatives: +%D +%D \starttyping +%D \tf \tfa \tfb \tfc ... +%D \bf \bfa \bfb \bfc ... +%D \sl \sla \slb \slc ... +%D \stoptyping +%D +%D The smaller ones are automatically supplied and derived from +%D the the bodyfont environment. +%D +%D \starttyping +%D \tfx \tfxx +%D \bfx \bfxx +%D \slx \slxx +%D \stoptyping +%D +%D There are only two smaller alternatives per style. The +%D larger alternatives on the other hand have no limitations. +%D +%D These larger alternatives are mostly used in chapter and +%D section titles or on title pages. When one switches to a +%D larger alternative, the bold an other ones automatically +%D adapt themselves: +%D +%D \startbuffer +%D \tfd Hi \bf there\sl, here \tfb I \bf am +%D \stopbuffer +%D +%S \startnarrower +%D \typebuffer +%S \stopnarrower +%D +%D therefore becomes: +%D +%D \startvoorbeeld +%D \getbuffer +%D \stopvoorbeeld +%D +%D Maybe this mechanism isn't always as logic, but as said +%D before, we tried to make it as intuitive as possible. +%D +%D So a specific kind of glyph can be characterized by: +%D +%D \startnarrower +%D family (cmr) + bodyfont (12pt) + style (rm) + alternative (bf) + size (a) +%D \stopnarrower +%D +%D The last component (the size) is optional. +%D +%D We introduced \type{\tf} as command to call for the current +%D normally sized typeface. This commands results in roman, +%D sans serif, teletype or whatever style is in charge. Such +%D rather massive switches of style sometimes take more +%D processing time than comfortable. Of course there is a +%D workaround for this: we can call fonts directly by means of +%D commands like: +%D +%D \starttyping +%D \rmtf \sssl \tttf \rmbsa +%D \stoptyping +%D +%D One should realize that this fast calls have limitations, +%D they lack for instance automatic super- and subscript +%D support. +%D +%D This leaves us two more commands: \type{\tx} and +%D \type{\txx}. These activate a smaller and even more smaller +%D font than the current one and adapt themselves to the +%D current alternative, so when \type{\bf} is active, +%D \type{\tx} gives a smaller boldface, which in turn can be +%D called directly by \type{\bfx}. +%D +%D These two smaller alternatives are specified by the bodyfont +%D environment and therefore not necessarily have similar sizes +%D as \type{\scriptsize} and \type{\scriptscriptsize}. The main +%D reason for this incompatibility (which can easily be undone) +%D lays in the fact that we often want a bit bigger characters +%D than in math mode. In \CONTEXT\ for instance the \type{\tx} +%D and \type{\txx} commands are used for surrogate +%D \cap{smallcaps} which support both nesting and alternatives, +%D like in {\bf\cap{a \cap{small} world}}, which was typeset by +%D +%D \starttyping +%D \bf\cap{a \cap{small} world} +%D \stoptyping +%D +%D And compare $\rm \scriptstyle THIS$ with the slightly larger +%D \cap{THIS}: \ruledhbox{$\rm \scriptstyle scriptstyle: THIS$} +%D or \ruledhbox{\cap{x style: THIS}} makes a big difference. + +%D The \type{x..d} sizes should be used grouped. If you +%D don't group them, i.e. call them in a row, \CONTEXT\ will +%D not be able to sort out your intention (\type {x} inside +%D \type {d} inside \type {x}. etc.). The following table +%D demonstrates this: +%D +%D \def\FontState{\setstrut\ruledhbox{\strut Hello}} +%D +%D \starttabulate[|||||] +%D \HL +%D \NC \rlap{\quad\bf grouped} \NC \NC \type {\tx} \NC \type {\txx} \NC \NR +%D \HL +%D \NC \type{\tfx} \NC \tfx \FontState \NC \tfx \tx \FontState \NC \tfx \txx \FontState \NC \NR +%D \NC \type{\tfxx} \NC \tfxx \FontState \NC \tfxx\tx \FontState \NC \tfxx\txx \FontState \NC \NR +%D \NC \type{\tf} \NC \tf \FontState \NC \tf \tx \FontState \NC \tf \txx \FontState \NC \NR +%D \NC \type{\tfa} \NC \tfa \FontState \NC \tfa \tx \FontState \NC \tfa \txx \FontState \NC \NR +%D \NC \type{\tfb} \NC \tfb \FontState \NC \tfb \tx \FontState \NC \tfb \txx \FontState \NC \NR +%D \NC \type{\tfc} \NC \tfc \FontState \NC \tfc \tx \FontState \NC \tfc \txx \FontState \NC \NR +%D \NC \type{\tfd} \NC \tfd \FontState \NC \tfd \tx \FontState \NC \tfd \txx \FontState \NC \NR +%D \NC \type{\tfx} \NC \tfx \FontState \NC \tfx \tx \FontState \NC \tfx \txx \FontState \NC \NR +%D \NC \type{\tfxx} \NC \tfxx \FontState \NC \tfxx\tx \FontState \NC \tfxx\txx \FontState \NC \NR +%D \HL +%D \stoptabulate +%D +%D \blank +%D +%D \starttabulate[|||||] +%D \HL +%D \NC \rlap{\quad\bf stacked} \NC \NC \type {\tx} \NC \type {\txx} \NC \NR +%D \HL +%D \NC \type{\tfx} +%D \NC \tfx \FontState +%D \NC \tfx \tx \FontState +%D \NC \tfx \txx \FontState +%D \NC \NR +%D \NC \type{\tfxx} +%D \NC \tfx\tfxx \FontState +%D \NC \tfx\tfxx \tx \FontState +%D \NC \tfx\tfxx \txx \FontState +%D \NC \NR +%D \NC \type{\tf} +%D \NC \tfx\tfxx\tf \FontState +%D \NC \tfx\tfxx\tf \tx \FontState +%D \NC \tfx\tfxx\tf \txx \FontState +%D \NC \NR +%D \NC \type{\tfa} +%D \NC \tfx\tfxx\tf\tfa \FontState +%D \NC \tfx\tfxx\tf\tfa \tx \FontState +%D \NC \tfx\tfxx\tf\tfa \txx \FontState +%D \NC \NR +%D \NC \type{\tfb} +%D \NC \tfx\tfxx\tf\tfa\tfb \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb \tx \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb \txx \FontState +%D \NC \NR +%D \NC \type{\tfc} +%D \NC \tfx\tfxx\tf\tfa\tfb\tfc \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb\tfc \tx \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb\tfc \txx \FontState +%D \NC \NR +%D \NC \type{\tfd} +%D \NC \tfx\tfxx\tf\tfa\tfb\tfd \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb\tfd \tx \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb\tfd \txx \FontState +%D \NC \NR +%D \NC \type{\tfx} +%D \NC \tfx\tfxx\tf\tfa\tfb\tfc\tfx \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb\tfc\tfx \tx \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb\tfc\tfx \txx \FontState +%D \NC \NR +%D \NC \type{\tfxx} +%D \NC \tfx\tfxx\tf\tfa\tfb\tfc\tfx\tfxx \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb\tfc\tfx\tfxx \tx \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb\tfc\tfx\tfxx \txx \FontState +%D \NC \NR +%D \HL +%D \stoptabulate + +%D \macros +%D {mf} +%D +%D Math fonts are a species in their own. They are tightly +%D hooked into smaller and even smaller ones of similar breed +%D to form a tight family. Let's first see how these are +%D related: +%D +%D \startbuffer +%D $\tf x^2+\bf x^2+\sl x^2+\it x^2+\bs x^2+ \bi x^2 =\rm 6x^2$ +%D $\tf x^2+\bf x^2+\sl x^2+\it x^2+\bs x^2+{\bi x^2}=\rm 6x^2$ +%D $\tf x^2+\bf x^2+\sl x^2+\it x^2+\bs x^2+ \bi x^2 =\tf 6x^2$ +%D $\tf x^2+\bf x^2+\sl x^2+\it x^2+\bs x^2+{\bi x^2}=\tf 6x^2$ +%D $\tf x^2+\bf x^2+\sl x^2+\it x^2+\bs x^2+ \bi x^2 =\bf 6x^2$ +%D $\tf x^2+\bf x^2+\sl x^2+\it x^2+\bs x^2+{\bi x^2}=\bf 6x^2$ +%D $\tf x^2+\bf x^2+\sl x^2+\it x^2+\bs x^2+ \bi x^2 =\sl 6x^2$ +%D $\tf x^2+\bf x^2+\sl x^2+\it x^2+\bs x^2+{\bi x^2}=\sl 6x^2$ +%D \stopbuffer +%D +%D \typebuffer +%D +%D Gives both an expected and unexpected result: +%D +%D \startvoorbeeld +%D \startlines +%D \getbuffer +%D \stoplines +%D \stopvoorbeeld +%D +%D We see here that the character shapes change accordingly to +%D the current family, but that the symbols are always typeset +%D in the font assigned to \type{\fam0}. +%D +%D \startbuffer +%D $\tf\mf x^2 + x^2 + x^2 + x^2 + x^2 + x^2 = 6x^2$ +%D $\bf\mf x^2 + x^2 + x^2 + x^2 + x^2 + x^2 = 6x^2$ +%D $\sl\mf x^2 + x^2 + x^2 + x^2 + x^2 + x^2 = 6x^2$ +%D $\bs\mf x^2 + x^2 + x^2 + x^2 + x^2 + x^2 = 6x^2$ +%D $\it\mf x^2 + x^2 + x^2 + x^2 + x^2 + x^2 = 6x^2$ +%D $\bi\mf x^2 + x^2 + x^2 + x^2 + x^2 + x^2 = 6x^2$ +%D \stopbuffer +%D +%D \startvoorbeeld +%D \startlines +%D \getbuffer +%D \stoplines +%D \stopvoorbeeld +%D +%D In this example we see a new command \type{\mf} surface +%D which means as much as {\em math font}. This commands +%D reactivates the last font alternative and therefore equals +%D \type{\bf}, \type{\sl} etc. but by default it equals +%D \type{\tf}: + +\unexpanded\def\mf + {\dodosetmathfont\fontalternative + \csname\fontalternative\endcsname} + +%D The previous example was typeset saying: +%D +%D \typebuffer +%D +%D Beware: the exact location of \type{\mf} is not that +%D important, we could as well has said +%D +%D \startbuffer +%D $\bf x^2 + x^2 + x^2 + x^2 + x^2 + x^2 = \mf 6x^2$ +%D \stopbuffer +%D +%D \typebuffer +%D +%D This is due to the way \TEX\ handles fonts in math mode. +%D +%D Of course we'll have to redefine \type{\mf} every time we +%D change the current \type{\fam}. + +%D \macros +%D {mbox,enablembox,mathop} +%D +%D Now how can we put this to use? Will the next sequence +%D give the desired result? +%D +%D \startbuffer +%D $\bf x^2 + \hbox{\mf whatever} + \sin(2x)$ +%D \stopbuffer +%D +%D \typebuffer +%D +%D It won't! +%D +%D \startvoorbeeld +%D \let\mathop=\normalmathop \getbuffer +%D \stopvoorbeeld +%D +%D The reason for this is that \type{\sin} is defined as: +%D +%D \starttyping +%D \def\sin{\mathop{\rm sin}\nolimits} +%D \stoptyping +%D +%D We can fix this by defining + +\unexpanded\def\mathop + {\normalmathop + \bgroup + \let\rm\mf + \let\next=} + +%D We can fix arbitrary horizontal boxes by redefining the +%D \TEX\ primitive \type{\hbox}: +%D +%D \starttyping +%D \def\hbox{\ifmmode\mbox\else\normalhbox\fi} +%D \stoptyping +%D +%D with +%D +%D \starttyping +%D \def\mbox#1#% +%D {\normalhbox#1\bgroup\mf\let\next=} +%D \stoptyping +%D +%D or more robust, that is, also accepting \type{\hbox\bgroup}: +%D +%D \starttyping +%D \def\mbox% +%D {\normalhbox\bgroup\mf +%D \dowithnextbox{\flushnextbox\egroup}% +%D \normalhbox} +%D \stoptyping +%D +%D And now: +%D +%D \startbuffer +%D $\bf x^2 + \hbox{whatever} + \sin(2x)$ +%D \stopbuffer +%D +%D \typebuffer +%D +%D Indeed gives: +%D +%D \startvoorbeeld +%D \enablembox\getbuffer +%D \stopvoorbeeld +%D +%D But, do we want this kind of trickery to be activated? No, +%D simply because we cannot be sure of incompatibilities, +%D although for instance unboxing goes ok. Therefore we +%D introduce: + +% best can go to math-ini and make \mf a hook then + +% better use \dowithnextboxcontent + +\def\normalmbox + {\normalhbox\bgroup\mf + \dowithnextbox{\flushnextbox\egroup}\normalhbox} + +% to test: +% +% \def\normalmbox +% {\dowithnextboxcontent\mf\flushnextbox\normalhbox} + +\def\mbox + {\ifmmode\normalmbox\else\normalhbox\fi} + +\def\enablembox + {\appendtoks + \ifx\normalhbox\undefined\let\normalhbox\hbox\fi + \let\hbox\mbox + \to\everymathematics} + +%D So in fact one can enable this feature if needed. I would say: +%D go along, but use grouping if needed! + +%D \macros +%D {mrfam,mifam,syfam,exfam, +%D bsfam,bifam,scfam,tffam, +%D mafam,mbfam,msfam} +%D +%D After this short mathematical excursion, we enter the world +%D of fonts and fontswitching. We start with something very +%D \TEX: \type{\fam} specified font families. \TEX\ uses +%D families for managing fonts in math mode. Such a family has +%D three members: text, script and scriptscript: $x^{y^z}$. In +%D \CONTEXT\ we take a bit different approach than \PLAIN\ +%D \TEX\ does. \PLAIN\ \TEX\ needs at least four families for +%D typesetting math. We use those but give them symbolic names. + +\chardef\mrfam = 0 % (Plain TeX) Math Roman +\chardef\mifam = 1 % (Plain TeX) Math Italic +\chardef\syfam = 2 % (Plain TeX) Math Symbol +\chardef\exfam = 3 % (Plain TeX) Math Extra + +%D \PLAIN\ \TEX\ also defines families for {\it italic}, {\sl +%D slanted} and {\bf bold} typefaces, so we don't have to +%D define them here. + +\ifx\itfam\undefined + +\chardef\itfam = 4 % (Plain TeX) Italic +\chardef\slfam = 5 % (Plain TeX) Slanted +\chardef\bffam = 6 % (Plain TeX) Boldface + +\fi + +%D Family~7 in \PLAIN\ \TEX\ is not used in \CONTEXT, because +%D we do massive switches from roman to sans serif, teletype or +%D other faces. + +\ifx\ttfam\undefined + +\chardef\ttfam = 7 % (Plain TeX) can be reused! + +\fi + +%D We define ourselves some more families for {\bs bold +%D slanted}, {\bi bold italic} and {\sc Small Caps}, so +%D we can use them in math mode too. Instead of separate +%D families for {\ss sans serif} and \type{teletype} we use the +%D more general \type{\tffam}, which stands for typeface. + +\chardef\bsfam = 8 % (ConTeXt) BoldSlanted +\chardef\bifam = 9 % (ConTeXt) BoldItalic +\chardef\scfam = 10 % (ConTeXt) SmallCaps +\chardef\tffam = 11 % (ConTeXt) TypeFace + +%D Because Taco needs a few more math families, we reuse +%D family~7 for all those typefaces that have no related +%D family, and therefore are grouped into one. + +\chardef\nnfam = 7 % (ReUsed) NoName + +%D Normally \type{\mrfam} equals \type{\tffam}, but a more +%D distinctive alternatives are possible, for instance the +%D Euler and Concrete Typefaces. +%D +%D After having defined all those in nature non||mathematical +%D families, we define ourselves some real math ones. These are +%D needed for the \AMS\ Symbol Fonts and Extended Lucida +%D Bright. + +\chardef\mafam = 12 % (ConTeXt) Math A Fam (AmsTeX A) +\chardef\mbfam = 13 % (ConTeXt) Math B Fam (AmsTeX B) +\chardef\mcfam = 14 % (ConTeXt) Math C Fam (MathTime) +\chardef\mdfam = 15 % (ConTeXt) Math D Fam (MathTime) + +%D Because there are 16~families and because \type{\ttfam} +%D is reused, at the moment we have no so many families +%D left. By default, we map any newly defined family on the +%D last one (F). + +\def\newfam#1{\chardef#1=15 } + +%D This hack is also needed because in \ETEX\ we are going +%D to reuse the \type {\newfam} allocation counter. + +%D To ease the support of font packages, we als define +%D shortcuts to these familynames. This is necessary because +%D the family names are in fact \type{\chardef}'s, which means +%D that we're dealing with numbers (one can check this by +%D applying \type{\showthe} and \type{\show}). In the +%D specification of math symbols however we need hexadecimal +%D numbers, so we have to convert the \type{\fam}'s value. + +\edef\hexmrfam {\hexnumber\mrfam} \edef\hexbsfam {\hexnumber\bsfam} +\edef\hexmifam {\hexnumber\mifam} \edef\hexbifam {\hexnumber\bifam} +\edef\hexsyfam {\hexnumber\syfam} \edef\hexscfam {\hexnumber\scfam} +\edef\hexexfam {\hexnumber\exfam} \edef\hextffam {\hexnumber\tffam} +\edef\hexitfam {\hexnumber\itfam} \edef\hexmafam {\hexnumber\mafam} +\edef\hexslfam {\hexnumber\slfam} \edef\hexmbfam {\hexnumber\mbfam} +\edef\hexbffam {\hexnumber\bffam} \edef\hexmcfam {\hexnumber\mcfam} +\edef\hexnnfam {\hexnumber\nnfam} \edef\hexmdfam {\hexnumber\mdfam} + +%D \macros +%D {uchar} +%D +%D This macro prepares \CONTEXT\ for \UNICODE\ support. By +%D defining it here, we have at least an safeguard for utility +%D file reading. + +\ifx\uchar\undefined \unexpanded\def\uchar#1#2{[#1,#2]} \fi + +%D We define some (very private) constants to improve speed, +%D memory usage and consistency. + +\def\@size@ {@f@si@} % bodyfont size prefix (12pt etc) +\def\@style@ {@f@st@} % full style prefix (roman etc) +\def\@shortstyle@ {@f@sh@} % short style prefix (rm etc) +\def\@letter@ {@f@le@} % first alternative typeface +\def\@noletter@ {@f@no@} % second alternative typeface +\def\@fontclass@ {@f@cl@} % fontclass + +%D The families can be grouped into math specific ones and +%D more text related families, although text ones can be +%D mapped onto the math ones to get for instance bold math. +%D +%D Both groups of families are handles by a couple of token +%D list tagged as strategies. This implementation makes +%D implementing extensions more comfortable. + +\newtoks \textstrategies +\newtoks \mathstrategies +\newtoks \symbstrategies + +\newif\ifsynchronizemathfonts \synchronizemathfontstrue + +\def\synchronizetext % stylish text in mmode + {\ifsynchronizemathfonts\the\textstrategies\fi} % \if...\fam\minusone\fi} + +\def\synchronizemath % math stuff in mmode + {\ifsynchronizemathfonts\the\mathstrategies\fi} % \if...\fam\minusone\fi} + +\def\synchronizesymb % stylish math stuff in mmode + {\ifsynchronizemathfonts\the\symbstrategies\fi} % \if...\fam\minusone\fi} + +%D By not setting the family we can append a font switch to \type +%D {\everymath}. On the other hand, one never knows in what family +%D state the strategies brought us. +%D +%D \starttyping +%D {\bfa $\the\fam$} {\bfa \everymath{} $\the\fam$} +%D \stoptyping + +%D \macros +%D {textonly} +%D +%D We can inhibit this slow||downer with: + +\def\textonly{\synchronizemathfontsfalse} % document this + +\appendtoks + \dosettextfamily\c!tf + \dosettextfamily\c!bf + \dosettextfamily\c!sl + \dosettextfamily\c!it + \dosettextfamily\c!bs + \dosettextfamily\c!bi + \dosettextfamily\c!sc +\to \textstrategies + +\def\dosettextfamily#1% better pass fontbody to dodoset + {\let\savedfontbody\fontbody + \let\fontfamily#1% + \let\fontbody\scriptscriptface\dodosettextfamily\scriptscriptfont + \let\fontbody\scriptface \dodosettextfamily \scriptfont + \let\fontbody\textface \dodosettextfamily \textfont + \let\fontbody\savedfontbody} + +% \def\s!nullfont{nullfont} + +\def\dodosettextfamily + {\ifx\fontclass\empty + \@EA\dodosettextfamilyA + \else + \@EA\dodosettextfamilyB + \fi} + +\def\dodosettextfamilyA#1% + {\ifcsname \fontbody\c!mm\fontfamily\fontsize\endcsname \autofontsizefalse + \csname \fontbody\c!mm\fontfamily\fontsize\endcsname \else + \ifcsname \fontbody\c!mm\fontfamily\endcsname \autofontsizetrue + \csname \fontbody\c!mm\fontfamily\endcsname \else + \ifcsname \fontbody\c!rm\fontfamily\fontsize\endcsname \autofontsizefalse + \csname \fontbody\c!rm\fontfamily\fontsize\endcsname \else + \ifcsname \fontbody\c!rm\fontfamily\endcsname \autofontsizetrue + \csname \fontbody\c!rm\fontfamily\endcsname \else + \nullfont \autofontsizetrue + \fi\fi\fi\fi + #1\csname\fontfamily\s!fam\endcsname\font} + +\def\dodosettextfamilyB#1% + {\ifcsname\fontclass\fontbody\c!mm\fontfamily\fontsize\endcsname \autofontsizefalse + \csname\fontclass\fontbody\c!mm\fontfamily\fontsize\endcsname \else + \ifcsname\fontclass\fontbody\c!mm\fontfamily\endcsname \autofontsizetrue + \csname\fontclass\fontbody\c!mm\fontfamily\endcsname \else + \ifcsname\fontclass\fontbody\c!rm\fontfamily\fontsize\endcsname \autofontsizefalse + \csname\fontclass\fontbody\c!rm\fontfamily\fontsize\endcsname \else + \ifcsname\fontclass\fontbody\c!rm\fontfamily\endcsname \autofontsizetrue + \csname\fontclass\fontbody\c!rm\fontfamily\endcsname \else + \dodosettextfamilyA#1% + \fi\fi\fi\fi + #1\csname\fontfamily\s!fam\endcsname\font} + +\def\mrfallback{\c!rm\c!tf} + +\appendtoks + \dosetmathfamily\mrfam\textface\scriptface\scriptscriptface\c!mr\mrfallback + \dosetmathfamily\mifam\textface\scriptface\scriptscriptface\c!mi\empty + \dosetmathfamily\syfam\textface\scriptface\scriptscriptface\c!sy\empty + \dosetmathfamily\exfam\textface\textface \textface \c!ex\empty + \dosetmathfamily\mafam\textface\scriptface\scriptscriptface\c!ma\empty + \dosetmathfamily\mbfam\textface\scriptface\scriptscriptface\c!mb\empty + \dosetmathfamily\mcfam\textface\scriptface\scriptscriptface\c!mc\empty +% \dosetmathfamily\mdfam\textface\scriptface\scriptscriptface\c!md\empty + \dosetmathfamily\nnfam\textface\scriptface\scriptscriptface\c!nn\empty +\to \mathstrategies + +\appendtoks + \dosetskewchar\mifam\defaultskewcharmi % implemented later on + \dosetskewchar\syfam\defaultskewcharsy % implemented later on +\to \mathstrategies + +\def\dosetmathfamily#1#2#3#4#5#6% + {\let\savedfontbody\fontbody % op hoger plan + \let\fontfamily#5% + \let\backfamily#6% + \let\fontbody #4\dodosetmathfamily\scriptscriptfont#1% + \let\fontbody #3\dodosetmathfamily \scriptfont#1% + \let\fontbody #2\dodosetmathfamily \textfont#1% + \let\fontbody\savedfontbody} + +\def\dodosetmathfamily + {\ifx\fontclass\empty + \@EA\dodosetmathfamilyA + \else + \@EA\dodosetmathfamilyB + \fi} + +\def\dodosetmathfamilyA#1#2% + {\ifcsname \fontbody\c!mm\fontfamily\fontsize\endcsname \autofontsizefalse + \csname \fontbody\c!mm\fontfamily\fontsize\endcsname \else + \ifcsname \fontbody\c!mm\fontfamily \endcsname \autofontsizetrue + \csname \fontbody\c!mm\fontfamily \endcsname \else + \ifcsname \fontbody \backfamily\fontsize\endcsname \autofontsizefalse + \csname \fontbody \backfamily\fontsize\endcsname \else + \ifcsname \fontbody \backfamily \endcsname \autofontsizetrue + \csname \fontbody \backfamily \endcsname \else + \nullfont \autofontsizetrue + \fi\fi\fi\fi + #1#2\font} + +\def\dodosetmathfamilyB#1#2% + {\ifcsname\fontclass\fontbody\c!mm\fontfamily\fontsize\endcsname \autofontsizefalse + \csname\fontclass\fontbody\c!mm\fontfamily\fontsize\endcsname \else + \ifcsname\fontclass\fontbody\c!mm\fontfamily \endcsname \autofontsizetrue + \csname\fontclass\fontbody\c!mm\fontfamily \endcsname \else + \ifcsname\fontclass\fontbody \backfamily\fontsize\endcsname \autofontsizefalse + \csname\fontclass\fontbody \backfamily\fontsize\endcsname \else + \ifcsname\fontclass\fontbody \backfamily \endcsname \autofontsizetrue + \csname\fontclass\fontbody \backfamily \endcsname \else + \dodosetmathfamilyA#1#2% + \fi\fi\fi\fi + #1#2\font} + +\appendtoks + \dosetsymbfamily\mrfam\textface\scriptface\scriptscriptface\c!mr + \dosetsymbfamily\mifam\textface\scriptface\scriptscriptface\c!mi + \dosetsymbfamily\syfam\textface\scriptface\scriptscriptface\c!sy + \dosetsymbfamily\exfam\textface\textface \textface \c!ex + \dosetsymbfamily\mafam\textface\scriptface\scriptscriptface\c!ma + \dosetsymbfamily\mbfam\textface\scriptface\scriptscriptface\c!mb + \dosetsymbfamily\mcfam\textface\scriptface\scriptscriptface\c!mc +% \dosetsymbfamily\mdfam\textface\scriptface\scriptscriptface\c!md % also ? +\to \symbstrategies + +\def\dosetsymbfamily#1#2#3#4#5% + {\let\savedfontbody\fontbody + \let\fontfamily#5% + \let\fontbody #4\dodosetsymbfamily\scriptscriptfont#1% + \let\fontbody #3\dodosetsymbfamily \scriptfont#1% + \let\fontbody #2\dodosetsymbfamily \textfont#1% + \let\fontbody\savedfontbody} + +\def\dodosetsymbfamily#1#2% + {\ifcsname\fontclass\fontbody\c!mm\fontfamily\fontalternative\fontsize\endcsname + \csname\fontclass\fontbody\c!mm\fontfamily\fontalternative\fontsize\endcsname + #1#2\font + \else\ifcsname\fontbody\c!mm\fontfamily\fontalternative\fontsize\endcsname + \csname\fontbody\c!mm\fontfamily\fontalternative\fontsize\endcsname + #1#2\font + \fi\fi} + +%D All used styles, like rm, ss and tt, are saved in a comma +%D separated list. Appart from practical limitations one can +%D define as many styles as needed. + +\def\fontrelativesizelist{\s!text,\s!script,\s!scriptscript,\c!x,\c!xx,\c!big,\c!small} + +%D \macros +%D {magfactor,magfactorhalf} +%D +%D There are several ways to specify a font. Three of them are +%D pure \TeX\ ones, the fourth one is new: +%D +%D \starttyping +%D \font\name=cmr12 +%D \font\name=cmr12 at 10pt +%D \font\name=cmr12 scaled \magstep2 +%D \font\name=cmr12 sa 1.440 +%D \stoptyping +%D +%D The non||\TEX\ alternative \type{sa} stands for {\em scaled +%D at}. This means as much as: scale the bodyfontsize with this +%D factor. The value 1.440 in this example is derived +%D from the \type{\magstep}'s as mentioned in +%D \in{table}[tab:magstep]. We therefore introduce +%D \type{\magfactor} as an alternative for \type{\magstep}. +%D +%D \placetable[here][tab:magstep] +%D {Factors to be used with \type{sa.}} +%D \starttable[|c|c|c|] +%D \HL +%D \NC \bf magstep \NC \bf equivalent \NC \bf factor \NC\SR +%D \HL +%D \NC 1 \NC \type{\magfactor1} \NC 1.200 \NC\FR +%D \NC 2 \NC \type{\magfactor2} \NC 1.440 \NC\MR +%D \NC 3 \NC \type{\magfactor3} \NC 1.728 \NC\MR +%D \NC 4 \NC \type{\magfactor4} \NC 2.074 \NC\MR +%D \NC 5 \NC \type{\magfactor5} \NC 2.488 \NC\LR +%D \HL +%D \stoptable + +\def\magstep#1% \relax removed, otherwise space after it sticks, else added + {\ifcase#1 1000\or1200\or1440\or1728\or2074\or2488\or1000\fi} + +\def\magstephalf + {1095} + +\def\magfactor#1% + {\ifcase#1 1.000\or1.200\or1.440\or1.728\or2.074\or2.488\or1\fi} + +\def\magfactorhalf + {1.095} + +%D These macros enable the use of definitions like \type{sa +%D \magfactor3} which saves us both (mis|)|calculations and +%D potential mistypings. +%D +%D Because \type {sa} (scaled at) and \type {mo} (mapped on) +%D are not low level \TEX\ supported alternatives, we have to +%D test for it ourselves. In doing so, we need an auxiliary +%D \DIMENSION. We cannot use \type{\scratchdimen} because font +%D loading can happen at any moment due to postponed loading. +%D We could instead have used dirty grouping tricks, but this +%D one works too. + +\let\defaultrelativefontsize \plusone +\let\localrelativefontsize \plusone +\def\localabsolutefontsize {\fontbody} + +\let\relativefontsize \defaultrelativefontsize + +\def\saverelativefontsize#1#2% #1=rm|ss|.. #2=waarde + {\setxvalue{\fontclass#1\s!rscale}{#2}} + +\def\checkrelativefontsize#1% + {\edef\relativefontsize + {\ifcsname\fontclass#1\s!rscale\endcsname + \csname\fontclass#1\s!rscale\endcsname + \else + \defaultrelativefontsize + \fi}} + +%D We also save: + +\def\savemathtextstyle#1% #1=rm|ss|.. + {\doifsomething{#1}{\setxvalue{\fontclass\c!mm\s!text}{#1}}} + +\def\currentmathtextstyle % we default to empty ! + {\executeifdefined{\fontclass\c!mm\s!text}\empty} + +%D Scaling macros: + +\newdimen\scaledfont + +\let\somefontspec\empty + +\def\@fs@{@fs@} + +% \def\parsefontspec#1% +% {\xdef\somefontspec{#1\space\relax}% +% \@EA\doparsefontspec\somefontspec]% =>#1 \relax] +% \donoparsefontspec} +% +% but, better: + +\let\somefontname\empty + +\def\parsefontspec#1% + {\xdef\somefontspec{#1\space}% + \@EA\doparsefontspec\somefontspec\relax]% =>#1 \relax] + \donoparsefontspec} + +\def\doparsefontspec#1 #2% + {\def\somefontname{#1}% + \ifx\somefontname\empty % space before fontname + \@EA\doparsefontspec + \else\ifx#2\relax + \@EAEAEA\setnaturalfontspec + \else + \@EAEAEA\setforcedfontspec + \fi\fi + #2} + +\def\setnaturalfontspec#1]% + {\@fs@scaled\plusthousand\relax} + +\def\setforcedfontspec#1 #2\relax]% + {\csname\@fs@#1\endcsname#2\relax} + +\def\@fs@scaled + {\afterassignment\do@fs@scaled\scratchcounter} + +\def\do@fs@scaled#1\relax + {\scaledfont\number\scratchcounter\points + \scaledfont\localrelativefontsize\scaledfont + \ifautofontsize\scaledfont\currentfontbodyscale\scaledfont\fi + \scratchcounter\scaledfont % \scaledfont is now pretty large + \advance\scratchcounter \medcard + \divide\scratchcounter \maxcard + \xdef\somefontspec{ scaled \the\scratchcounter}} + +\def\@fs@at + {\afterassignment\do@fs@at\scaledfont} + +\def\do@fs@at#1\relax + {\scaledfont\localrelativefontsize\scaledfont + \ifautofontsize\scaledfont\currentfontbodyscale\scaledfont\fi + \xdef\somefontspec{ at \the\scaledfont}} + +\def\@fs@sa + {\scaledfont\localabsolutefontsize + \setsamofontspec} + +\def\@fs@mo + {\scaledfont\setmappedfontsize\localabsolutefontsize + \setsamofontspec} + +\def\setsamofontspec#1\relax + {\checkfontscale#1\end\scaledfont + \scaledfont\localrelativefontsize\scaledfont + \ifautofontsize\scaledfont\currentfontbodyscale\scaledfont\fi + \xdef\somefontspec{ at \the\scaledfont}} + +\def\getfontparameters + {\expandafter\dogetfontparameter\@@fontdata,]=,} + +\def\getglobalfontparameters + {\expandafter\dogetglobalfontparameter\@@fontdata,]=,} + +\def\dogetfontparameter#1=#2,% + {\if]#1\else + \expandafter\def\csname\??ff\@@fontfile#1\endcsname{#2}% + \expandafter\dogetfontparameter + \fi} + +\def\dogetglobalfontparameter#1=#2,% + {\if]#1\else + \expandafter\gdef\csname\??ff\@@fontfile#1\endcsname{#2}% + \expandafter\dogetglobalfontparameter + \fi} + +\let\@@fontencoding\empty +\let\@@fontmapping \empty +\let\@@fonthandling\empty +\let\@@fontfeatures\empty +\let\@@skewchar \empty +\let\@@hyphenchar \empty % todo, will go to encoding + +%D This brings down maps processing from 466 to 309 seconds +%D ($-33$\%) and mfonts from 42 to 34 seconds ($-15$\%). + +\newif\ifskipfontcharacteristics \skipfontcharacteristicstrue + +%D When fontclasses are used, we define the font global, +%D since namespaces are used. Otherwise we parse the specs +%D each time. + +% wrong: this way we cannot set encoding etc +% +% \def\donoparsefontspec#1% +% {\edef\fontfile{\truefontname\somefontname}% +% \ifx\fontfile\s!unknown \let\fontfile\defaultfontfile \fi +% \edef\lastfontname{\fontfile\somefontspec}% +% \ifx\fontclass\empty\else\global\fi +% \expandafter\font\csname#1\endcsname\lastfontname\relax +% \relax} + +\let\fontfile\s!unknown + +\ifx\checkfontfilename\undefined \let\checkfontfilename\relax \fi + +% \definefontsynonym +% [blabla] +% [name:Latin Modern Something] +% \definefontsynonym +% [blabla] +% [file:texnansi-lmr10] +% [encoding=texnansi] +% \definedfont[blabla] test \currentencoding/\fontfile \par +% \definefontsynonym +% [blabla] +% [texnansi-lmtt10] +% [encoding=texnansi] +% \definedfont[blabla] test \currentencoding/\fontfile \par +% \definefontsynonym +% [blabla] +% [ec-lmtt10] +% [encoding=ec] +% \definedfont[blabla] test \currentencoding/\fontfile \par + +\def\checkfontfilename + {\expandafter\docheckfontfilename\fontfile:\empty:\empty\relax} + +\def\docheckfontfilename#1:#2:#3#4\relax + {\edef\!!stringa{#1}% + \edef\!!stringb{#2}% + \ifx\!!stringb\empty + \edef\checkedfontfile{\!!stringa}% + \else\ifx\!!stringa\v!file + \edef\checkedfontfile{"\!!stringb"}% + \else\ifx\!!stringa\v!name + \edef\checkedfontfile{"\!!stringb"}% + \else + \edef\checkedfontfile{\!!stringb}% + \fi\fi\fi} + +% \definefontfeature[default] [liga=yes,texligatures=yes,texquotes=yes] +% \definefontfeature[default-caps][liga=yes,texligatures=yes,texquotes=yes,smcp=yes,script=latn] +% +% \starttypescript [serif] [palatino-nova-regular] [name] +% \definefontsynonym[Serif] [palatinonova-regular][features=default] +% \definefontsynonym[SerifCaps][palatinonova-regular][features=default-caps] % also sets Serif +% \stoptypescript +% +% \starttypescript [serif] [palatino-nova-regular] [name] +% \definefontsynonym[Serif] [palatinonova-regular*default] +% \definefontsynonym[SerifCaps] [palatinonova-regular*default-caps] +% \stoptypescript + +% \definetypeface[mainface][rm][serif][palatino-nova-regular][default] \setupbodyfont[mainface] +% +% \starttext +% ``Test'' -- --- ff fi fl \sc ``Test'' -- --- ff fi fl +% \stoptext + +% \starttext +% \definefont +% [blabla] +% [name:Latin Modern Something] +% \definefont +% [blabla] +% [file:texnansi-lmr10] +% \blabla test +% \definefont +% [blabla] +% [texnansi-lmtt10] +% \blabla test +% \stoptext + +% \starttext +% +% \setupcolors[state=start] +% +% \definefontfeature +% [default-base] +% [script=latn,language=dflt,liga=yes,kern=yes,tlig=yes,trep=yes] +% \definefontfeature +% [default-node] +% [script=latn,language=dflt,liga=yes,kern=yes,tlig=yes,trep=yes,mode=node] +% \definefontfeature +% [default-none] +% [script=latn,language=dflt,liga=yes,kern=no, tlig=yes,trep=yes] +% +% \startoverlay +% {\vtop{\color[red] {\font\test=name:lmroman12regular*default-node \test \input ward \input zapf \input linden }}} +% {\vtop{\color[green]{\font\test=name:lmroman12regular*default-base \test \input ward \input zapf \input linden }}} +% {\vtop{\color[blue] {\font\test=name:lmroman12regular*default-none \test \input ward \input zapf \input linden }}} +% \stopoverlay +% +% \blank +% +% \startoverlay +% {\vtop{\color[red] {\font\test=name:texgyrepagella*default-node \test \input ward \input zapf \input linden }}} +% {\vtop{\color[green]{\font\test=name:texgyrepagella*default-base \test \input ward \input zapf \input linden }}} +% {\vtop{\color[blue] {\font\test=name:texgyrepagella*default-none \test \input ward \input zapf \input linden }}} +% \stopoverlay +% +% \blank +% +% \startoverlay +% {\vtop{\color[red] {\font\test=name:palatinonovaregular*default-node \test \input ward \input zapf \input linden }}} +% {\vtop{\color[green]{\font\test=name:palatinonovaregular*default-base \test \input ward \input zapf \input linden }}} +% {\vtop{\color[blue] {\font\test=name:palatinonovaregular*default-none \test \input ward \input zapf \input linden }}} +% \stopoverlay +% +% \startoverlay +% {\vtop{\color[red] {\font\test=name:OfficinaSerifBookITC*default-node \test \input ward \input zapf \input linden }}} +% {\vtop{\color[green]{\font\test=name:OfficinaSerifBookITC*default-base \test \input ward \input zapf \input linden }}} +% {\vtop{\color[blue] {\font\test=name:OfficinaSerifBookITC*default-none \test \input ward \input zapf \input linden }}} +% \stopoverlay +% +% \definefontfeature[superdefault][default][compose=yes] +% +% {\font\oeps=name:lmroman10regular*default at 30pt \oeps test \char7683} +% {\font\oeps=name:lmroman10regular*superdefault at 30pt \oeps test \char7683} +% +% \stoptext + +\let\doshowcheckedfontfeatures\relax + +\def\showcheckedfontfeatures + {\def\doshowcheckedfontfeatures{\writestatus\m!fonts{checked: \checkedfontfile}}} + +\def\checkedfontfile{\fontfile} % default + +\newtoks\everydefinefont + +\def\donoparsefontspec % #1 == \cs + {\edef\fontfile{\truefontname\somefontname}% + \ifx\fontfile\s!unknown \let\fontfile\defaultfontfile \fi % can for instance happen with MathGamma + \updatefontparameters + \checkfontfilename + \edef\lastfontname{\checkedfontfile\somefontspec}% + \ifx\fontclass\empty + \expandafter\definefontlocal + \else + \expandafter\definefontglobal + \fi} % #1 == \cs + +\def\definefontlocal#1% + {\expandafter\edef\csname#1\endcsname % ! ! ! ! not needed in luatex ! ! ! ! + {\noexpand\csname#1:\endcsname + \noexpand\reactivatefont{\somefontname}{\fontfile}}% + \expandafter\font\csname#1:\endcsname\lastfontname\relax + \expandafter\let\expandafter\lastrawfontcall\csname#1:\endcsname + \the\everydefinefont} + +\def\definefontglobal#1% stores \somefontname=Mono and \fontfile=cmtt10 + {\expandafter\xdef\csname#1\endcsname % ! ! ! ! not needed in luatex ! ! ! ! + {\noexpand\csname#1:\endcsname + \noexpand\reactivatefont{\somefontname}{\fontfile}}% + \global\expandafter\font\csname#1:\endcsname\lastfontname\relax + \expandafter\let\expandafter\lastrawfontcall\csname#1:\endcsname + \the\everydefinefont} + +\def\reactivatefont#1#2% + {\def\somefontname{#1}% + \def\fontfile {#2}% + \updatefontparameters} + +% can be handy for tracing purposes +% +% \def\reportfontdefinition +% {\bgroup +% \expanded{\infofont +% [\lastfontidentifier +% ->\newfontidentifier +% ->\fontname\csname\newfontidentifier\endcsname]}% +% \endgraf +% \egroup} + +%D An additional the second \type {\font} definition can +%D prevent fuzzy font refs +%D +%D \starttyping +%D \expandafter\font\csname\dummyfontname\endcsname\lastfontname +%D \stoptyping +%D +%D but somehow they changed \TEX\ so that it does not matter +%D any more. + +\def \defaultfontfile{\truefontname{Normal}} % was cmtt10, but that one is gone +\edef\nullfontname {\fontname\nullfont} +\edef\dummyfontname {font\strippedcsname\\} + +%D \macros +%D {everyfont,everyfontswitch} +%D +%D For special purposes, we provide a hook. + +% \newevery \everyfont \relax +% \newevery \everyfontswitch \relax + +% safer but sometimes introducing newlines in the log +% +% \batchmode +% \font\lastloadedfont\fontfile\somefontspec\relax +% \errorstopmode +% \edef\lastfontname{\fontname\lastloadedfont}% +% \ifx\lastfontname\nullfontname +% \showmessage\m!fonts{10}{\fontfile}% +% \expandafter\font\csname#1\endcsname=\defaultfontfile\somefontspec\relax +% \else +% \expandafter\let\csname#1\endcsname\lastloadedfont +% \fi + +%D We also accept \type{sa a}||\type{sa d} as specification. + +\def\checkfontscale#1#2\end#3% + {#3\ifcsname\??ft\s!default\noexpand#1\endcsname + \csname\??ft\s!default\noexpand#1\endcsname\else#1#2% + \fi#3} + +%D The duplicate font definition, using the ever the same dummy +%D font name, results in less fuzzy error messages. In the log +%D file, for instance when overfull boxes are reported, the +%D simple keyword `font' replaces the \TEX\ ordinated name. The +%D latter can be too misleading, due to the fact that \TEX\ has +%D a rather optimized font memory management. Thanks to Taco +%D for helping me sort this out. + +%D \macros +%D {definefontsynonym, doifelsefontsynonym, +%D expandfontsynonym, truefontname, truefontdata} +%D +%D While looking for fonts, fontsynonyms are used for accessing +%D the files! +%D +%D \starttyping +%D \definefontsynonym[Serif][Lucida-Bright] +%D \definefontsynonym[Lucida-Bright][lbr][encoding=texnansi] +%D \stoptyping +%D +%D The definitions can be accessed by: +%D +%D \startlines +%D name: \type {\truefontname{Lucida-Bright}} +%D data: \type {\truefontdata{\truefontname{Lucida-Bright}}} +%D \stoplines + +% \def\definefontsynonym +% {\dotripleempty\dodefinefontsynonym} +% +% \def\dodefinefontsynonym[#1][#2][#3]% +% {\@EA\edef\csname\??ff\fontclass#1\endcsname{#2}% +% \ifthirdargument +% \edef\@@fontdata{#3}% +% \ifx\@@fontdata\empty \else +% \edef\@@fontfile{#2}% +% \ifx\fontclass\empty +% \getfontparameters +% \else +% \getglobalfontparameters +% \fi +% \fi +% \fi} +% +% slightly faster, noticable when loading many typefaces, +% +% \testfeatureonce{5000}{\definefontsynonym[somefont][somename]} \end + +\def\classfont#1#2{#1#2} % \definefont[whatever][\classfont{xx}{yy} at 10pt] + +\def\definefontsynonym[#1]#2[#3]% + {\edef\@@fontfile{#3}% + \@EA\let\csname\??ff\fontclass#1\endcsname\@@fontfile + \doifnextoptionalelse\dodefinefontsynonym\donothing} + +\def\dodefinefontsynonym[#1]% + {\edef\@@fontdata{#1}% + \ifx\@@fontdata\empty \else \ifx\fontclass\empty + \getfontparameters + \else + \getglobalfontparameters + \fi \fi} + +\let\definefontfile\definefontsynonym % dedicated to Taco Hoekwater + +\def\setupfontsynonym + {\dodoubleempty\dosetupfontsynonym} + +\def\dosetupfontsynonym[#1][#2]% + {\edef\@@fontdata{#2}% + \ifx\@@fontdata\empty \else + \edef\@@fontfile{#1}% + \ifx\fontclass\empty + \getfontparameters + \else + \getglobalfontparameters + \fi + \fi} + +\def\truefontdata#1#2% + {\ifcsname\??ff#1#2\endcsname + % raw(Regular) raw(key) + \csname\??ff#1#2\endcsname + \else\ifcsname\??ff\fontclass#1\endcsname + % exp(palatino Regular) raw(key) + \expandafter\truefontdata\csname\??ff\fontclass#1\endcsname#2% + \else\ifcsname\??ff#1\endcsname + % exp(Regular) raw(key) + \expandafter\truefontdata\csname\??ff#1\endcsname#2% + \else\ifcsname\??ff#2\endcsname + % raw(key) + \csname\??ff#2\endcsname + \fi\fi\fi\fi} + +\def\truefontname#1% + {\ifcsname\??ff\fontclass#1\endcsname + \@EA\truefontname\csname\??ff\fontclass#1\endcsname + \else\ifcsname\??ff#1\endcsname + \@EA\truefontname\csname\??ff#1\endcsname + \else + #1% + \fi\fi} + +\def\expandfontsynonym#1#2% #2 := onelevelexpansion(#1) + {\ifcsname\??ff\fontclass#2\endcsname + \expandafter\def\expandafter#1\expandafter{\csname\??ff\fontclass#2\endcsname}% + \fi} + +\def\doifelsefontsynonym#1% + {\ifcsname\??ff\fontclass#1\endcsname + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +% \definetypeface[palatino][rm][serif][palatino,allbold][default] +% +% \startfontclass[palatino] +% \definefontsynonym [Serif] [SerifBold] +% \definefontsynonym [SerifItalic] [SerifBoldItalic] +% \definefontsynonym [SerifSlanted] [SerifBoldSlanted] +% \definefontsynonym [SerifCaps] [SerifBold] +% \stopfontclass +% +% \setupbodyfont[palatino] + +\def\startfontclass + {\dosingleempty\dostartfontclass} + +\def\dostartfontclass[#1]% + {\pushmacro\fontclass + \doifelse{#1}{\v!each} + {\let\fontclass\empty} + {\doifsomething{#1}{\def\fontclass{#1}}}} + +\def\stopfontclass + {\popmacro\fontclass} + +%D \macros +%D {tracedfontname} +%D +%D A goody: + +\def\tracedfontencoding#1% + {\ifcsname\??ff#1\s!encoding\endcsname + \space[\csname\??ff#1\s!encoding\endcsname]% + \fi} + +\def\tracedfontname#1% + {\ifcsname\??ff\fontclass#1\endcsname + #1\tracedfontencoding{\fontclass#1}\space->\space + \@EA\tracedfontname\csname\??ff\fontclass#1\endcsname + \else\ifcsname\??ff#1\endcsname + #1\tracedfontencoding{#1}\space->\space + \@EA\tracedfontname\csname\??ff#1\endcsname + \else + #1% + \fi\fi} + +%D \macros +%D {getfontfileparameters} +%D +%D For special purposes, one can use the next macro to +%D access font file characteristics, for instance: +%D +%D \starttyping +%D \getfontfileparameters{Regular} +%D \stoptyping +%D +%D can result in: +%D +%D \starttyping +%D \def\currentfontfileencoding{texnansi} +%D \stoptyping + +% \def\@@currentfontfile{currentfontfile} +% +% \def\getfontfileparameters#1% +% {\edef\fontdata{\truefontdata{\truefontname{#1}}}% +% \expanded{\getparameters[\@@currentfontfile][\fontdata]}} + +\def\getfontfileparameters#1% can be simpler for mkii + {\edef\@@truefontname{\truefontname{#1}}% + \edef\currentfontfileencoding{\truefontdata\@@truefontname\s!encoding}% + \edef\currentfontfilemapping {\truefontdata\@@truefontname\s!mapping }% + \edef\currentfontfilehandling{\truefontdata\@@truefontname\s!handling}% + \edef\currentfontfilefeatures{\truefontdata\@@truefontname\s!features}} + +%D \macros +%D {definefont} +%D +%D Before we implement the main definition macro, we first show +%D one for local use: +%D +%D \starttyping +%D \definefont[Some][LucidaBright at 100pt] \Some some +%D \definefont[More][LucidaBright scaled 3000] \More more +%D \definefont[Nice][LucidaBright mp 2] \Nice nice +%D \definefont[Text][LucidaBright sa 5.4] \Text last +%D \stoptyping +%D +%D The implementation looks as follows: + +\def\definefont + {\dotripleempty\dodefinefont} + +\def\dodefinefont[#1][#2][#3]% [name][spec][1.6 | line=10pt | setup_id] + {\doifinstringelse{ }{#2} + {\ifthirdargument + \unexpanded\setvalue{#1}{\redodefinefont{#1}{#2}{#3}}% + \else + \unexpanded\setvalue{#1}{\dododefinefont{#1}{#2}}% + \fi} + {\definefont[#1][#2 sa *][#3]}} + +\def\redodefinefont#1#2#3% + {\dododefinefont{#1}{#2}% + \doifsetupselse{#3} + {\setups[#3]} % don't forget to document this ! + {\setuplocalinterlinespace[#3]% + \setupspacing}} % needed ? + +\def\definefrozenfont + {\dotripleempty\dodefinefrozenfont} + +\def\dodefinefrozenfont[#1][#2][#3]% + {\dodefinefont[#1][#2][#3]% + \expandafter\let\csname\lastfontidentifier\expandafter\endcsname\csname\rawfontidentifier\endcsname} + +% \def\defineclassfont +% {\doquadrupleempty\dodefineclassfont} +% +% \def\dodefineclassfont[#1][#2][#3][#4]% #2 = class +% {\iffourthargument +% \definefont[#1][#2#3][#4]% +% %\else\ifthirdargument +% % \definefont[#1][#2#3]% +% \else +% \definefont[#1][#2]% +% \fi} + +%D The \type {*} makes the switch local, so that we can redefine a +%D logical name and/or change the size in between. + +\newif\ifautofontsize \autofontsizetrue + +\let\lastfontidentifier\empty + +\def\rawfontidentifier{**\lastfontidentifier\fontsize**} +\def\newfontidentifier{*\fontclass\lastfontidentifier\fontsize*} + +% best we can add the style as well because otherwise we get math encoding mixups +% +% \setupbodyfont [8pt] [\currentencoding/\ccaron\scaron\zcaron] +% \blank +% \switchtobodyfont[7pt] [\currentencoding/\ccaron\scaron\zcaron] + +\def\newfontidentifier{*\fontclass\lastfontidentifier\fontstyle\fontsize*} + +\def\dododefinefont#1#2% + {\edef\lastfontidentifier{#1}% + \let\localrelativefontsize\defaultrelativefontsize + \let\localabsolutefontsize\fontbody + \parsefontspec{#2}\rawfontidentifier + \let\localrelativefontsize\defaultrelativefontsize % not needed + \csname\rawfontidentifier\endcsname + \autofontsizefalse + \setfontcharacteristics + \the\everyfontswitch} + +\def\xxdododefinefont#1#2#3#4% \autofontsizetrue is set by calling routine + {\edef\lastfontidentifier{#3}% + \ifcsname\newfontidentifier\endcsname\else + \def\localrelativefontsize{#1}% + \def\localabsolutefontsize{#2}% + \parsefontspec{#4}\newfontidentifier + \let\localrelativefontsize\defaultrelativefontsize % not needed + \fi + \csname\newfontidentifier\endcsname + \autofontsizefalse + %\edef\lastfontidentifier{#3}% + \ifskipfontcharacteristics \else + \setfontcharacteristics + \the\everyfontswitch + \fi} + +%D I considered checking for mistakenly use of \PLAIN's +%D \type{\magstep}'s but although it would take only a few +%D lines of code, this would not add to consistent use. I +%D therefore removed this check. + +%D \macros +%D {mapfontsize} +%D +%D For special purposes, like in math, you may want to use +%D slightly different sizes than the ones given. This happens +%D for instance with the Math Times fonts. Mapped font sizes +%D can be specified by using the \type {mo} key instead of +%D \type {sa} in font definitions. +%D +%D \startbuffer +%D \mapfontsize[10pt][11pt] +%D \mapfontsize[11pt][12pt] +%D \mapfontsize[12pt][13pt] +%D +%D \definefont[test][Serif]\test TEST \par +%D \definefont[test][Serif sa 5]\test TEST \par +%D \definefont[test][Serif mo 5]\test TEST \par +%D \definefont[test][Serif sa d]\test TEST \par +%D \definefont[test][Serif at 60pt]\test TEST \par +%D \definefont[test][Serif scaled 6000]\test TEST \par +%D \stopbuffer +%D +%D \typebuffer +%D +%D \startpacked +%D \getbuffer +%D \stoppacked + +\def\mapfontsize + {\dodoubleargument\domapfontsize} + +\def\domapfontsize[#1][#2]% + {\scratchdimen#1\relax % \relax is really needed here + \setvalue{\??ft*\the\scratchdimen}{#2}} + +\def\setmappedfontsize#1% + {\ifcsname\??ft*#1\endcsname + \csname\??ft*#1\endcsname + \else + #1% + \fi} + +%D \macros +%D {getfontname} +%D +%D The names of the fonts can be called with the rather simple +%D macro \type{\getfontname}. When for instance we pass +%D \type{12ptrmtf} as argument, we get \getfontname{12ptrmtf}. + +\def\getfontname#1% + {\csname\??ft#1\endcsname} + +%D To be documented. + +\let\fontsizelist\empty +\let\fontstylelist\empty + +\def\definefontsize[#1]% sneller met toks + {\addtocommalist{#1}\fontsizelist + \def\docommand##1% + {\def\dodocommand####1% + {\def\dododocommand########1% + %{\checkbodyfont{}{########1}{####1}{##1}}% + {\checkbodyfont{########1}{####1}{##1}}% + \processcommacommand[\fontstylelist]\dododocommand}% + \processcommacommand[\fontalternativelist]\dodocommand}% + \processcommacommand[\fontsizelist]\docommand} + +\def\fontalternativetextlist{\c!tf,\c!bf,\c!it,\c!sl,\c!bs,\c!bi,\c!sc} +\def\fontalternativemathlist{\c!mr,\c!mi,\c!sy,\c!ex,\c!ma,\c!mb} + +\let\fontalternativelist\fontalternativetextlist % upward compatible + +%D \macros +%D {currentfontscale,currentfontbodyscale} +%D +%D Sometimes we need to have access to the font scale +%D including the \type{a}||\type{d} sizes. The next macro +%D returns the current scaling factor. Take a look at +%D \type {cont-log.tex} for an example of its use. + +\def\currentfontscale + {\csname\??ft\s!default + \ifcsname\??ft\s!default\xfontsize\endcsname \xfontsize \else + \ifcsname\??ft\s!default\s!text \endcsname \s!text \fi\fi + \endcsname} + +\def\currentfontbodyscale + {\csname\??ft\s!default + \ifcsname\??ft\s!default\fontsize\endcsname \fontsize \else + \ifcsname\??ft\s!default\s!text \endcsname \s!text \fi\fi + \endcsname} + +\setvalue{\??ft\s!default}{1} + +%D Both alternatives use \type {\xfontsize}, a macro that +%D expands to the current size in terms of \type {a} \unknown +%D \type {d}, nothing, or \type {x} \unknown \type {xx}. + +\def\xfontsize{\ifcase\currentxfontsize\fontsize\or\c!x\else\c!xx\fi} + +%D A typical use of this command is in commands that switch +%D to another font bypassing the font mechanism: +%D +%D \starttyping +%D \font\myfont=\truefontname{MyFont} at \currentfontscale\bodyfontsize +%D \stoptyping + +%D Now we enter the area of font switching. The switching +%D mechanism has to take care of several situations, like: +%D +%D \startitemize[packed] +%D \item changing the overal document fonts (including margins, +%D headers and footers) +%D \item changing local fonts (only the running text) +%D \item smaller and even more smaller alternatives (super- +%D and subscripts) +%D \stopitemize +%D +%D \TEX\ offers a powerfull family mechanism for super- and +%D subscripts in math mode. In text mode however, we don't use +%D families for the smaller alternatives, and therefore have +%D to take care of it otherwise. + +%D \macros +%D {definebodyfontenvironment,setupbodyfontenvironment} +%D +%D The relationship between the several sizes of a font, is +%D defined by: +%D +%D \showsetup{definebodyfontenvironment} +%D +%D Later on we will see how these parameters are used, so for +%D the moment we stick with an example: +%D +%D \starttyping +%D \definebodyfontenvironment +%D [12pt] +%D [ text=12pt, +%D script=9pt, +%D scriptscript=7pt, +%D x=10pt, +%D xx=8pt, +%D big=12pt, +%D small=10pt] +%D \stoptyping +%D +%D The first argument specifies the bodyfont size to which the +%D settings apply. All second parameters are specified in +%D dimensions and tell us more about related sizes. +%D +%D Afterwards, one can change values with +%D +%D \showsetup{setupbodyfontenvironment} +%D +%D Due to the fact that \type{\c!text} and \type{\s!text} can +%D have a similar meaning, and therefore can lead to an +%D unwanted loop, we temporary redefine \type{\c!text}. For +%D the moment this in only place that some trickery is needed +%D to fool the multilingual interface. +%D +%D When instead of a size the keyword \type{unknown} is +%D passed, fractions (relations) are used instead of fixed +%D sizes. + +\let\bodyfontenvironmentlist\empty + +\newcount\@@fontdefhack + +\def\@@beginfontdef + {\ifcase\@@fontdefhack + \let\k!savedtext \k!text \let\k!text \s!text + \let\k!k!savedtext \k!k!text \let\k!k!text \!!plusone + \let\k!saveddefault \k!default \let\k!default \s!default + \let\k!k!saveddefault\k!k!default \let\k!k!default \!!plusone + \fi + \advance\@@fontdefhack \plusone } + +\def\@@endfontdef + {\advance\@@fontdefhack \minusone + \ifcase\@@fontdefhack + \let\k!k!default\k!k!saveddefault + \let\k!default \k!saveddefault + \let\k!k!text \k!k!savedtext + \let\k!text \k!savedtext + \fi} + +\def\definebodyfontenvironment + {\dotripleempty\dodefinebodyfontenvironment} + +\def\dodefinebodyfontenvironment[#1][#2][#3]% class size settings + {\ifthirdargument + \@@beginfontdef + \doifelse{#2}\s!default + {\getparameters[\??ft\s!default][#3]} + {\normalizebodyfontsize#2\to\tempbodyfontsize + \addtocommalist\tempbodyfontsize\bodyfontenvironmentlist + \@EA\dododefinebodyfontenvironment\@EA[\tempbodyfontsize][#1][#3]}% + \@@endfontdef + \else + \definebodyfontenvironment[\fontclass][#1][#2]% change */* + \fi} + +\def\dododefinebodyfontenvironment[#1][#2][#3]% size class settings + {\@@beginfontdef + \doifundefined{\??ft#2#1\c!em} % \s!text goes wrong in testing because + {\def\docommand##1% % the 12pt alternative will called when + {\scratchdimen#1\relax % typesetting the test (or so) + \scratchdimen\csname\??ft\s!default##1\endcsname\scratchdimen + \normalizebodyfontsize\scratchdimen\to\tempbodyfontsize + \setevalue{\??ft#2#1##1}{\tempbodyfontsize}}% + \processcommacommand[\fontrelativesizelist]\docommand + \copyparameters + [\??ft#2#1][\??ft\s!default] + [\c!interlinespace,\c!em]}% + \getparameters[\??ft#2#1][#3]% + \@@endfontdef + % new code, see remark + \ifloadingfonts \else % only runtime + \doifundefined{\@size@#1} % only once + {\letvalue{\@size@#1}\empty % prevent loop + \defineunknownfont{#1}}% % safeguard + \fi + % so far + \setvalue{\@size@#1}{\docompletefontswitch[#1]}} + +%D {\bf Remark:} We need to cover the following cases, +%D otherwise users can get confused: +%D +%D \starttyping +%D \setupbodyfont[23pt] +%D +%D \definebodyfontenvironment[23pt] +%D \setupbodyfont[23pt] +%D +%D \definebodyfontenvironment[23pt] +%D \definebodyfont[23pt][rm,ss,tt][default] +%D \setupbodyfont[23pt] +%D \stoptyping + +%D Beware: while some font defs can be global, the bodyfont +%D environment checks local. This means that multiple local +%D checks resulting in definitions are not that efficient. +%D So, apart from an occasional switch, one should define an +%D environment at the outer level. + +\def\checkbodyfontenvironment[#1]% + {\definebodyfontenvironment[\fontclass][#1][]} + +% original +% +% \def\setupbodyfontenvironment +% {\dotripleempty\dosetupbodyfontenvironment} +% +% \def\dosetupbodyfontenvironment[#1][#2][#3]% class size settings +% {\@@beginfontdef +% \ifthirdargument +% \getparameters[\??ft#1#2][#3]% +% \else +% \getparameters[\??ft#1][#2]% +% \fi +% \@@endfontdef} +% +% this one already catches both define/setup + +\def\setupbodyfontenvironment{\definebodyfontenvironment} + +% officially, but not needed (yet): +% +% \def\dosetupbodyfontenvironment[#1][#2][#3]% class size settings +% {\ifthirdargument +% \localbodyfontsize#2\relax +% \normalizebodyfontsize\localbodyfontsize\to\normalizedbodyfontsize +% \doifundefinedelse{\??ft#1\normalizedbodyfontsize\c!em} +% {\definebodyfontenvironment[#1][#2][#3]}% +% {\getparameters[\??ft#1\normalizedbodyfontsize][#3]}% +% \else +% \localbodyfontsize#1\relax +% \normalizebodyfontsize\localbodyfontsize\to\normalizedbodyfontsize +% \doifundefinedelse{\??ft\normalizedbodyfontsize\c!em} +% {\definebodyfontenvironment[#1][#2]}% +% {\getparameters[\??ft\normalizedbodyfontsize][#2]}% +% \fi} + +%D Just a couple of interface macros: + +\def\bodyfontvariable#1% + {\??ft\ifcsname\??ft\fontclass#1\endcsname\fontclass\fi#1} + +\def\bodyfontcsname + {\csname\bodyfontvariable} + +\def\bodyfontinterlinespecs + {\bodyfontvariable{\normalizedbodyfontsize\c!interlinespace}} + +\def\bodyfontinterlinespace + {\csname\bodyfontinterlinespecs\endcsname} + +%D We default all parameters to the main bodyfont size (begin +%D \type{#1}), so the next setup is valid too: +%D +%D \starttyping +%D \definebodyfontenvironment[24pt] +%D \stoptyping +%D +%D All parameters can be redefined when needed, so one does +%D not have to stick to the default ones. + +%D \macros +%D {definebodyfont} +%D +%D The next step in defining a bodyfont involves the actual font +%D files, which can be recognized by their extension +%D \type{tfm}. Installing those file is often beyond the +%D scope of the user and up to the system administrator. +%D +%D \showsetup{definebodyfont} +%D +%D This commands takes three arguments: a (series of) bodyfont +%D size(s), the style group to which the definitions belong, +%D and an alternative, as specified by the \TEX\ (math) families, +%D extended with~a, b~\unknown. +%D +%D We show two examples, that show all the alternative +%D scaling options. The \type{\tfa} alternatives can be +%D extended with \type{\bfa}, \type{\slb}, etc. or even +%D \type{e} and higher alternatives. +%D +%D \starttyping +%D \definebodyfont [12pt] [rm] +%D [tf=cmr12, +%D bf=cmbx12, +%D it=cmti12, +%D sl=cmsl12, +%D bi=cmbxti10 at 12pt, +%D bs=cmbxsl10 at 12pt, +%D tfa=cmr12 scaled \magstep1, +%D tfb=cmr12 scaled \magstep2, +%D tfc=cmr12 scaled \magstep3, +%D tfd=cmr12 scaled \magstep4, +%D sc=cmcsc10 at 12pt] +%D +%D \definebodyfont [12pt,11pt,10pt,9pt,8pt] [rm] +%D [tf=lbr sa 1, +%D bf=lbd sa 1, +%D it=lbi sa 1, +%D sl=lbsl sa 1, +%D bi=lbdi sa 1, +%D bs=lbdi sa 1, +%D tfa=lbr sa 1.200, +%D tfb=lbr sa 1.440, +%D tfc=lbr sa 1.728, +%D tfd=lbr sa 2.074, +%D sc=lbr sa 0.833] +%D \stoptyping +%D +%D The second example shows that we can define more sizes at +%D once. The main difference between these examples is that the +%D Computer Modern Roman come in many design sizes. This means +%D that there we cannot define them in bulk using \type{sa}. +%D Instead of \type{rm} (roman) one can define \type{ss} (sans +%D serif), \type{tt} (teletype), \type{hw} (hand written), +%D \type{cg} (calygraphic) and whatever styles. +%D +%D The first argument may be a comma separated list. This, +%D combined with specifications using \type{sa} can save a lot +%D of typing. Although all arguments should be specified, we +%D treat the second argument as optional. +%D +%D Defining a bodyfont involves two actions: defining the +%D specific style related alternatives, like \type{\rma}, +%D \type{\bfa} and \type{\rmsla}, and storing the definitions +%D of their bodyfont size related fonts. The first step is +%D bodyfont independant but executed every time. This permits +%D user definitions like \type{\tfw} or \type{\bfq} for real +%D large alternatives. + +\def\definebodyfont + {\doquadrupleempty\redefinebodyfont} + +\def\redefinebodyfont[#1][#2][#3][#4]% + {\iffourthargument + \processcommacommand[#1]{\reredefinebodyfont[#2][#3][#4]}% + \else + \dodefinebodyfont[#1][#2][#3]% + \fi} + +\def\reredefinebodyfont[#1][#2][#3]#4% + {\pushmacro\fontclass + \doifelse{#4}\s!default + {\let\fontclass\empty} + {\def\fontclass{#4}}% + \definebodyfont[#1][#2][#3]% + \popmacro\fontclass} + +\def\dodefinebodyfont[#1][#2][#3]% body|identifier style defs|identifier + {\ifthirdargument + \doifnumberelse{#1} + {\doifassignmentelse{#3} + {% [12pt] [style] [settings] + \doifundefined{#2}{\expanded{\definefontstyle[#2][#2]}}% new + \processcommalist[#1]{\dododefinebodyfont{#2}{#3}}} + {% [12pt] [style] [identifier] + \dodefinedefaultbodyfont[#1][#2][#3]}} % body style identifier + {% [identifier] [style] [settings] % see *** + \setvalue{\s!default#1#2}##1##2{\expanded{\xdodefinebodyfont[##1][##2][#3]}}}% + \else\ifsecondargument + \definebodyfont[#1][\c!rm][#2]% + \else + \doifundefined{\@size@#1} % Maybe there are default dependancies + {\defineunknownfont{#1}}% defined which we can use ([unknown]) + \doifundefined{\@size@#1} % and if not, then we have at least to + {\definebodyfont[#1][\c!rm][]}% make sure some basics are set up. + \fi\fi} + +\def\xdodefinebodyfont[#1][#2][#3]% body|identifier style defs|identifier + {%\writestatus{[#1]}{[#2][#3]}% + \checkrelativefontsize{#2}% rather new, inherit from other defs + \ifundefined{#2}\expanded{\definefontstyle[#2][#2]}\fi % new + \processcommalist[#1]{\dododefinebodyfont{#2}{#3}}% + \let\relativefontsize\defaultrelativefontsize} + +\def\dododefinebodyfont#1#2#3% style defs body + {\checkbodyfontenvironment[#3]% just to be sure. + \processcommalist[#2]{\dodododefinebodyfont{#1}{#3}}} + +\def\dodododefinebodyfont#1#2#3% style body def + {\dododododefinebodyfont{#1}{#2}[#3]} + +\newif \ifresolvefontfile % protected by fontclass anyway + +\def\resolvefontname#1 {\truefontname{#1} } + +\def\iflocalclassfonts{\ifx\fontclass\empty} + +\def\dododododefinebodyfont#1#2[#3#4#5=#6]% style body def + {\ifundefined{#1#3#4#5}% + %\checkbodyfont{#2}{#1}{#3#4}{#5}% not \definefontsize[#5] + \checkbodyfont{#1}{#3#4}{#5}% not \definefontsize[#5] + \fi + \iflocalclassfonts + \letbeundefined{*\fontclass#2#1#3#4#5*}% + \scratchtoks{#6}% + \expanded{\unexpanded\noexpand\setvalue{#2#1#3#4#5}% + {\noexpand\xxdododefinefont{\relativefontsize}{#2}% + {#2#1#3#4#5}{\the\scratchtoks}}}% + \else + %\expanded{\writestatus{defining}{[\fontclass][#2#1#3#4#5] \resolvefontname#6 }}% + \global\letbeundefined{*\fontclass#2#1#3#4#5*}% + \ifresolvefontfile + \unexpanded\setxvalue{\fontclass#2#1#3#4#5}% + {\noexpand\xxdododefinefont{\relativefontsize}{#2}% + {#2#1#3#4#5}{\resolvefontname#6 }}% + \else + \scratchtoks{#6}% + \expanded{\unexpanded\noexpand\setgvalue{\fontclass#2#1#3#4#5}% + {\noexpand\xxdododefinefont{\relativefontsize}{#2}% + {#2#1#3#4#5}{\the\scratchtoks}}}% + \fi + \fi} + +% \def\checkbodyfont#1#2#3#4% body style alt size / gdef % #4 can be empty +% {\def\c!!mm{#2}% +% \ifx\c!!mm\c!mm % prevents \max and alike (re)defs +% \unexpanded\setgvalue {#2}{\setcurrentfontstyle {#2}}% \rm +% \unexpanded\setgvalue {#3}{\setcurrentfontalternative {#3}}% \sl +% \else +% \unexpanded\setgvalue {#2#4}{\setcurrentfontstylesize {#2}{#4}}% \rma +% \unexpanded\setgvalue {#3#4}{\setcurrentfontalternativesize {#3}{#4}}% \sla +% \unexpanded\setgvalue {#2#3#4}{\setcurrentfontstylealternativesize{#2}{#3}{#4}}% \rmsla +% \unexpanded\setgvalue {#2}{\setcurrentfontstyle {#2}}% \rm +% \unexpanded\setgvalue {#3}{\setcurrentfontalternative {#3}}% \sl +% \unexpanded\setgvalue {#2\c!x}{\setcurrentfontxstylealternative {#2}}% \rmx +% \unexpanded\setgvalue{#2\c!xx}{\setcurrentfontxxstylealternative {#2}}% \rmxx +% \unexpanded\setgvalue {#3\c!x}{\setcurrentfontxalternative {#3}}% \slx +% \unexpanded\setgvalue{#3\c!xx}{\setcurrentfontxxalternative {#3}}% \slxx +% \unexpanded\setgvalue {#2#3}{\setcurrentfontstylealternative {#2}{#3}}% \rmsl +% \fi} +% +% leaner + +\def\checkbodyfont#1% tests for ttsl mmbf + {\def\c!!mm{#1}% + \ifx\c!!mm\c!mm % prevents \max and alike (re)defs + \expandafter\checkmathbodyfont + \else + \expandafter\checktextbodyfont + \fi{#1}} % no \c!!mm, not expanded later on + +\def\checkmathbodyfont#1#2#3% style alt size / gdef % #3 can be empty + {%\message{!m #1 #2 #3!}% + % #1 #2 #3 = signal + \unexpanded\setgvalue {#1#2#3}{\setcurrentfontstylealternativesize{#1}{#2}{#3}}% \mmsla + \unexpanded\setgvalue {#1}{\setcurrentfontstyle {#1}}% \mm + \unexpanded\setgvalue {#2}{\setcurrentfontalternative {#2}}}% \sl + +\def\checktextbodyfont#1#2#3% style alt size / gdef % #3 can be empty + {%\message{!t #1 #2 #3!}% + \unexpanded\setgvalue {#1#3}{\setcurrentfontstylesize {#1}{#3}}% \rma + \unexpanded\setgvalue {#2#3}{\setcurrentfontalternativesize {#2}{#3}}% \sla + \unexpanded\setgvalue {#1#2#3}{\setcurrentfontstylealternativesize{#1}{#2}{#3}}% \rmsla + \unexpanded\setgvalue {#1}{\setcurrentfontstyle {#1}}% \rm + \unexpanded\setgvalue {#2}{\setcurrentfontalternative {#2}}% \sl + \unexpanded\setgvalue {#1\c!x}{\setcurrentfontxstylealternative {#1}}% \rmx + \unexpanded\setgvalue{#1\c!xx}{\setcurrentfontxxstylealternative {#1}}% \rmxx + \unexpanded\setgvalue {#2\c!x}{\setcurrentfontxalternative {#2}}% \slx + \unexpanded\setgvalue{#2\c!xx}{\setcurrentfontxxalternative {#2}}% \slxx + \unexpanded\setgvalue {#1#2}{\setcurrentfontstylealternative {#1}{#2}}}% \rmsl + +\def\dodefinedefaultbodyfont[#1][#2][#3]% sizes styles identifier + {\def\dododefinedefaultbodyfont##1% + {\def\dodododefinedefaultbodyfont####1% + {\def\dododododefinedefaultbodyfont########1% + {\doifdefined{\s!default########1####1} + {% [12pt] [style] [identifier] + \getvalue{\s!default########1####1}{##1}{####1}}}% + \processcommalist[#3]\dododododefinedefaultbodyfont}% + \processcommalist[#2]\dodododefinedefaultbodyfont}% + \processcommalist[#1]\dododefinedefaultbodyfont} + +%D Unknown families are added to the family list! For the +%D moment we also set the direct calls here. Some day a better +%D solution will be implemented. The good news is that unknown +%D fonts are defined automatically. + +\newif\ifdefiningunknownfont + +% \def\defineunknownfont#1% +% {\let\c!savedtext\c!text +% \let\c!text\s!text +% \donefalse +% \def\docommand##1% +% {\doifdefined{\??ft\s!default##1} +% {\donetrue +% \scratchdimen#1\relax +% \scratchdimen\csname\??ft\s!default##1\endcsname\scratchdimen +% \normalizebodyfontsize\scratchdimen\to\!!stringa +% \letvalue{\??ft#1##1}\!!stringa}}% +% \processcommacommand[\fontrelativesizelist]\docommand +% \let\c!text\c!savedtext +% \ifdone +% \donefalse +% \def\defineunknownbodyfont##1% see *** +% %{\doifdefined{\s!default##1} +% % {\donetrue\getvalue{\s!default##1}{#1}}}% +% {\doifdefined{\s!default\s!default##1} +% {\donetrue\getvalue{\s!default\s!default##1}{#1}{##1}}}% +% \processcommacommand +% [\fontstylelist] +% \defineunknownbodyfont +% \ifdone +% \setvalue{\@size@#1}{\docompletefontswitch[#1]}% +% \ifdefiningunknownfont \else +% \definingunknownfonttrue +% \def\defineunknownsubfont##1% +% {\doifundefined{\@size@\getvalue{\??ft#1##1}} +% {\defineunknownfont{\getvalue{\??ft#1##1}}}}% +% \processcommacommand[\fontrelativesizelist]\defineunknownsubfont +% \definingunknownfontfalse +% \fi +% \fi +% \ifdone +% \showmessage\m!fonts{14}{#1}% +% \fi +% \fi} + +\def\dodefineunknownfont#1#2% + {\doifdefined{\??ft\s!default#2} + {\donetrue + \scratchdimen#1\relax + \scratchdimen\csname\??ft\s!default#2\endcsname\scratchdimen + \normalizebodyfontsize\scratchdimen\to\!!stringa + \letvalue{\??ft#1#2}\!!stringa}} + +% \def\dodefineunknownbodyfont#1#2% see *** +% {\doifdefined{\s!default\s!default#2} +% {\donetrue +% \getvalue{\s!default\s!default#2}{#1}{#2}}} +% +% but ... it needs to be as follows: +% +% \def\dodefineunknownbodyfont#1#2% see *** +% {\doifdefined{\s!default\fontclass#2}% was \s!default\s!default (related to change */*) +% {\donetrue +% \getvalue{\s!default\fontclass#2}{#1}{#2}}} +% +% eh ... this does not work so we revert back to: + +\def\dodefineunknownbodyfont#1#2% see *** + {\doifdefined{\s!default\s!default#2}% somehow related to */* + {\donetrue + \getvalue{\s!default\s!default#2}{#1}{#2}}} + +\def\dodefineunknownsubfont#1#2% + {\doifundefined{\@size@\getvalue{\??ft#1#2}} + {\donetrue + \defineunknownfont{\getvalue{\??ft#1#2}}}} + +\def\defineunknownfont#1% + {\let\c!savedtext\c!text + \let\c!text\s!text + \donefalse + \processcommacommand[\fontrelativesizelist]{\dodefineunknownfont{#1}}% + \let\c!text\c!savedtext + \ifdone + \donefalse + \processcommacommand + [\fontstylelist] + {\dodefineunknownbodyfont{#1}}% + \ifdone + \donefalse + \setvalue{\@size@#1}{\docompletefontswitch[#1]}% + \ifdefiningunknownfont \else + \definingunknownfonttrue + \processcommacommand[\fontrelativesizelist]{\dodefineunknownsubfont{#1}}% + \definingunknownfontfalse + \fi + \fi + \ifdone + \showmessage\m!fonts{14}{#1}% + \fi + \fi} + +% \def\defineunknownfontstyles#1% +% {\def\defineunknownbodyfont##1% see *** +% {\executeifdefined{\s!default\s!default##1}\gobbletwoarguments{#1}{##1}}% +% \rawprocesscommacommand[\fontstylelist]\defineunknownbodyfont} + +%D These macros show that quite some definitions take place. +%D Fonts are not loaded yet! This means that at format +%D generation time, no font files are preloaded. + +%D A previous implementation used: +%D +%D \starttyping +%D \type {\setvalue{name}{\donottest...}} +%D \stoptyping +%D +%D instead of the more memory hungry: +%D +%D \starttyping +%D \unexpanded\setvalue{name}{...} +%D \stoptyping +%D +%D The first alternative saves about 500 hash entries (about +%D 2.5\% of the total number of entries used. The second +%D alternative is currently used, because that one can more +%D easily be made \ETEX\ aware. + +%D \macros +%D {everybodyfont,Everybodyfont,everyglobalbodyfont} +%D +%D Every change in bodyfont size has conseqences for the baseline +%D distance and skips between paragraphs. These are initialized +%D in other modules. Here we only provide the hooks that +%D garantees their handling. + +% \ifx\everybodyfont\undefined % permits reloading +% \newevery \everybodyfont \EveryBodyFont +% \fi + +%D At the system level one can initialize thing like: +%D +%D \starttyping +%D \appendtoks \setupspacing \to \everybodyfont +%D \stoptyping +%D +%D While users can add their own non standard commands like: +%D +%D \starttyping +%D \EveryBodyFont{\message{changing to bodyfont \the\bodyfontsize}} +%D \stoptyping +%D +%D Personnaly I never felt the need for such extensions, but +%D at least its possible. + +%D \macros +%D {globalbodyfontsize,localbodyfontsize} +%D +%D Next we'll do the tough job of font switching. Here we +%D have to distinguish between the global (overal) bodyfont +%D size and the local (sometimes in the textflow) size. We +%D store these dimensions in two \DIMENSION\ registers. + +\ifdefined\globalbodyfontsize\else \newdimen\globalbodyfontsize \fi \globalbodyfontsize=12pt +\ifdefined\localbodyfontsize \else \newdimen\localbodyfontsize \fi \localbodyfontsize =\globalbodyfontsize + +%D \macros +%D {bodyfontsize} +%D +%D These two registers are not to be misused in calculations. +%D For this purpose we keep a copy: + +\newdimen\bodyfontsize \bodyfontsize=\globalbodyfontsize + +%D \macros +%D {outputresolution} +%D +%D Sometimes (to be honest: not in this module) we need to +%D take the system resolution into account. Therefore we also +%D define a macro: + +\def\outputresolution {600} + +%D \macros +%D {bodyfontfactor,bodyfontpoints} +%D +%D For multiplication purposes we keep an auxiliary counter +%D and macro (here the expansion is not explicitly needed): + +\newcount\bodyfontpoints \dimensiontocount\bodyfontsize\bodyfontpoints + +\edef\bodyfontfactor{\withoutpt\the\bodyfontsize} + +%D When we assign for instance 12pt to a \DIMENSION\ register +%D the \type{\the}'d value comes out as 12.0pt, which is +%D often not the way users specify the bodyfont size. Therefore +%D we also store the normalized value. + +\chardef\fontdigits=2 % was 1 + +% \def\normalizebodyfontsize#1\to#2% +% {\scratchdimen#1\relax +% \ifcase\fontdigits\advance\scratchdimen.5\points\fi +% \@EA\@EA\@EA\donormalizedbodyfontsize\@EA\WITHOUTPT\the\scratchdimen00\to#2} +% +% \def\donormalizedbodyfontsize#1.#2#3#4\to#5% \points ? +% {\edef#5% +% {#1% +% \ifcase\fontdigits\or +% \ifcase#2 \else.#2\fi % and not: \ifcase#2\else ... +% \else +% \ifcase#2#3 \else.#2\ifcase#3 \else#3\fi\fi % not: \ifcase#2#3\else ... +% \fi +% \s!pt}} + +\def\normalizebodyfontsize#1\to#2% + {\scratchdimen\dimexpr#1+\ifcase\fontdigits.5\or.05\or.005\fi\points\relax + \@EA\@EA\@EA\donormalizedbodyfontsize\@EA\WITHOUTPT\the\scratchdimen000\to#2} + +\def\donormalizedbodyfontsize#1.#2#3#4#5\to#6% \points ? + {\edef#6% not \ifcase#2\else due to \relax adding + {#1% + \ifcase\fontdigits + \or \ifcase#2 \else .#2\fi % 1 + \or \ifcase#2#3 \else .#2\ifcase#3 \else #3\fi\fi % 2 + \else \ifcase#2#3#4 \else .#2\ifcase#4 \ifcase#3 \else#3\fi \else#3#4\fi\fi % 3 + \fi + \s!pt}} + +%D To be internationalized: + +\def\korpsgrootte {\bodyfontsize} +\def\korpspunten {\bodyfontpoints} + +%D some day. + +%D \macros +%D {fontstyle,fontalternative,fontsize} +%D +%D Within a bodyfont, fonts can come in different sizes. For +%D instance \type{\tf} is accompanied by \type{\tfa}, +%D \type{\tfb} etc. The first two characters denote the +%D alternative, while the third character in these sequences +%D represents the size. The actual size is saved in a macro +%D +%D The style, being roman (\type{\rm}), sans serif (\type{\ss}) +%D etc. is also available in a macro in \type{rm}, \type{ss} +%D etc. form: + +\let\defaultfontalternative = \c!tf +\let\defaultfontstyle = \empty +\let\defaultfontsize = \empty + +\let\fontalternative = \defaultfontalternative +\let\fontstyle = \defaultfontstyle +\let\fontsize = \defaultfontsize + +%D {\em The following approach is obsolete.} +%D +%D All things related to fonts are grouped into files with +%D names like \type{font-cmr}. These files are loaded by: + +\def\resetfontdefinitionfile[#1]% + {\letbeundefined{\c!file\f!fontprefix#1}} + +\newif\ifloadfontfileonce + +\def\doreadfontdefinitionfile#1#2% #1 = set/switch state + {\doifundefined{\c!file\f!fontprefix#2}% + {\ifloadfontfileonce + \letvalue{\c!file\f!fontprefix#2}\empty + \fi + \makeshortfilename[\truefilename{\f!fontprefix#2}]% + \startreadingfile + \readsysfile{\shortfilename.mkii} + {\showmessage\m!fonts2{#2}} + {\showmessage\m!fonts3{#2}}% + \stopreadingfile}} + +%D When \type {\loadfontfileoncetrue}, such files are +%D only loaded once! This permits redundant loading, but at +%D the same time forced grouping when we want continuously mix +%D all kind of font, which of course is a kind of +%D typographically sin. The \type{"} is made inactive if +%D needed to prevent problems with loading files that use this +%D character in numbers. + +\def\doswitchpoints[#1]% + {\expanded{\dodoswitchpoints{#1}}} + +\def\dodoswitchpoints#1% + {\doifundefined{\@size@#1} + {\defineunknownfont{#1}}% + %\defineunknownfontstyles{#1}% + \doifdefinedelse{\@size@#1} + {\getvalue{\@size@#1}% + \localbodyfontsize#1\relax + \normalizebodyfontsize\localbodyfontsize\to\normalizedbodyfontsize + \checkbodyfontenvironment[\normalizedbodyfontsize]} + {\showmessage\m!fonts4{#1}}} + +\unprotected \def\doswitchstyle[#1]% + {\doifdefinedelse{\@style@#1} + {\getvalue{\@style@#1}% + \edef\fontstyle{#1}% + \ifmmode\mr\fi % in order to be compatible with \rm in math mode + }% \the\everybodyfont} % cleaner, in setting size as well as style + {\showmessage\m!fonts5{#1}}} + +%D \TEX\ loads font metric files like \type{cmr10.tfm} and +%D \type{tir.tfm} only once. In \PLAIN\ \TEX\ some font files +%D are {\em preloaded}. This means that the font files are +%D loaded, but not accessible yet by name. This is accomplished +%D by saying: +%D +%D \starttyping +%D \font\preloaded=cmr10 at 11pt +%D \stoptyping +%D +%D and using the name \type{\preloaded} again and again, so +%D fonts are indeed loaded, but unnamed, and therefore +%D unaccessible. In \CONTEXT\ we don't preload fonts, not even +%D the \PLAIN\ \TEX\ ones, although users can access them. Now +%D why is this done? + +%D Defining fonts using \type{\definebodyfont} takes time, so we +%D prefer to predefine at least the Computer Modern Roman +%D fonts. However, loading all those fonts at definition time +%D would take both time and space. But even worse, once fonts +%D are loaded into memory, their encoding vector is fixed, +%D which is a handicap when we want to distribute the compact +%D \type{fmt} files. So what we want to do is defining fonts in +%D a way that postpones the loading. We accomplish this by only +%D loading the fonts when we switch to another bodyfont size. +%D Among the other alternatives, such as loading the font at +%D the moment of activation and redefining the activation +%D macro afterwards, this proved to be the most efficient +%D alternative. +%D +%D The next few macros take care of the one exeption on this +%D scheme. When at format generation time we load the default +%D font file, the one that defines the Computer Modern Fonts, +%D we don't want the fonts metrics to end up in the format +%D file, so we temporary prohibit loading. This means that at +%D runtime we have to load the default bodyfont size just before +%D we start typesetting. +%D +%D Therefore we have to signal the font switching macros that +%D we are preloading fonts. As long as the next boolean is, +%D true, no loading is done. + +\newif\ifloadingfonts \loadingfontstrue + +%D \macros +%D {preloadfonts} +%D +%D Preloading is only called for once, during the startup +%D sequence of a session. After the loading job is done, the +%D macro relaxes itself and reset the signal. + +\def\preloadfonts % never called, needs a clean up + {\showmessage\m!fonts6{\normalizedbodyfontsize\normalspace\fontstyle}% + \global\loadingfontsfalse + \doswitchpoints[\normalizedbodyfontsize]% + \doswitchstyle[\fontstyle]% + \the\everybodyfont + \the\everyglobalbodyfont + \saveinterlinespace + \global\let\preloadfonts\relax} + +% \prependtoks \preloadfonts \to \everydump % saves .1 s on a DELL P60 - 2GHZ + +%D Here comes the main font switching macros. These macros +%D handle changes in size as well as returning to the global +%D bodyfont size. + +\def\dosetfont#1#2% #1 = set/switch state + {\doifelse{#2}\v!global + {\restoreglobalbodyfont} + {\processcommacommand[#2]{\dodosetfont{#1}}% ##1 get also passed + \ifloadingfonts\else + \doswitchpoints[\normalizedbodyfontsize]% + \doswitchstyle[\fontstyle]% + \fi}% + \chardef\currentxfontsize\zerocount} + +\def\dodosetfont#1#2% #1 = set/switch state | check fo rempty, else space + {\doifsomething{#2}{\dododosetfont{#1}{#2}{\showmessage\m!fonts4{#2}}}} + +\def\dododosetfont#1#2#3% #1 = set/switch state + {\doifnumberelse{#2} + {\dodododosetfont{#1}{#2}{#3}} + {\doifdefinedelse{\??ft\normalizedbodyfontsize\interfaced{#2}} + {\edef\fontstep{\bodyfontcsname\normalizedbodyfontsize\interfaced{#2}\endcsname}% + \expanded{\dodododosetfont{#1}{\fontstep}}{#3}} + {\doifelse{#2}\v!reset + {\let\fontstyle\empty % new 31/7/2006 + \let\fontsize \empty} + {\doifdefinedelse{\@style@#2} + {\edef\fontstyle{#2}} + {\doreadfontdefinitionfile{#1}{#2}}}}}} + +\def\dodododosetfont#1#2#3% #1 = set/switch state + {\scratchdimen#2\relax + \normalizebodyfontsize\scratchdimen\to\normalizedsetfont + \doifundefined{\@size@\normalizedsetfont} + {\defineunknownfont{#2}}% + \doifdefinedelse{\@size@\normalizedsetfont} + {\localbodyfontsize\normalizedsetfont + \let\normalizedbodyfontsize\normalizedsetfont} + {#3\dosetsubstitutefont{#1}{#2}}} + +%D In the previous macros we use \type{\currentxfontsize} to +%D hold the current x||size of the font. This enables us to +%D support for instance \type{\sl} inside a \type{\tx} switch. + +\chardef\currentxfontsize=0 + +%D When users specify for instance a 13 point bodyfont while no +%D such bodyfont is defined, the system automatically tries to +%D find a best fit, that is the nearest smaller defined +%D bodyfontzize. A smaller one is definitely better than a larger +%D one, simply because otherwise a lot of overfull box messages +%D are more probable to occur. By taking a value slightly +%D smaller than half a point, we can use the next method. + +\def\dosetsubstitutefont#1#2% #1 = set/switch state + {\scratchdimen#2\relax + \advance\scratchdimen .499\points + \dimensiontocount\scratchdimen\scratchcounter + \advance\scratchcounter \minusone + \ifnum\scratchcounter>\plusthree + \dododosetfont{#1}{\the\scratchcounter\s!pt}{}% + \fi} + +% The following bunch of macros deals with the (run time) +% expansion of names onto the definitions made by \type +% {\definebodyfont}. + +% \let\fontbody \empty % ... 10pt 11pt 12pt ... +% \let\fontstyle \empty % rm ss tt mm hw cg ... +% \let\fontalternative\empty % tf bf sl it bs bi sc ... +% \let\fontsize \empty % xy-abcd ... + +\def\defaultfontbody{\normalizedbodyfontsize} + +\let\fontbody\defaultfontbody + +\let\fontclass\empty \let\globalfontclass\fontclass + +% \def\setcurrentfontclass#1% +% {\edef\fontclass{#1}} + +\def\registerfontclass#1% + {\letgvalue{\@fontclass@#1}\v!yes} % global ? + +\def\setcurrentfontclass#1% + {\ifcsname\@fontclass@#1\endcsname + \edef\fontclass{#1}% + \fi} + +\let\defaultfontstyle \c!rm +\let\defaultfontalternative \c!tf +\let\defaultfontsize \empty + +%D \macros +%D {bigmath,nobigmath} +%D +%D We can inhibit this slow||downer with: + +% these can best be combined + +% 0=never 1=everymath 2=always + +\chardef\synchronizebigmathflag=1 + +\appendtoks + \ifcase\synchronizebigmathflag + % never + \or + \synchronizebigmath + \or + % always + \fi +\to \everymathematics + +\def\nobigmath{\chardef\synchronizebigmathflag 0 } +\def\bigmath {\chardef\synchronizebigmathflag 2 \synchronizebigmath} + +\let\bigmathfontsize\empty + +\def\synchronizebigmath + {\ifx\bigmathfontsize\fontsize + % already in sync + \else + \let\bigmathfontsize\fontsize + \synchronizemath \synchronizetext + \fi} + +\def\checkbigmathsynchronization + {\ifcase\synchronizebigmathflag + % never + \or + \ifmmode \synchronizebigmath \fi + \or + \synchronizebigmath + \fi} + +%D So far for synchronisation. + +\def\dosetcurrentfontsize#1% + {\edef\fontsize{#1}% + \checkbigmathsynchronization} + +\def\dosetcurrentfontalternative#1% + {\edef\fontalternative{#1}% + \ifmmode % maybe no test, or actually, an option + \fam\csname\fontalternative\s!fam\endcsname + \fi} + +\def\setcurrentfont#1#2#3#4% + {%\message{[1 #1 #2 #3 #4]}% + \edef\fontbody{#1}% + \edef\fontstyle{#2}% + \dosetcurrentfontalternative{#3}% + \dosetcurrentfontsize{#4}% + \synchronizefont} + +\def\setcurrentfontbody#1% + {%\message{[2 #1]}% + \edef\fontbody{#1}% + \synchronizefont} + +% \def\setcurrentfontstyle#1% +% {%\message{[3 #1]}% +% \edef\fontstyle{#1}% +% \ifmmode\mr\fi % otherwise \rm not downward compatible +% \synchronizefont} +% +% For Taco: optional fall backs: + +\ifx\checkfontclass\undefined \let\checkfontclass\gobbleoneargument \fi % implemented in type-ini + +\def\setcurrentfontstyle#1% + {%\message{[3 #1]}% + \checkfontclass{#1}% + \edef\fontstyle{#1}% + \ifmmode\mr\fi % otherwise \rm not downward compatible + \synchronizefont} + +\def\setcurrentfontbodyalternative#1#2% + {%\message{[4 #1 #2]}% + \edef\fontbody{#1}% + \dosetcurrentfontalternative{#2}% + \synchronizefont} + +\def\setcurrentfontalternative#1% + {%\message{[5 #1]}% + \dosetcurrentfontalternative{#1}% + \synchronizefont} + +\def\setcurrentfontsize#1% + {%\message{[6 #1]}% + \dosetcurrentfontsize{#1}% + \synchronizefont} + +\def\setcurrentfontstylealternative#1#2% \rmsl + {%\message{[7 #1 #2]}% + \edef\fontstyle{#1}% + \dosetcurrentfontalternative{#2}% + \synchronizefont} + +\def\setcurrentfontstylesize#1#2% \rmsla + {%\message{[8 #1 #2]}% + \edef\fontstyle{#1}% + \dosetcurrentfontsize{#2}% + \synchronizefont} + +\def\setcurrentfontalternativesize#1#2% \sla + {%\message{[9 #1 #2]}% + \dosetcurrentfontalternative{#1}% + \dosetcurrentfontsize{#2}% + \synchronizefont} + +\def\setcurrentfontstylealternativesize#1#2#3% \rmsla + {%\message{[10 #1 #2 #3]}% + \edef\fontstyle{#1}% + \dosetcurrentfontalternative{#2}% + \dosetcurrentfontsize{#3}% + \synchronizefont} + +%D In principle one can assign alternative fallback routines. +%D Some day we will. + +\newtoks\fontstrategies +\newif\iftryingfont + +\let\fontstrategy\relax + +\def\synchronizefont + {\tryingfonttrue + \ifx\fontclass\empty\else + \global\let\fontstrategy\dofontclassstrategy + \the\fontstrategies \relax % \relax still needed ? + \fi + \iftryingfont + \global\let\fontstrategy\dofontstrategy + \the\fontstrategies \relax % \relax still needed ? + \fi + \ifskipfontcharacteristics + \setfontcharacteristics + \the\everyfontswitch + \fi} + +\def\dofontstrategy#1#2#3#4#5% + {\ifcsname#2#3#4#5\endcsname + #1\csname#2#3#4#5\endcsname \tryingfontfalse + \fi} + +\def\dofontclassstrategy#1#2#3#4#5% + {\ifcsname\fontclass#2#3#4#5\endcsname + #1\csname\fontclass#2#3#4#5\endcsname \tryingfontfalse + \fi} + +\appendtoks \iftryingfont \fontstrategy \autofontsizefalse % --- --- --- --- % pt tt bf a + \fontbody \fontstyle \fontalternative \fontsize +\fi \to \fontstrategies + +\appendtoks \iftryingfont \fontstrategy \autofontsizetrue % --- --- --- def % pt tt bf + \fontbody \fontstyle \fontalternative \defaultfontsize +\fi \to \fontstrategies + +\appendtoks \iftryingfont \fontstrategy \autofontsizetrue % --- --- def --- % pt tt tf a + \fontbody \fontstyle \defaultfontalternative \fontsize +\fi \to \fontstrategies + +\appendtoks \iftryingfont \fontstrategy \autofontsizetrue % --- --- def def % pt tt tf + \fontbody \fontstyle \defaultfontalternative \defaultfontsize +\fi \to \fontstrategies + +\appendtoks \iftryingfont \fontstrategy \autofontsizefalse % --- def def def % pt rm tf + \fontbody \defaultfontstyle \defaultfontalternative \defaultfontsize +\fi \to \fontstrategies + +\appendtoks \iftryingfont \fontstrategy \autofontsizetrue % def def def def % rm tf + \defaultfontbody \defaultfontstyle \defaultfontalternative \defaultfontsize +\fi \to \fontstrategies + +%D Let's synchronize: + +\newif\ifsynchronizefonts \synchronizefontstrue + +\prependtoks + \ifsynchronizefonts + \synchronizemath + \synchronizetext + \synchronizefont % problem: syncs last font + \fi +\to \everybodyfont + +%D Setting the normal sized as well as the x and xx smaller +%D sizes is accomplished by the next set of macros. When in +%D math mode, the commands \type{\tx} and \type{\txx} are +%D just a switch to the script and double script styles, but +%D in text mode the values defined by the bodyfontenvironment are +%D used. Here we also set \type{\currentxfontsize}. + +\def\dosetcurrentfontxxxalternative#1#2#3#4% + {\chardef\currentxfontsize#2\relax + \ifmmode + #4% + \else\ifcsname\bodyfontvariable{\normalizedbodyfontsize#3}\endcsname + \setcurrentfontbodyalternative{\bodyfontcsname\normalizedbodyfontsize#3\endcsname}{#1}% + \fi\fi} + +\def\setcurrentfontxalternative#1% + {\dosetcurrentfontxxxalternative{#1}1\c!x\scriptstyle + \let\tx\txx} + +\def\setcurrentfontxxalternative#1% + {\dosetcurrentfontxxxalternative{#1}2\c!xx\scriptscriptstyle + \let\tx\empty + \let\txx\empty} + +\def\checknestedxfontsize % option + {\ifcase\currentxfontsize\else\ifx\fontsize\empty\else + \chardef\currentxfontsize\zeropoint + \let\fontsize\empty + \let\tx\normaltx + \let\txx\normaltxx + \fi\fi} + +\def\setcurrentfontxalternative#1% + {\checknestedxfontsize + \dosetcurrentfontxxxalternative{#1}1\c!x\scriptstyle + \let\tx\txx} + +\def\setcurrentfontxxalternative#1% + {\checknestedxfontsize + \dosetcurrentfontxxxalternative{#1}2\c!xx\scriptscriptstyle + \let\tx\empty + \let\txx\empty} + +% This alterative is not really needed, but for old time's sake +% we keep it there. We can speed it up when needed. + +% \def\setcurrentfontxstylealternative #1{\csname#1\endcsname\tfx} +% \def\setcurrentfontxxstylealternative#1{\csname#1\endcsname\tfxx} + +\def\setcurrentfontxstylealternative #1{\csname#1\endcsname\tx} +\def\setcurrentfontxxstylealternative#1{\csname#1\endcsname\txx} + +%D These macros also show us that when we call for \type{\tx}, +%D this macro is redefined to be \type{\txx}. Therefore calls +%D like: +%D +%D \startbuffer +%D {small \tx is \tx beautiful} +%D {small \tx is \txx beautiful} +%D {small \txx is \tx beautiful} +%D {small \txx is \txx beautiful} +%D \stopbuffer +%D +%D \typebuffer +%D +%D result in: +%D +%D \startvoorbeeld +%D \startlines +%D \getbuffer +%D \stoplines +%D \stopvoorbeeld +%D +%D Setting the main size involves the style list and therefore +%D takes a bit more time. Keep in mind that the fontsize is +%D represented by a character or empty. + +\unexpanded\def\tx {\setcurrentfontxalternative \fontalternative} +\unexpanded\def\txx{\setcurrentfontxxalternative\fontalternative} + +\let\normaltx \tx +\let\normaltxx\txx + +%D \macros +%D {definefontstyle} +%D +%D When setting of switching the overall style we can use the +%D short identifier like rm and ss, but when defined we can +%D also use more verbose names like roman or sansserif. Such +%D names are defined by: +%D +%D \starttyping +%D \definefontstyle [serif,rm] [rm] +%D \definefontstyle [sansserif,ss] [ss] +%D \stoptyping + +\def\dodefinefontstyle[#1][#2]% + {\rawdoifinsetelse{#2}{\fontstylelist} + {}%\debuggerinfo\m!fonts{unknown style #2}} + {\addtocommalist{#2}\fontstylelist + \showmessage\m!fonts8{#2\space (#1)}}% + % check kan hier + \def\docommand##1% + {\setvalue{\@shortstyle@##1}{#2}% + \setvalue{\@style@##1}{\csname#2\endcsname}}% + \processcommalist[#1]\docommand} + +\def\definefontstyle + {\dodoubleargument\dodefinefontstyle} + +\def\setfontstyle#1#2% #1:name (roman, romaan) #2:style (rm) + {\edef\fontstyle{#1}% + \setcurrentfontstyle\normalizedbodyfontsize} + +\chardef\defaultskewcharmi=127 % '177 +\chardef\defaultskewcharsy= 48 % '60 + +% \def\dosetskewchar#1% +% {\skewchar\font\ifx\@@fontskewchar\empty#1\else\@@fontskewchar\fi} + +\def\dosetskewchar#1#2% + {\ifx\@@fontskewchar\empty + \skewchar\textfont #1#2% + \skewchar\scriptfont #1#2% + \skewchar\scriptscriptfont#1#2% + \else + \skewchar\textfont #1\@@fontskewchar + \skewchar\scriptfont #1\@@fontskewchar + \skewchar\scriptscriptfont#1\@@fontskewchar + \fi} + +%D The previous macros show that it's is not always +%D neccessary to define the whole bunch of fonts, take for +%D instance the sequence: +%D +%D \starttyping +%D \setupbodyfont +%D [ams] +%D +%D \definebodyfont [24pt] [mm] +%D [ma=msam10 at 24pt, +%D mb=msbm10 at 24pt] +%D +%D \switchtobodyfont +%D [24pt] +%D +%D This is a 24pt $\blacktriangleleft$ +%D \stoptyping +%D +%D Here we didn't define the 24 point bodyfont environment, so +%D it's defined automatically. Of course one can always use the +%D \TEX\ primitive \type{\font} to switch to whatever font +%D needed. + +%D When asking for a complete font switch, for instance from 10 +%D to 12~points, the next macro does the job. First we +%D normalize the size, next we define the current range of +%D text, script and scriptscript sizes, then we set the text +%D fonts and the math families and finally we activate the +%D default typeface and also set the font specific parameters +%D assigned to \type{\everybodyfont} + +\def\dosetbodyfontface#1#2% + {\edef#1{\bodyfontcsname\normalizedbodyfontsize#2\endcsname}} + +\def\docompletefontswitch[#1]% + {\bodyfontsize#1\relax + \dimensiontocount\bodyfontsize\bodyfontpoints + \edef\bodyfontfactor{\withoutpt\the\bodyfontsize}% + \normalizebodyfontsize\bodyfontsize\to\normalizedbodyfontsize + \dosetbodyfontface \textface \s!text + \dosetbodyfontface \scriptface \s!script + \dosetbodyfontface \scriptscriptface \s!scriptscript} + +\docompletefontswitch[12pt] % init + +%D \macros +%D {setupbodyfont,switchtobodyfont} +%D +%D The next two macros are user ones. With \type{\setupbodyfont} +%D one can set the document bodyfont size, font family, style +%D and/or options defined in files, for example: +%D +%D \starttyping +%D \setupbodyfont[cmr,ams,12pt,roman] +%D \stoptyping +%D +%D This command affects the document as a whole: text, headers +%D and footers. The second macro however affects only the text: +%D +%D \starttyping +%D \switchtobodyfont[10pt] +%D \stoptyping +%D +%D So we've got: +%D +%D \showsetup{setupbodyfont} +%D \showsetup{switchtobodyfont} +%D +%D Both macros look alike. The second one also has to take +%D all kind of keywords into account. + +\ifx\saveinterlinespace \undefined \let\saveinterlinespace \relax \fi +\ifx\restoreinterlinespace\undefined \let\restoreinterlinespace\relax \fi + +\chardef\bodyfontsetstate=0 + +\definecomplexorsimple\setupbodyfont + +\def\simplesetupbodyfont + {\restoreglobalbodyfont + \saveinterlinespace} + +\def\complexsetupbodyfont[#1]% + {\doifsomething{#1} + {\dosetfont1{#1}% + \globalbodyfontsize\localbodyfontsize + \normalizebodyfontsize\globalbodyfontsize\to\normalizedglobalbodyfontsize + \let\globalfontstyle\fontstyle + \ifloadingfonts\else + \the\everybodyfont + \the\everyglobalbodyfont + \saveinterlinespace + \fi}} + +\unexpanded\def\switchtobodyfont[#1]% + {\doifsomething{#1} + {\doifdefinedelse{\??ft\normalizedbodyfontsize\interfaced{#1}} + {\setbodyfontstep{#1}} % so we have a fast [small] switch + {\dosetfont0{#1}}% + \the\everybodyfont}} % indeed needed in case nothing is executed + +%D The following alternative is meant for math||to||text +%D switching and will be optimized. + +\def\fastswitchtobodyfont#1% + {\ifcsname\??ft\normalizedbodyfontsize#1\endcsname + \edef\futurebodyfontsize + {\csname\??ft\normalizedbodyfontsize#1\endcsname}% + \ifcsname\@size@\futurebodyfontsize\endcsname + \csname\@size@\futurebodyfontsize\endcsname + \localbodyfontsize\futurebodyfontsize\relax + \fi + \fi + \csname\@style@\fontstyle\endcsname + \the\everybodyfont} + +%D Because the last macro can appear in arguments or be assigned +%D to parameters, we protect this one for unwanted expansion. + +\def\dodosetmathfont#1% + {\setcurrentfontalternative{#1}% + % \doifdefinedelse{#1\s!fam} % adapted + % {\edef\mffam{\getvalue{#1\s!fam}}} + % {\edef\mffam{\getvalue{\c!nn\s!fam}}}% + \textfont \mrfam\textfont \mffam + \scriptfont \mrfam\scriptfont \mffam + \scriptscriptfont\mrfam\scriptscriptfont\mffam} + +\def\domffam#1% + {\csname\ifcsname#1\s!fam\endcsname#1\else\c!nn\fi\s!fam\endcsname} + +\def\mffam + {\domffam\fontalternative} + +\def\dosetmathfont + {\def\rm{\fam\mrfam}\dodosetmathfont} + +\def\enableencodinginmath + {\appendtoks + \everyhbox{\mr\everyhbox\emptytoks}% + \everyvbox{\mr\everyvbox\emptytoks}% + \to \everymathematics} % was \everymath + +% \enableencodinginmath % too untested to enable by default + +%D \starttyping +%D $\cases{& \ccaron}$ $x=\hbox{\ccaron $x=\hbox{\ccaron}$}$ +%D \stoptyping + +%D The font specific features are bound to the filename. + +\def\updatefontparameters + {\edef\@@fontencoding{\truefontdata\fontfile \s!encoding}% + \edef\@@fontmapping {\truefontdata\fontfile \s!mapping }% + \edef\@@fonthandling{\truefontdata\somefontname\s!handling}% + \edef\@@fontfeatures{\truefontdata\fontfile \s!features}% + \edef\@@fontskewchar{\truefontdata\fontfile \s!skewchar}} + +\def\setfontcharacteristics + {\updatefontparameters % redundant, will go away, faster too + \fastenableencoding + {\ifx\@@fontencoding\empty + \s!default \else \@@fontencoding + \fi}% + \fastenablemapping + {\ifx\@@fontmapping\empty + \ifx\@@fontencoding\empty + \s!default \else \@@fontencoding + \fi + \else + \@@fontmapping + \fi}% + \fastenablehandling + {\ifx\@@fonthandling\empty + \s!default \else \@@fonthandling + \fi}% + {\lastfontidentifier}% + \the\everyfont + \synchronizepatternswithfont} + +\ifx\synchronizepatternswithfont\undefined + \def\synchronizepatternswithfont{\synchronizepatterns} +\fi + +%D Experimental: + +\def\definefontfeature + {\dotripleargument\dodefinefontfeature} + +\def\dododefinefontfeature#1% + {\edef\!!stringa{\ifx\!!stringa\empty\else\!!stringa,\fi\executeifdefined{\??fa#1}\empty}} + +\def\dodefinefontfeature[#1][#2][#3]% + {\doifassignmentelse{#2} + {\setevalue{\??fa#1}{#2}} + {\let\!!stringa\empty + \processcommalist[#2]\dododefinefontfeature + \setevalue{\??fa#1}{\ifx\!!stringa\empty\else\!!stringa,\fi#3}}} + +\definefontfeature + [default] + [liga=yes,kern=yes,tlig=yes,trep=yes] % texligatures=yes,texquotes=yes + +\definefontfeature + [smallcaps] + [liga=yes,kern=yes,tlig=yes,trep=yes,smcp=yes] % texligatures=yes,texquotes=yes + +\definefontfeature + [oldstyle] + [liga=yes,kern=yes,tlig=yes,trep=yes,onum=yes] % texligatures=yes,texquotes=yes + +\definefontfeature % no calt + [arabic] + [language=dflt,script=arab, + init=yes,medi=yes,fina=yes,isol=yes, + liga=yes,dlig=yes,rlig=yes,clig=yes, + mark=yes,mkmk=yes,kern=yes,curs=yes] + +\definefontfeature + [none] + [features=no] + +%D The next auxilliary macro is an alternative to \type +%D {\fontname}. + +% \def\purefontname#1{\expandafter\splitoffpurefontname\fontname#1 \\} +% +% extra level is needed: + +\def\purefontname#1{\@EA\splitoffpurefontname\@EA{\@EA{\@EA\unstringed\fontname#1}} \\} + +\def\splitoffpurefontname#1 #2\\{#1} + +%D \macros +%D {switchstyleonly} +%D +%D For switching a style but keeping the alternative, there +%D is: +%D +%D \starttyping +%D {\bf text \switchstyleonly\ss text} +%D {\bf text \switchstyleonly[ss]text} +%D {\sl text \switchstyleonly[sansserif]text} +%D \stoptyping + +\definecomplexorsimple\switchstyleonly + +\def\simpleswitchstyleonly#1% stupid version + {\complexswitchstyleonly[\strippedcsname#1]} + +\def\complexswitchstyleonly[#1]% todo : check + {\setcurrentfontstyle{\getvalue{\@shortstyle@#1}}% + \the\everybodyfont} % needed ? + +%D \macros +%D {os,frak, goth, cal} +%D +%D Old style numerals can be typeset with \type{\os} and look +%D like {\os 1234567890} instead of the more common looking +%D 1234567890. +%D +%D On behalf of {\frac Tobias Burnus}, we define some more of +%D these. Later we will link these names to real file names. + +% older +% +% \definefont [os] [OldStyle sa *] +% \definefont [frak] [Fraktur sa *] +% \definefont [goth] [Gothic sa *] +% \definefont [cal] [Calligraphic sa *] +% \definefont [bbd] [Blackboard sa *] +% +% newer + +\unexpanded\def\os {\mathortext{\fam\purefamily {oldstyle}}{\symbolicfont {OldStyle}}} +\unexpanded\def\frak{\mathortext{\fam\purefamily {fraktur}}{\symbolicfont {Fraktur}}} +\unexpanded\def\goth{\mathortext{\fam\purefamily {gothic}}{\symbolicfont {Gothic}}} +\unexpanded\def\cal {\mathortext{\fam\purefamily{calligraphic}}{\symbolicfont{Calligraphic}}} +\unexpanded\def\bbd {\mathortext{\fam\purefamily {blackboard}}{\symbolicfont {Blackboard}}} + +\definefontsynonym [OldStyle] [Serif] +\definefontsynonym [Fraktur] [Serif] +\definefontsynonym [Gothic] [Serif] +\definefontsynonym [Calligraphic] [Serif] +\definefontsynonym [Blackboard] [Serif] + +%D \macros +%D {fraktur, gothic, calligraphic, blackboard} +%D +%D These macros assume that we use text fonts, and not math +%D families. + +\ifx\mathtext\undefined \let\mathtext\hbox \fi + +\unexpanded\def\fraktur #1{\mathortext\domathtext\donothing{\frak#1}} +\unexpanded\def\gothic #1{\mathortext\domathtext\donothing{\goth#1}} +\unexpanded\def\calligraphic#1{\mathortext\domathtext\donothing{\cal #1}} +\unexpanded\def\blackboard #1{\mathortext\domathtext\donothing{\bbd#1}} + +%D Torture test: +%D +%D \starttyping +%D \usetypescript[modern] [texnansi] +%D \usetypescript[lucida] [texnansi] +%D \usetypescript[palatino][texnansi] +%D \usetypescript[times] [texnansi] +%D \usetypescript[fourier] [ec] +%D +%D \startbuffer +%D \section{\blackboard{T\high{\blackboard{T}}} \blackboard{E}\high{\blackboard{E}} \blackboard{X}\high{\blackboard{X}}} +%D +%D {\fontclass: 123 \os123 \cal TEX $\os 123$} +%D +%D $\blackboard{T}^{\blackboard{T}} \blackboard{E}^{\blackboard{E}} \blackboard{X}^{\blackboard{X}}$ +%D \blackboard{T}\high{\blackboard{T}} \blackboard{E}\high{\blackboard{E}} \blackboard{X}\high{\blackboard{X}} +%D \stopbuffer +%D +%D {\setupbodyfont[lucida] \getbuffer} +%D {\setupbodyfont[modern] \getbuffer} +%D {\setupbodyfont[palatino] \getbuffer} +%D {\setupbodyfont[times] \getbuffer} +%D {\setupbodyfont[fourier] \getbuffer} +%D \stoptyping + +%D \macros +%D {definebodyfontswitch} +%D +%D \PLAIN\ \TEX\ defines some macro's like \type{\tenpoint} +%D to switch to a specific bodyfontsize. Just for the sake of +%D compatibility we can define them like: +%D +%D \starttyping +%D \definebodyfontswitch [twelvepoint] [12pt] +%D \stoptyping +%D +%D We don't support language specific synonyms here, mainly +%D because \PLAIN\ \TEX\ is english anyway. + +\def\dodefinebodyfontswitch[#1][#2]% + {\def\docommand##1{\setvalue{##1}{\switchtobodyfont[#2]}}% + \processcommalist[#1]\docommand} + +\def\definebodyfontswitch + {\dodoubleargument\dodefinebodyfontswitch} + +%D \macros +%D {setsmallbodyfont,setmainbodyfont,setbigbodyfont} +%D +%D When we're typesetting at for instance 10pt, we can call for +%D the \type{small} as well as the \type{big} alternative, +%D related to this main size, using \type{\switchtobodyfont[small]}. +%D The three alternatives can be activated by the next three +%D system calls and are defined by the bodyfontenvironment. + +\let\fontstep\empty % we can use \fontstep for tracing purposes + +\def\setbodyfontstep#1% + {\edef\fontstep{\bodyfontcsname\normalizedbodyfontsize\interfaced{#1}\endcsname}% + \doswitchpoints[\fontstep]% + \doswitchstyle[\fontstyle]} + +\unexpanded\def\setsmallbodyfont{\setbodyfontstep\v!small\the\everybodyfont} +\unexpanded\def\setbigbodyfont {\setbodyfontstep\v!big \the\everybodyfont} + +\unexpanded\def\setmainbodyfont + {\doswitchpoints[\normalizedbodyfontsize]% + \doswitchstyle[\fontstyle]% + \the\everybodyfont + \the\everyglobalbodyfont + \saveinterlinespace} + +%D \macros +%D {restoreglobalbodyfont} +%D +%D Users can set whatever font available while typesetting text. +%D Pagenumbers, footers, headers etc. however must be typeset +%D in the main bodyfont and style of the document. Returning to +%D the global state can be done with the next macro: + +\let\mainfontclass\empty + +\def\fullrestoreglobalbodyfont + {\let\fontsize\defaultfontsize + \let\fontbody\defaultfontbody + \chardef\currentxfontsize\zerocount + \let\fontclass\globalfontclass + \doswitchpoints[\normalizedglobalbodyfontsize]% + \doswitchstyle[\globalfontstyle]% + \redoconvertfont % just in case a pagebreak occurs + \tf + \the\everybodyfont + \the\everyglobalbodyfont + \saveinterlinespace} + +\def\partialrestoreglobalbodyfont + {\let\fontsize\defaultfontsize + \let\fontbody\defaultfontbody + \chardef\currentxfontsize\zerocount + \redoconvertfont + \tf + \saveinterlinespace} + +\def\restoreglobalbodyfont % ook style etc + {\ifx\fontclass\globalfontclass + \ifx\fontstyle\globalfontstyle + \ifx\normalizedbodyfontsize\normalizedglobalbodyfontsize + \partialrestoreglobalbodyfont + \else + \fullrestoreglobalbodyfont + \fi + \else + \fullrestoreglobalbodyfont + \fi + \else + \fullrestoreglobalbodyfont + \fi} + +% in case of troubles: \let\restorebodyfont\fullrestoreglobalbodyfont + +%D This macro has to be called when entering the pagebody +%D handling routine as well as the footnote insert routine. +%D Users can access this feature |<|for instance when one wants +%D to typeset tables and alike in the main bodyfont and style +%D while the running text is temporary set to a smaller one|>| +%D by saying \type{\switchtobodyfont[global]}. + +%D \macros +%D {rasterfont} +%D +%D There are (at the moment) two situations in which we want to +%D have fast access to a particular font. When we are using +%D \TEX\ to typeset rasters, we use small {\rasterfont.}'s +%D (a rather small period indeed), the same as \PICTEX\ uses +%D for drawing purposes. + +\definefont [rasterfont] [Serif at 5pt] + +%D \macros +%D {infofont} +%D +%D The second situation occurs when we enable the info mode, +%D and put all kind of status information in the margin. We +%D don't want huge switches to the main bodyfont and style, so +%D here too we use a direct method. + +% old mechanism +% +% \def\infofont{\getvalue{\!!sixpoint\c!tt\c!tf}\the\everybodyfont} + +\let\infofont\relax % satisfy dep checker + +\definefont [infofont] [Mono at 6pt] % todo \the\everybodyfont + +%D \macros +%D {definealternativestyle} +%D +%D In the main modules we are going to implement lots of +%D parameterized commands and one of these parameters will +%D concern the font to use. To suit consistent use of fonts we +%D here implement a mechanism for defining the keywords that +%D present a particular style or alternative. +%D +%D \starttyping +%D \definealternativestyle [keywords] [\style] [\nostyle] +%D \stoptyping +%D +%D The first command is used in the normal textflow, while the +%D second command takes care of headings and alike. Consider +%D the next two definitions: +%D +%D \starttyping +%D \definealternativestyle [bold] [\bf] [] +%D \definealternativestyle [cap] [\cap] [\cap] +%D \stoptyping +%D +%D A change \type{\bf} in a heading which is to be set in +%D \type{\tfd} does not look that well, so therefore we leave +%D the second argument of \type{\definealternativestyle} empty. +%D When we capatalize characters using the pseudo small cap +%D command \type{\cap}, we want this to take effect in both +%D text and headings, which is accomplished by assigning both +%D arguments. + +\def\dodefinealternativestyle[#1][#2][#3]% + {\def\docommand##1% + {\doifundefined{##1}{\setvalue{##1}{\groupedcommand{#2}{}}}% + \setvalue{\@letter@ ##1}{#2}% + \setvalue{\@noletter@##1}{#3}}% + \processcommalist[#1]\docommand} + +\def\definealternativestyle + {\dotripleempty\dodefinealternativestyle} + +\def\definestyle{\definealternativestyle} + +%D Maybe too geneneric, but probably ok is the following. (Maybe one +%D day we will use a dedicated grouped command for styles.) + +\appendtoks + \let\groupedcommand\thirdofthreearguments +\to \simplifiedcommands + +%D This command also defines the keyword as command. This means +%D that the example definition of \type{bold} we gave before, +%D results in a command \type{\bold} which can be used as: +%D +%D \startbuffer +%D He's a \bold{bold} man with a {\bold head}. +%D \stopbuffer +%D +%D \typebuffer +%D +%D or +%D +%D \startvoorbeeld +%D \definealternativestyle[bold][\bf][]\getbuffer +%D \stopvoorbeeld +%D +%D Such definitions are of course unwanted for \type{\cap} +%D because this would result in an endless recursive call. +%D Therefore we check on the existance of both the command and +%D the substitution. The latter is needed because for instance +%D \type{\type} is an entirely diferent command. That command +%D handles verbatim, while the style command would just switch +%D to teletype font. This is just an example of a tricky +%D naming coincidence. + +%D \macros +%D {doconvertfont,noconvertfont, +%D dontconvertfont,redoconvertfont} +%D +%D After having defined such keywords, we can call for them by +%D using +%D +%D \starttyping +%D \doconvertfont{keyword}{text} +%D \stoptyping +%D +%D We deliberately pass an argument. This enables us to +%D assign converters that handle one agrument, like +%D \type{\cap}. +%D +%D By default the first specification is used to set the style, +%D exept when we say \type{\dontconvertfont}, after which the +%D second specification is used. We can also directly call for +%D \type{\noconvertfont}. In nested calls, we can restore the +%D conversion by saying \type{\redoconvertfont}. + +\def\@@dodoconvertfont#1{\csname\@letter@ #1\endcsname} +\def\@@donoconvertfont#1{\csname\@noletter@#1\endcsname} + +\unexpanded\def\dodoconvertfont#1% #2% we need the protection + {\doifdefinedelse{\@letter@#1} % in testing + {\doifelsenothing{#1}\gobbleoneargument\@@dodoconvertfont} + {\doifdefinedelse{#1}\getvalue \firstofoneargument}% + {#1}} % {#2}} + +\let\doconvertfont\dodoconvertfont + +\def\noconvertfont#1% #2% + {\doifdefinedelse{\@noletter@#1} + {\doifelsenothing{#1}\gobbleoneargument\@@donoconvertfont}\gobbleoneargument + {#1}} % {#2}} + +%D Extras: + +\unexpanded\def\dontconvertfont + {\let\doconvertfont\noconvertfont} + +\unexpanded\def\redoconvertfont + {\let\doconvertfont\dodoconvertfont} + +%D These commands are not grouped! Grouping is most probably +%D done by the calling macro's and would lead to unnecessary +%D overhead. + +%D \macros +%D {em,emphasistypeface,emphasisboldface} +%D +%D The next macro started as a copy of Donald Arseneau's +%D \type{\em} (\TUGNEWS\ Vol.~3, no.~1, 1994). His +%D implementation was a bit more sophisticated version of the +%D standard \LATEX\ one. We furter enhanced the macro, so now +%D it also adapts itself to boldface mode. Because we favor +%D {\sl slanted} type over {\it italic}, we made the emphasis +%D adaptable, for instance: +%D +%D \starttyping +%D \def\emphasistypeface {\it} +%D \def\emphasisboldface {\bi} +%D \stoptyping +%D +%D But we prefer: + +\def\emphasistypeface {\sl} +\def\emphasisboldface {\bs} + +%D or even better: + +% \def\doemphasistypeface#1#2% +% {\doifelsevalue{\??ft\fontclass\normalizedbodyfontsize\c!em}\v!slanted#1% +% {\doifelsevalue{\??ft\fontclass\normalizedbodyfontsize\c!em}\v!italic #2% +% {\doifelsevalue{\??ft \normalizedbodyfontsize\c!em}\v!slanted#1% +% {\doifvalue {\??ft \normalizedbodyfontsize\c!em}\v!italic #2}}}} + +\def\doemphasistypeface#1#2% + {\doifelsevalue{\??ft\fontclass\normalizedbodyfontsize\c!em}\v!slanted + {#1}% + {\doifelsevalue{\??ft\fontclass\normalizedbodyfontsize\c!em}\v!italic + {#2}% + {\doifelsevaluenothing{\??ft\fontclass\normalizedbodyfontsize\c!em} + {\doifelsevalue{\??ft\normalizedbodyfontsize\c!em}\v!slanted + {#1}% + {\doifelsevalue{\??ft\normalizedbodyfontsize\c!em}\v!italic + {#2}% + {\getvalue{\??ft\normalizedbodyfontsize\c!em}}}} + {\getvalue{\??ft\fontclass\normalizedbodyfontsize\c!em}}}}} + +\def\emphasistypeface{\doemphasistypeface\sl\it} +\def\emphasisboldface{\doemphasistypeface\bs\bi} + +%D To be set with the default body font environment: \type +%D {em} being \type {slanted} or \type {italic}. + +\newconditional\emneeded + +\newtoks\everyemphasized + +\unexpanded\def\em + {\relax + \ifdim\slantperpoint>\zeropoint + \settrue\emneeded + \else + \setfalse\emneeded + \fi + \setemphasisboldface % new + \ifx\fontalternative\c!it % \ifnum\fam=\itfam + \def\emphasistypeface{\it}\tf + \else\ifx\fontalternative\c!sl % \ifnum\fam=\slfam + \def\emphasistypeface{\sl}\tf + \else\ifx\fontalternative\c!bf % \ifnum\fam=\bffam + \emphasisboldface + \else\ifx\fontalternative\c!bs % \ifnum\fam=\bsfam + \def\emphasisboldface{\bs}\bf + \else\ifx\fontalternative\c!bi % \ifnum\fam=\bifam + \def\emphasisboldface{\bi}\bf + \else + \emphasistypeface + \fi\fi\fi\fi\fi + \the\everyemphasized + \ifconditional\emneeded\relax + \else + \expandafter\aftergroup + \fi + \emphasiscorrection} + +% compare ... +% +% \appendtoks \red \to \everyemphasized +% \setupbodyfontenvironment [default] [em={\italic\color[red]}] + +%D The next feature was not present in previous versions. It +%D takes care of \type {\em \bf ...} sitiations. + +\def\setemphasisboldface + {\let\normalbf\bf + \let\setemphasisboldface\relax + \unexpanded\def\bf + {%\relax + \let\bf\relax % new + \ifx\fontalternative\c!it % \ifnum\fam=\itfam + \bi + \else\ifx\fontalternative\c!sl % \ifnum\fam=\slfam + \bs + \else + \normalbf + \fi\fi + \let\bf\normalbf}} + +%D Donald's (adapted) macros take the next character into +%D account when placing italic correction. As a bonus we also +%D look for something that looks like a dash, in which case we +%D don't correct. + +\let\italiccorrection=\/ + +\def\emphasiscorrection + {\ifhmode + \expandafter\emphasislook + \fi} + +\def\emphasislook + {\begingroup + \beginrobusttest + \futurelet\next\emphasistest} + +\def\emphasistest + {\normalifcat\noexpand\next,% + \endrobusttest\expandafter\doemphasiscorrection + \normalelse + \endrobusttest\expandafter\dododoemphasiscorrection + \normalfi} + +\def\doemphasiscorrection + {\futurelet\next\dodoemphasiscorrection} + +\def\dodoemphasiscorrection + {\setbox\scratchbox\hbox{\next}% + \ifdim\ht\scratchbox=\zeropoint % probably a space + \expandafter\dododoemphasiscorrection + \else\ifdim\ht\scratchbox<.3ex + \expandafter\expandafter\expandafter\endgroup + \else + \expandafter\expandafter\expandafter\dododoemphasiscorrection + \fi\fi} + +\def\dododoemphasiscorrection + {\scratchskip\lastskip + \ifdim\scratchskip=\zeropoint\relax % == \ifzeropt\scratchskip + \italiccorrection\relax + \else + \unskip\italiccorrection\hskip\scratchskip + \fi + \endgroup} + +%D We end with some examples which show the behavior when +%D some punctuation is met. We also show how the mechanism +%D adapts itself to bold, italic and slanted typing. +%D +%D \startbuffer +%D test {test}test \par +%D test {\sl test}test \par +%D test {\em test}test \par +%D test {\em test}--test \par +%D +%D test {test}, test \par +%D test {\em test}, test \par +%D +%D test {\em test {\em test {\em test} test} test} test \par +%D test {\bf test {\em test {\em test} test} test} test \par +%D test {\sl test {\em test {\em test} test} test} test \par +%D test {\it test {\em test {\em test} test} test} test \par +%D \stopbuffer +%D +%D \typebuffer +%D +%D We get: +%D +%D \startvoorbeeld +%D \startpacked +%D \getbuffer +%D \stoppacked +%D \stopvoorbeeld + +%D \macros +%D {emphbf,emphit,emphsl,emphtf} +%D +%D The next emphasis alternatives are for \THANH. They adapt +%D their style as good as possible. + +\def\emphbf{\groupedcommand{\bf\def\emphit{\bi}\def\emphsl{\bs}}{}} +\def\emphit{\groupedcommand{\it\def\emphbf{\bi}\def\emphsl{\sl}}{}} +\def\emphsl{\groupedcommand{\sl\def\emphbf{\bs}\def\emphit{\it}}{}} +\def\emphtf{\groupedcommand{\tf\def\emphbf{\bf}\def\emphit{\it}\def\emphsl{\sl}}{}} + +%D \startbuffer +%D TEXT {\emphbf text \emphit text \emphtf text \emphsl text} TEXT +%D TEXT \emphbf{text \emphit{text} \emphtf{text} \emphsl{text}} TEXT +%D \stopbuffer +%D +%D \typebuffer +%D \startlines +%D \getbuffer +%D \stoplines + +%D \macros +%D {setfont} +%D +%D Every now and then we want to define a font directly, for +%D instance when we typeset title pages. The next macro saves +%D some typing: + +\def\setfont% geen \dosetfont mogelijk + {\def\next{\nextfont\setupinterlinespace}% + \afterassignment\next\font\nextfont=} + +%D One can call this macro as: +%D +%D \starttyping +%D \setfont cmr10 at 60pt +%D \stoptyping +%D +%D After which the font is active and the baselines and +%D struts are set. + +%D \macros +%D {showbodyfont} +%D +%D One can call for a rather simple overview of a bodyfont and the +%D relations between its alternative fonts. +%D +%D \showsetup{showbodyfont} +%D +%D The current bodyfont (here we omitted the argument) looks like: +%D +%D \showbodyfont +%D +%D The implementation is rather straightforward in using +%D \type{\halign}. + +\fetchruntimecommand \showbodyfont {\f!fontprefix\s!run.mkii} + +%D \macros +%D {showfontstrip, testminimalbaseline, showminimalbaseline} +%D +%D The next command can come in handy when combining +%D different fonts into a collection (typeface) and +%D determining optimal baseline distances. +%D +%D \showfontstrip \blank \showminimalbaseline + +\fetchruntimecommand \showfontstrip {\f!fontprefix\s!run.mkii} +\fetchruntimecommand \testminimalbaseline {\f!fontprefix\s!run.mkii} +\fetchruntimecommand \showminimalbaseline {\f!fontprefix\s!run.mkii} + +%D \macros +%D {showkerning} +%D +%D A goody is: +%D +%D \showkerning{Can you guess what kerning is?} + +\fetchruntimecommand \showkerning {\f!fontprefix\s!run.mkii} + +%D \macros +%D {showbodyfontenvironment} +%D +%D The current bodyfontenvironment is: +%D +%D \showbodyfontenvironment +%D +%D This overview is generated using: +%D +%D \showsetup{showbodyfontenvironment} + +\fetchruntimecommand \showbodyfontenvironment {\f!fontprefix\s!run.mkii} + +%D \macros +%D {showfont,showfontstyle,showligatures} +%D +%D The following command generates a fontmap: +%D +%D \startbuffer +%D \showfont[SansBold at 12pt] +%D \stopbuffer +%D +%D \typebuffer +%D +%D Below the table the name, encoding, mapping and handling are +%D shown. Special characters like the \type {\skewchar} and +%D \type {\hyphenchar} als marked. +%D +%D \getbuffer + +% to be internationalized + +\fetchruntimecommand \showfont {\f!fontprefix\s!run.mkii} +\fetchruntimecommand \showfontstyle {\f!fontprefix\s!run.mkii} +\fetchruntimecommand \showligature {\f!fontprefix\s!run.mkii} +\fetchruntimecommand \showligatures {\f!fontprefix\s!run.mkii} +\fetchruntimecommand \showcharratio {\f!fontprefix\s!run.mkii} + +%D \macros +%D {getglyph, symbolicfont} +%D +%D Individual glyphs can be accessed by using +%D +%D \starttyping +%D \getglyph{fontname}{character} +%D \stoptyping +%D +%D This macro is used in for instance the symbol modules and +%D as one can see, it does obey the small and even smaller +%D sizes. The \type {\symbolicfont} macro can be used to +%D switch to a font named \type {fontname} (see \type +%D {cont-log} and \type {symb-eur} for examples of symbolic +%D definitions. + +\def\fontstringA + {\ifx\fontstyle\c!rm \s!Serif \else + \ifx\fontstyle\c!ss \s!Sans \else + \ifx\fontstyle\c!tt \s!Mono \else + \s!Serif \fi\fi\fi} + +\def\fontstringB + {\ifx\fontstyle\c!rm \s!Regular \else + \ifx\fontstyle\c!ss \s!Support \else + \ifx\fontstyle\c!tt \s!Type \else + \s!Serif \fi\fi\fi} + +\def\fontstringC + {\ifx\fontalternative\c!bf \s!Bold \else + \ifx\fontalternative\c!sl \s!Slanted \else + \ifx\fontalternative\c!it \s!Italic \else + \ifx\fontalternative\c!bs \s!BoldSlanted \else + \ifx\fontalternative\c!bi \s!BoldItalic \fi\fi\fi\fi\fi} + +\def\fontstringD % default fontstyle + {\expandafter\ifx\csname\??tf\fontclass\s!default\endcsname\c!rm \s!Serif \else + \expandafter\ifx\csname\??tf\fontclass\s!default\endcsname\c!ss \s!Sans \else + \expandafter\ifx\csname\??tf\fontclass\s!default\endcsname\c!tt \s!Mono \else + \s!Serif \fi\fi\fi} + +% potential generalization: + +% \letvalue{\??ff:t:\c!rm}\s!Serif +% \letvalue{\??ff:t:\c!ss}\s!Sans +% \letvalue{\??ff:t:\c!tt}\s!Mono +% +% \letvalue{\??ff:s:\c!bf}\s!Bold +% \letvalue{\??ff:s:\c!sl}\s!Slanted +% \letvalue{\??ff:s:\c!it}\s!Italic +% \letvalue{\??ff:s:\c!bs}\s!BoldSlanted +% \letvalue{\??ff:s:\c!bi}\s!BoldItalic +% +% \letvalue{\??ff:a:\c!rm}\s!Regular +% \letvalue{\??ff:a:\c!ss}\s!Support +% \letvalue{\??ff:a:\c!tt}\s!Type +% +% \def\fontstringA{\executeifdefined{\??ff:t:\fontstyle}\s!Serif} +% \def\fontstringB{\executeifdefined{\??ff:a:\fontstyle}\s!Serif} +% \def\fontstringC{\executeifdefined{\??ff:s:\fontstyle}\empty} +% \def\fontstringD{\executeifdefined{\??ff:t:\csname\??tf\fontclass\s!default\endcsname}\s!Serif} + +\def\glyphfontfile#1% + {#1% + \ifcsname\??ff#1\fontstringA\fontstringC\endcsname + \fontstringA\fontstringC + \else\ifcsname\??ff#1\fontstringB\fontstringC\endcsname + \fontstringB\fontstringC + \else\ifcsname\??ff#1\fontstringA\endcsname + \fontstringA + \else\ifcsname\??ff#1\fontstringB\endcsname + \fontstringB + \else\ifcsname\??ff#1\fontstringC\endcsname + \fontstringC + \fi\fi\fi\fi\fi} + +%D The next macro can be used to make decisions based on the shape: + +\def\doifitalicelse#1#2% + {\ifx\fontalternative\c!sl#1\else + \ifx\fontalternative\c!it#1\else + \ifx\fontalternative\c!bs#1\else + \ifx\fontalternative\c!bi#1\else#2\fi\fi\fi\fi} + +%D For an example of usage of the following command, +%D see \type {cont-log.tex}. +%D +%D \starttyping +%D \def\symbolicfont#1% +%D {\definedfont[\glyphfontfile{#1} sa *]} +%D \stoptyping +%D +%D Since we know what scaling it to be applied, we can +%D implement a much faster alternative: + +\let\thedefinedfont\relax + +% \def\symbolicscaledfont#1#2% +% {\scaledfont\fontbody +% \scaledfont#1\scaledfont +% \font\thedefinedfont\truefontname{\glyphfontfile{#2}} at +% \currentfontbodyscale\scaledfont +% \thedefinedfont} +% +% \unexpanded\def\symbolicfont +% {\symbolicscaledfont\plusone} +% +% even more control (needed for mthsqrt etc) + +\def\symbolicsizedfont#1#2#3% + {\scaledfont#1% + \scaledfont#2\scaledfont + \font\thedefinedfont\truefontname{\glyphfontfile{#3}} at + \currentfontbodyscale\scaledfont + \thedefinedfont} + +\def\symbolicscaledfont + {\symbolicsizedfont\fontbody} + +\unexpanded\def\symbolicfont + {\symbolicsizedfont\fontbody\plusone} + +\unexpanded\def\getglyph#1#2% slow, faster, much faster + %{{\definefont[\s!dummy][\glyphfontfile{#1} sa \currentfontscale]\dummy#2}} + %{{\definefont[\s!dummy][\glyphfontfile{#1} sa *]\dummy#2}} + %{{\symbolicfont{#1}#2}} + {{\symbolicfont{#1}\doifnumberelse{#2}\char\donothing#2}} + +\unexpanded\def\getscaledglyph#1#2#3% + {{\symbolicscaledfont{#1}{#2}\doifnumberelse{#3}\char\donothing#3}} + +\unexpanded\def\getrawglyph#1#2% for simple symbols + {{\scaledfont\fontbody + \font\thedefinedfont=#1 at \currentfontbodyscale\scaledfont + \thedefinedfont\doifnumberelse{#2}\char\donothing#2}} + +%D The last implementation of \type {\getglyph} permits +%D definitions like: +%D +%D \starttyping +%D \definefontsynonym [EuroSans] [eurose] +%D \definefontsynonym [EuroSansBold] [euroseb] +%D \definefontsynonym [EuroSansItalic] [eurosei] +%D \definefontsynonym [EuroSansSlanted] [eurosei] +%D \definefontsynonym [EuroSansBoldItalic] [eurosebi] +%D \definefontsynonym [EuroSansBoldSlanted] [eurosebi] +%D +%D \definesymbol [euro] [\getglyph{Euro}{\char160}] +%D +%D \def\euro{\symbol[euro]} +%D \stoptyping +%D +%D These definitions guarantee that the next calls work okay: +%D +%D \starttyping +%D \ss \tf\euro \bf\euro \sla\euro \itd\euro \bs\euro \bic\euro +%D \stoptyping +%D +%D The shape as well as the size is adapted to the current +%D environment. + +%D Fonts can only be used when loaded. In \CONTEXT\ we +%D postpone the loading of fonts, even when we load \PLAIN. +%D This means that we have to redefine one of the \PLAIN\ +%D macros. Let's tell that to the user first: + +\writestatus{loading}{Postponed Plain TeX Font Definitions} + +%D \macros +%D {bordermatrix} +%D +%D In \PLAIN\ \TEX\ the width of a parenthesis is stored in +%D the \DIMENSION\ \type{\mathparentwd}. This value is derived from +%D the width of \type{\tenrm B}, so let's take care of it now: + +\let\normalbordermatrix=\bordermatrix + +\def\bordermatrix% + {\bgroup + \setbox0\hbox{\getvalue{\textface\c!mm\c!ex}B}% + \global\mathparentwd\wd0\relax + \egroup + \normalbordermatrix} + +%D Because we want to be as \PLAIN\ compatible as possible, we +%D make most of \PLAIN's font mechanisme available to the +%D \CONTEXT\ user. + +\def\setplainfonts#1#2% + {\setvalue {ten#1}{\getvalue{\!!tenpoint #2}}% + \setvalue{seven#1}{\getvalue{\!!sevenpoint#2}}% + \setvalue {five#1}{\getvalue{\!!fivepoint #2}}} + +\setplainfonts {\c!rm} {\c!rm\c!tf} +\setplainfonts {\c!bf} {\c!rm\c!bf} +\setplainfonts {\c!sl} {\c!rm\c!sl} +\setplainfonts {\c!it} {\c!rm\c!it} +\setplainfonts {\c!tt} {\c!rm\c!tt} +\setplainfonts {\c!sy} {\c!mm\c!sy} +\setplainfonts {\c!ex} {\c!mm\c!ex} +\setplainfonts {\c!i} {\c!mm\c!mi} + +\let\setplainfonts=\undefined + +%D \macros +%D {ss, SS, sz} +%D +%D We are going to redefine \type{\ss} but for those wo still +%D want to have access to the german \SS, we save it's value in +%D \type{\SS}. Ok, I should have used \type{\sf} instead of +%D \type{\ss} in the first place. + +\ifx\undefined\SS \let\SS=\ss \fi +\ifx\undefined\sz \let\sz=\ss \fi + +%D \macros +%D {xi} +%D +%D We are going to redefine \type{\xi}, but fortunately this +%D is a math mode character, so we can just say: + +\let\normalxi=\xi + +%D \macros +%D {smashaccent} +%D +%D When we let \TEX\ put an accent on top of a character, such +%D composed characters can get more height that height of a +%D standard \type{\strut}. The next macro takes care of such +%D unwanted compositions. +%D +%D We need to reach over the number that specifies the accent, +%D and in doing so we use \type{\scratchcounter} as a placeholder +%D because it accepts 8 bit numbers in octal, decimal or +%D hexadecimal format. Next we set the height of the accented +%D character to the natural height of the character. + +\unexpanded\def\smashaccent#1% + {\dontleavehmode + \bgroup + \setbox\scratchbox\hbox{#1}% + \ifdim\ht\scratchbox>\strutheight\relax\ht\scratchbox\strutheight\fi + \ifdim\dp\scratchbox>\strutdepth \relax\dp\scratchbox\strutdepth \fi + \box\scratchbox + \egroup} + +%D For instance we can say: +%D +%D \starttyping +%D \smashaccent{\"Uberhaupt} +%D \stoptyping +%D +%D But normally one will use it as a prefix in definitions. +%D The difference is in the height: +%D +%D \leavevmode\ruledhbox +%D {\ruledhbox{\smashaccent{\"U}berhaupt}\quad +%D oder\quad +%D \ruledhbox{\"Uberhaupt}} + +%D \macros +%D {moveaccent} +%D +%D Exact positioning of accents can be realized by saying: +%D +%D \starttyping +%D \moveaccent{-.1ex}{\"u}berhaupt +%D \stoptyping +%D +%D Again, this one will mostly used as a prefix in definitions. +%D Here the difference is in the position: +%D +%D \leavevmode\ruledhbox +%D {\ruledhbox{\moveaccent{-.1ex}{\"}Uberhaupt}\quad +%D oder\quad +%D \ruledhbox{\"Uberhaupt}} + +\unexpanded\def\moveaccent#1#2% + {\smashaccent + {\dimen0\exheight + \dimen2\dimen0 + \advance\dimen2 -#1% + \exheight\dimen2 + #2\relax + \exheight\dimen0}} + +%D Personally I think that using \TEX\ is complicated by the +%D way fonts are handled. Apart from the many encodings, we +%D also deal with different naming schemes. Confronted with +%D this problem, I decided to change the definitions into: +%D +%D \starttyping +%D \definebodyfont [12pt] [rm] [tf=Times-Roman at 12pt] +%D \stoptyping +%D +%D combined with for instance: +%D +%D \starttyping +%D \definefontsynonym [Times-Roman] [tir] +%D \stoptyping + +%D Now we're up to some definitions. + +\definebodyfontenvironment + [\s!default] + [ \s!text=1.0, + \s!script=0.7, + \s!scriptscript=0.5, + \c!a=1.200, + \c!b=1.440, + \c!c=1.728, + \c!d=2.074, + *=\currentfontscale, % wildcard + \c!x=0.8, + \c!xx=0.6, + \c!big=1.2, + \c!small=0.8, + \c!interlinespace=, + \c!em=\v!slanted] + +\definebodyfontenvironment + [20.7pt] + [ \s!text=20.7pt, + \s!script=\!!fourteenpointfour, + \s!scriptscript=\!!twelvepoint, + \c!x=17.3pt, + \c!xx=\!!fourteenpointfour, + \c!big=20.7pt, % !!!! + \c!small=17.3pt] + +\definebodyfontenvironment + [17.3pt] + [ \s!text=17.3pt, + \s!script=\!!twelvepoint, + \s!scriptscript=\!!tenpoint, + \c!x=\!!fourteenpointfour, + \c!xx=\!!twelvepoint, + \c!big=20.7pt, + \c!small=\!!fourteenpointfour] + +\definebodyfontenvironment + [\!!fourteenpointfour] + [ \s!text=\!!fourteenpointfour, + \s!script=\!!elevenpoint, + \s!scriptscript=\!!ninepoint, + \c!x=\!!twelvepoint, + \c!xx=\!!tenpoint, + \c!big=17.3pt, + \c!small=\!!twelvepoint] + +\definebodyfontenvironment + [\!!twelvepoint] + [ \s!text=\!!twelvepoint, + \s!script=\!!ninepoint, + \s!scriptscript=\!!sevenpoint, + \c!x=\!!tenpoint, + \c!xx=\!!eightpoint, + \c!big=\!!fourteenpointfour, + \c!small=\!!tenpoint] + +\definebodyfontenvironment + [\!!elevenpoint] + [ \s!text=\!!elevenpoint, + \s!script=\!!eightpoint, + \s!scriptscript=\!!sixpoint, + \c!x=\!!ninepoint, + \c!xx=\!!sevenpoint, + \c!big=\!!twelvepoint, + \c!small=\!!ninepoint] + +\definebodyfontenvironment + [\!!tenpoint] + [ \s!text=\!!tenpoint, + \s!script=\!!sevenpoint, + \s!scriptscript=\!!fivepoint, + \c!x=\!!eightpoint, + \c!xx=\!!sixpoint, + \c!big=\!!twelvepoint, + \c!small=\!!eightpoint] + +\definebodyfontenvironment + [\!!ninepoint] + [ \s!text=\!!ninepoint, + \s!script=\!!sevenpoint, + \s!scriptscript=\!!fivepoint, + \c!x=\!!sevenpoint, + \c!xx=\!!fivepoint, + \c!big=\!!elevenpoint, + \c!small=\!!sevenpoint] + +\definebodyfontenvironment + [\!!eightpoint] + [ \s!text=\!!eightpoint, + \s!script=\!!sixpoint, + \s!scriptscript=\!!fivepoint, + \c!x=\!!sixpoint, + \c!xx=\!!fivepoint, + \c!big=\!!tenpoint, + \c!small=\!!sixpoint] + +\definebodyfontenvironment + [\!!sevenpoint] + [ \s!text=\!!sevenpoint, + \s!script=\!!sixpoint, + \s!scriptscript=\!!fivepoint, + \c!x=\!!sixpoint, + \c!xx=\!!fivepoint, + \c!big=\!!ninepoint, + \c!small=\!!fivepoint] + +\definebodyfontenvironment + [\!!sixpoint] + [ \s!text=\!!sixpoint, + \s!script=\!!fivepoint, + \s!scriptscript=\!!fivepoint, + \c!x=\!!fivepoint, + \c!xx=\!!fivepoint, + \c!big=\!!eightpoint, + \c!small=\!!fivepoint] + +\definebodyfontenvironment + [\!!fivepoint] + [ \s!text=\!!fivepoint, + \s!script=\!!fivepoint, + \s!scriptscript=\!!fivepoint, + \c!x=\!!fivepoint, + \c!xx=\!!fivepoint, + \c!big=\!!sevenpoint, + \c!small=\!!fivepoint] + +\definebodyfontenvironment + [\!!fourpoint] + [ \s!text=\!!fourpoint, + \s!script=\!!fourpoint, + \s!scriptscript=\!!fourpoint, + \c!x=\!!fourpoint, + \c!xx=\!!fourpoint, + \c!big=\!!sixpoint, + \c!small=\!!fourpoint] + +\definebodyfontswitch [fourteenpointfour] [\!!fourteenpointfour] +\definebodyfontswitch [twelvepoint] [\!!twelvepoint] +\definebodyfontswitch [elevenpoint] [\!!elevenpoint] +\definebodyfontswitch [tenpoint] [\!!tenpoint] +\definebodyfontswitch [ninepoint] [\!!ninepoint] +\definebodyfontswitch [eightpoint] [\!!eightpoint] +\definebodyfontswitch [sevenpoint] [\!!sevenpoint] +\definebodyfontswitch [sixpoint] [\!!sixpoint] +\definebodyfontswitch [fivepoint] [\!!fivepoint] +\definebodyfontswitch [fourpoint] [\!!fourpoint] + +\definebodyfontswitch [xii] [\!!twelvepoint] +\definebodyfontswitch [xi] [\!!elevenpoint] +\definebodyfontswitch [x] [\!!tenpoint] +\definebodyfontswitch [ix] [\!!ninepoint] +\definebodyfontswitch [viii] [\!!eightpoint] +\definebodyfontswitch [vii] [\!!sevenpoint] +\definebodyfontswitch [vi] [\!!sixpoint] + +%D So far. + +\definefontstyle [\c!mm] [\c!mm] +\definefontstyle [\c!rm,\v!roman,\v!serif,\v!regular] [\c!rm] +\definefontstyle [\c!ss,\v!sansserif,\v!sans,\v!support] [\c!ss] +\definefontstyle [\c!tt,\v!teletype,\v!type,\v!mono] [\c!tt] +\definefontstyle [\c!hw,\v!handwritten] [\c!hw] +\definefontstyle [\c!cg,\v!calligraphic] [\c!cg] + +\definefontsize[\c!a] \definefontsize[\c!b] +\definefontsize[\c!c] \definefontsize[\c!d] + +\definealternativestyle [\v!mediaeval] [\os] [] +\definealternativestyle [\v!normal] [\tf] [] +\definealternativestyle [\v!bold] [\bf] [] +\definealternativestyle [\v!type] [\tt] [] +\definealternativestyle [\v!mono] [\tt] [] +\definealternativestyle [\v!slanted] [\sl] [] +\definealternativestyle [\v!italic] [\it] [] +\definealternativestyle [\v!boldslanted,\v!slantedbold] [\bs] [] +\definealternativestyle [\v!bolditalic,\v!italicbold] [\bi] [] +\definealternativestyle [\v!small,\v!smallnormal] [\tfx] [] +\definealternativestyle [\v!smallbold] [\bfx] [] +\definealternativestyle [\v!smalltype] [\ttx] [] +\definealternativestyle [\v!smallslanted] [\slx] [] +\definealternativestyle [\v!smallboldslanted,\v!smallslantedbold] [\bsx] [] +\definealternativestyle [\v!smallbolditalic,\v!smallitalicbold] [\bix] [] + +\definealternativestyle [\v!sans,\v!sansserif] [\ss] [] +\definealternativestyle [\v!sansbold] [\ss\bf] [] + +%D Slow but handy: + +\definealternativestyle [\v!smallbodyfont] [\setsmallbodyfont] [] +\definealternativestyle [\v!bigbodyfont] [\setbigbodyfont] [] + +%D We treat {\sc Small Caps} and \cap {Pseudo Caps} a bit +%D different. We also provide an \WORD {uppercase} style. + +\definealternativestyle [\v!cap,\v!capital] [\smallcapped] [\smallcapped] +\definealternativestyle [\v!smallcaps] [\sc] [\sc] +\definealternativestyle [\v!WORD] [\WORD] [\WORD] + +%D \macros +%D {...math} +%D +%D New or old? + +% tzt proper \define... +% +% watch out: \synchronizesymb resets the family so we need a second +% \mf (or maybe \mr): messy and to be sorted out + +\def\tfmath{\tf\mf\synchronizesymb\mf} +\def\bfmath{\bf\mf\synchronizesymb\mf} +\def\slmath{\sl\mf\synchronizesymb\mf} +\def\itmath{\it\mf\synchronizesymb\mf} +\def\bsmath{\bs\mf\synchronizesymb\mf} +\def\bimath{\bi\mf\synchronizesymb\mf} +\def\scmath{\sc\mf\synchronizesymb\mf} +\def\nnmath{\nn\mf\synchronizesymb\mf} + +\def\textmath {\synchronizesymb} + +%D \macros +%D {fontstylesuffix} +%D +%D The next macro is used to map non latin fontnames on +%D fonts. See \type {font-uni} for an example of its use. + +%\def\fontstylesuffix% +% {\ifnum\fam=\tffam \s!Regular \else +% \ifnum\fam=\bffam \s!Bold \else +% \ifnum\fam=\slfam \s!Slanted \else +% \ifnum\fam=\itfam \s!Italic \else +% \ifnum\fam=\bsfam \s!BoldSlanted \else +% \ifnum\fam=\bifam \s!BoldItalic \else +% \s!Regular \fi\fi\fi\fi\fi\fi}% + +\def\fontstylesuffix% why the \s!Regular ? see \getglyph + {\ifx\fontalternative\c!tf \s!Regular \else + \ifx\fontalternative\c!bf \s!Bold \else + \ifx\fontalternative\c!sl \s!Slanted \else + \ifx\fontalternative\c!it \s!Italic \else + \ifx\fontalternative\c!bs \s!BoldSlanted \else + \ifx\fontalternative\c!bi \s!BoldItalic \else + \ifx\fontalternative\c!sc \s!Caps \else + \s!Regular \fi\fi\fi\fi\fi\fi\fi}% + +%D We still have to take care of \type{\xi}, so: + +\def\xi{\ifmmode\normalxi\else\elevenpoint\fi} + +%D \macros +%D {definefontvariant,fontvariant,variant} +%D +%D A slightly adapted version of Adam Lindsays variant patches: +%D +%D \starttyping +%D \usetypescriptfile[type-psc] \loadmapfile[texnansi-public-fpl] +%D \usetypescript[palatino][texnansi] \setupbodyfont[palatino] +%D +%D \definefontvariant [Serif][osf] [OsF] +%D +%D \startlines +%D {\sc abcdefgHIJKlmnop} +%D 123{\Var[osf]456}789 +%D {\Var[osf] 123{\Var[reset]456}789} +%D {\it 123{\Var[osf]456}789 +%D {\Var[osf]123{\Var[reset]456}789}} +%D {\tfb\bf 123{\Var[osf]456}789 +%D {\Var[osf] 123{\Var[reset]456}789}} +%D {\sc 123{\Var[osf]456}789 +%D {\Var[osf] 123{\Var[reset]456}789}} +%D \stoplines +%D \stoptyping + +\def\definefontvariant + {\dotripleargument\dodefinefontvariant} + +\def\dodefinefontvariant[#1][#2][#3]% + {\setvalue{\??fv#1#2}{#3}} + +\def\fontvariant#1#2{\executeifdefined{\??fv#1#2}\empty} + +\def\dosetscaledfont + {\checkrelativefontsize\fontstyle + \scaledfont\currentfontscale\bodyfontsize + \scaledfont\relativefontsize\scaledfont} + +\unexpanded\def\variant[#1]% slow + {\dosetscaledfont + \expanded{\definedfont + [\truefontname{\fontstringA\fontstylesuffix\fontvariant\fontstringA{#1}} + at \scaledfont]}% + \ignoreimplicitspaces} + +\ifx\Var\undefined \let\Var\variant \fi + +%D By default we load the Computer Modern Roman fonts (but +%D not yet at this moment) and activate the 12pt roman +%D bodyfont. Sans serif and teletype are also available and +%D can be called for by \type{\ss} and \type{\tt}. + +% \setupbodyfont [unk, rm] + +%D Also needed is: + +\definefont[tinyfont][Mono at 1ex] + +%D \macros +%D {doiffontpresentelse} +%D +%D Some unused left||overs: +%D +%D \starttyping +%D \def\doiffontpresentelse#1#2#3% +%D {\bgroup +%D \batchmode\font\dummy=\truefontname{#1}\errorstopmode +%D \edef\lastfontname{\fontname\dummy}% +%D \ifx\lastfontname\nullfontname\egroup#3\else\egroup#2\fi} +%D +%D \def\abortiffontnotfound#1% +%D {\doiffontpresentelse{#1}{}{\showmessage\m!fonts{10}{\truefontname{#1}}\endinput}} +%D \stoptyping +%D +%D We now provide (slow, but sometimes handy): +%D +%D \starttyping +%D \doiffontpresentelse{texnansi-lmr10}{YES}{NO} +%D \doiffontpresentelse{adam-lindsay-modern-serif}{YES}{NO} +%D \stoptyping + +\def\doiffontpresentelse#1% + {\bgroup + \scratchcounter\interactionmode + \batchmode + \font\dummy=\truefontname{#1}\relax + \interactionmode\scratchcounter + \edef\lastfontname{\fontname\dummy}% + \ifx\lastfontname\nullfontname + \egroup\expandafter\secondoftwoarguments + \else + \egroup\expandafter\firstoftwoarguments + \fi} + +%D New commands (not yet interfaced): + +\def\style[#1]% for inline usage, like \color + {\groupedcommand{\ifcsname#1\endcsname\csname#1\endcsname\else\definedfont[#1]\fi}{}} + +\def\startstyle[#1]% + {\begingroup + \ifcsname#1\endcsname\csname#1\endcsname\else\definedfont[#1]\fi} + +\def\stopstyle + {\endgroup} + +%D Still experimental (might even go away). + +% \definestylecollection[mine] + +% \definestyleinstance[mine][default][sorry] +% \definestyleinstance[mine][tt][bs][ttbs:\rm\sl] +% \definestyleinstance[mine][tt][bf][ttbf:\rm\sl] +% \definestyleinstance[mine][bf][\sl] +% \definestyleinstance[mine][sl][\tt] + +% {\bf test \mine test \sl test \mine test \bs oeps \mine oeps {\tt test \mine \bf test}} + +\definesystemvariable{sx} + +\def\definestylecollection + {\dosingleargument\dodefinestylecollection} + +\def\dodefinestylecollection[#1]% + {\iffirstargument + \unexpanded\setvalue{#1}{\styleinstance[#1]}% + \def\docommand##1% + {\def\dodocommand####1{\letbeundefined{\??sx##1:####1:\commalistelement}}% + \processcommacommand[\fontalternativelist,\s!default]\dodocommand}% + \processcommacommand[\fontstylelist,\s!default]\docommand + \fi} + +\def\definestyleinstance + {\doquadrupleargument\dodefinestyleinstance} + +\def\dodefinestyleinstance[#1][#2][#3][#4]% [name] [rm|ss|tt|..] [sl|bf|...] [whatever] + {\iffirstargument + \doifundefined{#1}{\definestylecollection[#1]}% + \fi + \iffourthargument + \setvalue{\??sx#1:#2:#3}{#4}% + \else\ifthirdargument + \setvalue{\??sx#1::#2}{#3}% + \else\ifsecondargument + \letvalue{\??sx#1::#2}\empty + \fi\fi\fi} + +\unexpanded\def\styleinstance[#1]% will be faster + {%\begingroup\expanded{\infofont[#1:\fontstyle:\fontalternative]}\endgroup + \executeifdefined{\??sx#1:\fontstyle:\fontalternative}% + {\executeifdefined{\??sx#1:\fontstyle:\s!default}% + {\executeifdefined{\??sx#1::\fontalternative} + {\getvalue {\??sx#1::\s!default}}}}} + +% \unexpanded\def\styleinstance[#1]% +% {\csname\??sx#1% +% \ifcsname:\fontstyle:\fontalternative\endcsname +% :\fontstyle:\fontalternative +% \else\ifcsname:\fontstyle:\s!default\endcsname +% :\fontstyle:\s!default +% \else\ifcsname::\fontalternative\endcsname +% ::\fontalternative +% \else\ifcsname::\s!default\endcsname +% ::\s!default +% \else +% % nothing, \relax +% \fi\fi\fi\fi +% \endcsname} + +%D \Compatibility with \MKIV: + +\def\somefontsize{\scaledfont} + +\protect \endinput diff --git a/tex/context/base/font-ini.mkiv b/tex/context/base/font-ini.mkiv new file mode 100644 index 000000000..c7d515cca --- /dev/null +++ b/tex/context/base/font-ini.mkiv @@ -0,0 +1,3896 @@ +%D \module +%D [ file=font-ini, +%D version=1998.09.11, % (second) +%D version=2001.02.20, % (third) +%D title=\CONTEXT\ Font Macros, +%D subtitle=Initialization, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +% runtime commands will move to modules + +% simplification ... we no longer deal with specific mmtfa specifications + +% todo: always fontclass, then less testing + +% \definefontfeature[smallcaps][smcp=yes,script=latn] +% \definefontfeature[smallcaps][SmallCapitals=yes,script=latn] +% \definefontfeature[smallcaps][Small Capitals=yes,script=latn] +% \definefontfeature[smallcaps][small capitals=yes,script=latn] +% \definefontfeature[smallcaps][smallcapitals=yes,script=latn] +% +% \definedfont[cambria*smallcaps] test + +% \starttext +% \definefontfeature[basekerned][default][mode=base] +% \definefontfeature[nodekerned][default][mode=node] +% \definefontfeature[nonekerned][default][mode=base,kern=no] +% \setupcolors[state=start] +% \startoverlay +% {\vbox{\red \definedfont[Serif*nonekerned at 12pt]\input tufte }} +% {\vbox{\blue \definedfont[Serif*basekerned at 12pt]\input tufte }} +% {\vbox{\green\definedfont[Serif*nodekerned at 12pt]\input tufte }} +% \stopoverlay +% \stoptext + +% \enabletrackers[otf.kerns] +% +% \definefontfeature[withkern][default][mode=node] +% \definefontfeature[nokern] [default][mode=node,kern=no] +% \definefontfeature[single] [default][mode=node,cpsp=yes] +% \definefontfeature[simple] [default][mode=node,cpsp=yes,kern=no] +% +% {\definedfont[Serif*default] [FGFGFGFGFGFGFGFGFGFGFGFGFG ABCDEFGHIJKLMNOPQRSTUVWXYZ] \par} +% {\definedfont[Serif*nokern] [FGFGFGFGFGFGFGFGFGFGFGFGFG ABCDEFGHIJKLMNOPQRSTUVWXYZ] \par} +% {\definedfont[Serif*single] [FGFGFGFGFGFGFGFGFGFGFGFGFG ABCDEFGHIJKLMNOPQRSTUVWXYZ] \par} +% {\definedfont[Serif*simple] [FGFGFGFGFGFGFGFGFGFGFGFGFG ABCDEFGHIJKLMNOPQRSTUVWXYZ] \par} + +% figure out why \fontbody is not expanded + +\writestatus{loading}{ConTeXt Font Macros / Initialization} + +\registerctxluafile{font-ini}{1.001} +\registerctxluafile{node-fnt}{1.001} % here +\registerctxluafile{font-enc}{1.001} +\registerctxluafile{font-map}{1.001} +\registerctxluafile{font-syn}{1.001} +\registerctxluafile{font-log}{1.001} +\registerctxluafile{font-tfm}{1.001} +\registerctxluafile{font-enh}{1.001} +\registerctxluafile{font-afm}{1.001} +\registerctxluafile{font-cid}{1.001} % cid maps +\registerctxluafile{font-ott}{1.001} % otf tables +\registerctxluafile{font-otf}{1.001} % otf main +\registerctxluafile{font-otd}{1.001} % otf dynamics +\registerctxluafile{font-oti}{1.001} % otf initialization +\registerctxluafile{font-otb}{1.001} % otf main base +\registerctxluafile{font-otn}{1.001} % otf main node +\registerctxluafile{font-ota}{1.001} % otf analyzers +\registerctxluafile{font-otp}{1.001} % otf pack +\registerctxluafile{font-otc}{1.001} % otf context +\registerctxluafile{font-vf} {1.001} +\registerctxluafile{font-def}{1.001} +\registerctxluafile{font-ctx}{1.001} +\registerctxluafile{font-xtx}{1.001} +\registerctxluafile{font-fbk}{1.001} +\registerctxluafile{font-gds}{1.001} +\registerctxluafile{font-ext}{1.001} +\registerctxluafile{font-pat}{1.001} +\registerctxluafile{font-chk}{1.001} +\registerctxluafile{font-agl}{1.001} + +\unprotect + +% \def\fontrange#1% +% {\dofontrange{#1 =\bodyfontsize}} +% +% \def\dofontrange#1% +% {\dodofontrange#1 \relax}% \fontstringA +% +% \def\dodofontrange#1 #2 % +% {\ifdim\bodyfontsize#2% +% #1\expandafter\gobbleuntilrelax +% \else +% \expandafter\dodofontrange +% \fi} +% +% \definefont +% [crap] +% [\fontrange +% {Regular <10pt +% RegularBold <12pt +% RegularSlanted <15pt +% Regular} sa 1] +% +% may be better: +% +% \definefontrange +% [crap] +% [Regular <10pt +% RegularBold <12pt +% RegularSlanted <15pt] +% [Regular sa 1] +% +% +% \dostepwiserecurse{2}{15}{1} +% {{\switchtobodyfont[\recurselevel pt]\crap test}\endgraf} + +% adapted, else wrong interlinespace + +\def\setfontparameters + {\synchronizefontsfalse + \the\everybodyfont + \synchronizefontstrue} + +% handy + +\newcounter\pushedfont + +\def\savefont + {\edef\savedfont{\the\font}% + \pushmacro\savedfont} + +\def\restorefont + {\popmacro\savedfont + \savedfont} + +\def\pushcurrentfont + {\edef\popcurrentfont + {\noexpand\def\noexpand\fontbody{\fontbody}% + \noexpand\def\noexpand\fontstyle{\fontstyle}% + \noexpand\dosetcurrentfontalternative{\fontalternative}% + \noexpand\dosetcurrentfontsize{\fontsize}% + \noexpand\synchronizefont}} + +% \definetypeface[one][rm][serif][computer-roman][default] +% \definetypeface[two][rm][serif][computer-roman][default][rscale=.9] +% +% {\one \bf test \two test} +% {\one \bf test \pushcurrentfont \two \popcurrentfont test} + +%D \macros +%D {definedfont,startfont,doiffontcharelse} +%D +%D A couple of relatively new macros: + +% \newtoks \everydefinedfont % not ot be confused with \everydefinefont + +\def\dodefinedfont[#1]% + {\iffirstargument\definefont[thedefinedfont][#1]\fi % we can speed this one up + \csname thedefinedfont\endcsname + \the\everydefinedfont} + +\unexpanded\def\definedfont + {\dosingleempty\dodefinedfont} + +\unexpanded\def\startfont + {\bgroup\definedfont} + +\unexpanded\def\stopfont + {\egroup} + +\def\doiffontcharelse#1#2% + {\bgroup + \definedfont[#1]% + \iffontchar\font#2\relax + \egroup\expandafter\firstoftwoarguments + \else + \egroup\expandafter\secondoftwoarguments + \fi} + +%D For more detailed (and historic information) we refer to the file +%D \type {font-ini.mkii}. Here we have a much simplified lower level +%D implementation due to a different approach to math. Also the chapter +%D on fonts in the reference manual explains a lot. + +%D \macros +%D {rm,ss,tt,hw,cg} +%D +%D Fonts are defined in separate files. When we define a font, +%D we distinguish between several styles. In most cases we will +%D use: +%D +%D \startlinecorrection +%D \starttable[|l||] +%D \HL +%D \NC roman regular serif \NC \type{\rm} \NC\FR +%D \NC sansserif sans support \NC \type{\ss} \NC\MR +%D \NC type teletype mono \NC \type{\tt} \NC\LR +%D \HL +%D \stoptable +%D \stoplinecorrection +%D +%D The number of styles is not limited to these three. When +%D using Lucida Bright we can for instance also define: +%D +%D \startlinecorrection +%D \starttable[|l||] +%D \HL +%D \NC handwritten \NC \type{\hw} \NC\FR +%D \NC calligraphic \NC \type{\cg} \NC\LR +%D \HL +%D \stoptable +%D \stoplinecorrection +%D +%D Within such a font set (\type{cmr}) and style (\type{\rm}) +%D we can define a number of text font alternatives: +%D +%D \startlinecorrection +%D \starttable[|l||] +%D \HL +%D \NC typeface \NC \type{\tf} \NC\FR +%D \NC boldface \NC \type{\bf} \NC\MR +%D \NC slanted \NC \type{\sl} \NC\MR +%D \NC italic \NC \type{\it} \NC\MR +%D \NC boldslanted \NC \type{\bs} \NC\MR +%D \NC bolditalic \NC \type{\bi} \NC\MR +%D \NC smallcaps \NC \type{\sc} \NC\LR +%D \HL +%D \stoptable +%D \stoplinecorrection +%D +%D Internally fonts are stored as combination of size, style +%D and alternative, e.g. \type{12pt}+\type{\ss}+\type{\bf}. +%D Users are not confronted with sizes, but use the style or +%D style+alternative to activate them. +%D +%D During the definition of a bodyfont one can also declare the +%D available larger alternatives: +%D +%D \starttyping +%D \tf \tfa \tfb \tfc ... +%D \bf \bfa \bfb \bfc ... +%D \sl \sla \slb \slc ... +%D \stoptyping +%D +%D The smaller ones are automatically supplied and derived from +%D the the bodyfont environment. +%D +%D \starttyping +%D \tfx \tfxx +%D \bfx \bfxx +%D \slx \slxx +%D \stoptyping +%D +%D There are only two smaller alternatives per style. The +%D larger alternatives on the other hand have no limitations. +%D +%D These larger alternatives are mostly used in chapter and +%D section titles or on title pages. When one switches to a +%D larger alternative, the bold an other ones automatically +%D adapt themselves: +%D +%D \startbuffer +%D \tfd Hi \bf there\sl, here \tfb I \bf am +%D \stopbuffer +%D +%S \startnarrower +%D \typebuffer +%S \stopnarrower +%D +%D therefore becomes: +%D +%D \startvoorbeeld +%D \getbuffer +%D \stopvoorbeeld +%D +%D Maybe this mechanism isn't always as logic, but as said +%D before, we tried to make it as intuitive as possible. +%D +%D So a specific kind of glyph can be characterized by: +%D +%D \startnarrower +%D family (cmr) + bodyfont (12pt) + style (rm) + alternative (bf) + size (a) +%D \stopnarrower +%D +%D The last component (the size) is optional. +%D +%D We introduced \type{\tf} as command to call for the current +%D normally sized typeface. This commands results in roman, +%D sans serif, teletype or whatever style is in charge. Such +%D rather massive switches of style sometimes take more +%D processing time than comfortable. Of course there is a +%D workaround for this: we can call fonts directly by means of +%D commands like: +%D +%D \starttyping +%D \rmtf \sssl \tttf \rmbsa +%D \stoptyping +%D +%D One should realize that this fast calls have limitations, +%D they lack for instance automatic super- and subscript +%D support. +%D +%D This leaves us two more commands: \type{\tx} and +%D \type{\txx}. These activate a smaller and even more smaller +%D font than the current one and adapt themselves to the +%D current alternative, so when \type{\bf} is active, +%D \type{\tx} gives a smaller boldface, which in turn can be +%D called directly by \type{\bfx}. +%D +%D These two smaller alternatives are specified by the bodyfont +%D environment and therefore not necessarily have similar sizes +%D as \type{\scriptsize} and \type{\scriptscriptsize}. The main +%D reason for this incompatibility (which can easily be undone) +%D lays in the fact that we often want a bit bigger characters +%D than in math mode. In \CONTEXT\ for instance the \type{\tx} +%D and \type{\txx} commands are used for surrogate +%D \cap{smallcaps} which support both nesting and alternatives, +%D like in {\bf\cap{a \cap{small} world}}, which was typeset by +%D +%D \starttyping +%D \bf\cap{a \cap{small} world} +%D \stoptyping +%D +%D And compare $\rm \scriptstyle THIS$ with the slightly larger +%D \cap{THIS}: \ruledhbox{$\rm \scriptstyle scriptstyle: THIS$} +%D or \ruledhbox{\cap{x style: THIS}} makes a big difference. +%D +%D The \type{x..d} sizes should be used grouped. If you +%D don't group them, i.e. call them in a row, \CONTEXT\ will +%D not be able to sort out your intention (\type {x} inside +%D \type {d} inside \type {x}. etc.). The following table +%D demonstrates this: +%D +%D \def\FontState{\setstrut\ruledhbox{\strut Hello}} +%D +%D \starttabulate[|||||] +%D \HL +%D \NC \rlap{\quad\bf grouped} \NC \NC \type {\tx} \NC \type {\txx} \NC \NR +%D \HL +%D \NC \type{\tfx} \NC \tfx \FontState \NC \tfx \tx \FontState \NC \tfx \txx \FontState \NC \NR +%D \NC \type{\tfxx} \NC \tfxx \FontState \NC \tfxx\tx \FontState \NC \tfxx\txx \FontState \NC \NR +%D \NC \type{\tf} \NC \tf \FontState \NC \tf \tx \FontState \NC \tf \txx \FontState \NC \NR +%D \NC \type{\tfa} \NC \tfa \FontState \NC \tfa \tx \FontState \NC \tfa \txx \FontState \NC \NR +%D \NC \type{\tfb} \NC \tfb \FontState \NC \tfb \tx \FontState \NC \tfb \txx \FontState \NC \NR +%D \NC \type{\tfc} \NC \tfc \FontState \NC \tfc \tx \FontState \NC \tfc \txx \FontState \NC \NR +%D \NC \type{\tfd} \NC \tfd \FontState \NC \tfd \tx \FontState \NC \tfd \txx \FontState \NC \NR +%D \NC \type{\tfx} \NC \tfx \FontState \NC \tfx \tx \FontState \NC \tfx \txx \FontState \NC \NR +%D \NC \type{\tfxx} \NC \tfxx \FontState \NC \tfxx\tx \FontState \NC \tfxx\txx \FontState \NC \NR +%D \HL +%D \stoptabulate +%D +%D \blank +%D +%D \starttabulate[|||||] +%D \HL +%D \NC \rlap{\quad\bf stacked} \NC \NC \type {\tx} \NC \type {\txx} \NC \NR +%D \HL +%D \NC \type{\tfx} +%D \NC \tfx \FontState +%D \NC \tfx \tx \FontState +%D \NC \tfx \txx \FontState +%D \NC \NR +%D \NC \type{\tfxx} +%D \NC \tfx\tfxx \FontState +%D \NC \tfx\tfxx \tx \FontState +%D \NC \tfx\tfxx \txx \FontState +%D \NC \NR +%D \NC \type{\tf} +%D \NC \tfx\tfxx\tf \FontState +%D \NC \tfx\tfxx\tf \tx \FontState +%D \NC \tfx\tfxx\tf \txx \FontState +%D \NC \NR +%D \NC \type{\tfa} +%D \NC \tfx\tfxx\tf\tfa \FontState +%D \NC \tfx\tfxx\tf\tfa \tx \FontState +%D \NC \tfx\tfxx\tf\tfa \txx \FontState +%D \NC \NR +%D \NC \type{\tfb} +%D \NC \tfx\tfxx\tf\tfa\tfb \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb \tx \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb \txx \FontState +%D \NC \NR +%D \NC \type{\tfc} +%D \NC \tfx\tfxx\tf\tfa\tfb\tfc \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb\tfc \tx \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb\tfc \txx \FontState +%D \NC \NR +%D \NC \type{\tfd} +%D \NC \tfx\tfxx\tf\tfa\tfb\tfd \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb\tfd \tx \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb\tfd \txx \FontState +%D \NC \NR +%D \NC \type{\tfx} +%D \NC \tfx\tfxx\tf\tfa\tfb\tfc\tfx \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb\tfc\tfx \tx \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb\tfc\tfx \txx \FontState +%D \NC \NR +%D \NC \type{\tfxx} +%D \NC \tfx\tfxx\tf\tfa\tfb\tfc\tfx\tfxx \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb\tfc\tfx\tfxx \tx \FontState +%D \NC \tfx\tfxx\tf\tfa\tfb\tfc\tfx\tfxx \txx \FontState +%D \NC \NR +%D \HL +%D \stoptabulate +%D +%D Remark: math support has changed a bit. + +%D \macros +%D {uchar} +%D +%D This macro prepares \CONTEXT\ for \UNICODE\ support. By +%D defining it here, we have at least an safeguard for utility +%D file reading. + +\ifdefined\uchar\else \unexpanded\def\uchar#1#2{[#1,#2]} \fi + +%D We define some (very private) constants to improve speed, +%D memory usage and consistency. + +\def\@size@ {@f@si@} % bodyfont size prefix (12pt etc) +\def\@style@ {@f@st@} % full style prefix (roman etc) +\def\@shortstyle@ {@f@sh@} % short style prefix (rm etc) +\def\@letter@ {@f@le@} % first alternative typeface +\def\@noletter@ {@f@no@} % second alternative typeface +\def\@fontclass@ {@f@cl@} % fontclass + +%D \macros +%D {fontclass, defaultfontclass} +%D +%D The fontclass model was introduced a while after we implement +%D the basic font model and at that time we still defaulted to +%D no model at all. Nowadays we default to the \type {modern} +%D fontclass. + +\let\fontclass \empty +\let\defaultfontclass\empty + +%D \macros +%D {textonly} +%D +%D Traditionally math has a big impact on font definitions, mainly +%D because we need to define alphabet variants using families and +%D fonts. This means that one can easily get 10 fonts loaded per +%D math size. In \MKIV\ we use a different approach: one family +%D which has either a virtual font made of traditional fonts, or +%D an \OPENTYPE\ font that has it all. +%D +%D We currently use only one math family but in the future we +%D might consider using a second one for bold math. For the +%D moment we keep the \MKII\ method of using a token register +%D for definitions but we already dropped the text and symbols +%D ones since they now live in the same family. + +\newtoks \mathstrategies + +\newif\ifsynchronizemathfonts \synchronizemathfontstrue + +\def\synchronizemath % math stuff in mmode + {\ifsynchronizemathfonts\the\mathstrategies\fi} + +\def\textonly{\synchronizemathfontsfalse} % document this + +%D The main math font definer. We have removed some optimized +%D code simply because we now always have a fontclass. We could +%D check for fontclass being default or empty and save a few +%D tests but it does not help us when no math is defined. + +\chardef\mrfam\zerocount % math regular +\chardef\mbfam\plusone % math bold + +\unexpanded\def\mr{\ifmmode\fam\zerocount\else\setcurrentfontalternative\c!mr\fi} % \fam\csname\c!mr fam\endcsname +\unexpanded\def\mb{\ifmmode\fam\plusone \else\setcurrentfontalternative\c!mb\fi} % \fam\csname\c!mb fam\endcsname + +\def\mathtextsuffix {-text} +\def\mathscriptsuffix {-script} +\def\mathscriptscriptsuffix{-scriptscript} + +% \let\mathsizesuffix\empty + +\let\currentmathsize\empty + +\def\mathsizesuffix{\ifcase0\currentmathsize\or\mathtextsuffix\or\mathscriptsuffix\or\mathscriptscriptsuffix\fi} + +\def\dodosetmathfamily#1#2% + {\ifcsname\fontclass \fontbody\c!mm\fontfamily\fontsize\currentmathsize\endcsname \autofontsizefalse + \csname\fontclass \fontbody\c!mm\fontfamily\fontsize\currentmathsize\endcsname \else + \ifcsname\fontclass \fontbody\c!mm\fontfamily \currentmathsize\endcsname \autofontsizetrue + \csname\fontclass \fontbody\c!mm\fontfamily \currentmathsize\endcsname \else + \dodosetmathfamilyx#1#2% + \fi\fi + #1#2\font} + +\def\dodosetmathfamilyx#1#2% + {\ifcsname\defaultfontclass\fontbody\c!mm\fontfamily\fontsize\currentmathsize\endcsname \autofontsizefalse + \csname\defaultfontclass\fontbody\c!mm\fontfamily\fontsize\currentmathsize\endcsname \else + \ifcsname\defaultfontclass\fontbody\c!mm\fontfamily \currentmathsize\endcsname \autofontsizetrue + \csname\defaultfontclass\fontbody\c!mm\fontfamily \currentmathsize\endcsname \else + \dodosetmathfamilyxx#1#2% + \fi\fi} + +\def\dodosetmathfamilyxx#1#2% + {\ifcsname \fontbody\c!mm\fontfamily\fontsize\currentmathsize\endcsname \autofontsizefalse + \csname \fontbody\c!mm\fontfamily\fontsize\currentmathsize\endcsname \else + \ifcsname \fontbody\c!mm\fontfamily \currentmathsize\endcsname \autofontsizetrue + \csname \fontbody\c!mm\fontfamily \currentmathsize\endcsname \else + \nullfont \autofontsizetrue + \fi\fi} + +\def\dosetmathfamily#1#2% + {\let\savedfontbody\fontbody % op hoger plan + \let\fontfamily#2% + \let\currentmathsize\!!plusthree\let\fontbody\scriptscriptface\dodosetmathfamily\scriptscriptfont#1% + \let\currentmathsize\!!plustwo \let\fontbody\scriptface \dodosetmathfamily\scriptfont #1% + \let\currentmathsize\!!plusone \let\fontbody\textface \dodosetmathfamily\textfont #1% + \let\currentmathsize\empty + \let\fontbody\savedfontbody + \autofontsizefalse} + +\appendtoks + \dosetmathfamily\mrfam\c!mr +\to \mathstrategies + +% not official ! + +\chardef\boldmathmode\zerocount % might change ... maybe \mathfontsupport 1 (normal) 2 (bold too) + +\def\enableboldmath {\chardef\boldmathmode\plusone } % todo: \setupbodyfont[boldmath,...] +\def\disableboldmath{\chardef\boldmathmode\zerocount} + +\appendtoks + \ifcase\boldmathmode\or\dosetmathfamily\mbfam\c!mb\fi +\to \mathstrategies + +% \chardef\msfam\plustwo % math symbol +% +% \def\c!ms{ms} +% +% \unexpanded\def\ms{\ifmmode\fam\plustwo\else\setcurrentfontalternative\c!ms\fi} +% +% \chardef\symbolmathmode\zerocount +% +% \def\enablesymbolmath {\chardef\symbolmathmode\plusone } +% \def\disablesymbolmath{\chardef\symbolmathmode\zerocount} +% +% \appendtoks +% \ifcase\symbolmathmode\or\dosetmathfamily\msfam\c!ms\fi +% \to \mathstrategies + +%D All used styles, like rm, ss and tt, are saved in a comma +%D separated list. Appart from practical limitations one can +%D define as many styles as needed. + +\def\fontrelativesizelist{\s!text,\s!script,\s!scriptscript,\c!x,\c!xx,\c!big,\c!small} + +%D There are several ways to specify a font. Three of them are +%D pure \TeX\ ones, the fourth one is new: +%D +%D \starttyping +%D \font\name=cmr12 +%D \font\name=cmr12 at 10pt +%D \font\name=cmr12 scaled 2 +%D \font\name=cmr12 sa 1.440 +%D \stoptyping +%D +%D The non||\TEX\ alternative \type{sa} stands for {\em scaled +%D at}. This means as much as: scale the bodyfontsize with this +%D factor. The scaled option is not that useful as one needs to +%D know the design size. +%D +%D Because \type {sa} (scaled at) and \type {mo} (mapped on) +%D are not low level \TEX\ supported alternatives, we have to +%D test for it ourselves. In doing so, we need an auxiliary +%D \DIMENSION. We cannot use \type{\scratchdimen} because font +%D loading can happen at any moment due to postponed loading. +%D We could instead have used dirty grouping tricks, but this +%D one works too. + +% \enableexperiments[fonts.autorscale] +% +% \starttypescript[mscore] +% \definetypeface [mscore] [rm] [serif] [mscoretimes] [default] +% \definetypeface [mscore] [ss] [sans] [mscorearial] [default] [rscale=auto] % 0.860] +% \definetypeface [mscore] [tt] [mono] [mscorecourier] [default] [rscale=auto] % 1.065] +% \definetypeface [mscore] [mm] [math] [times] [default] [rscale=auto] % 1.020] +% \stoptypescript +% +% \starttext +% \setupbodyfont[mscore,12pt] +% \startTEXpage +% test \ss test \tt test +% \stopTEXpage +% \stoptext + +\let\defaultrelativefontsize \plusone +\let\localrelativefontsize \plusone +\def\localabsolutefontsize {\fontbody} + +\let\relativefontsize \defaultrelativefontsize + +% \def\saverelativefontsize#1#2% #1=rm|ss|.. #2=waarde +% {\setxvalue{\fontclass#1\s!rscale}{#2}} + +\def\checkrelativefontid + {\ifcsname\??tf\fontclass\s!rname\endcsname + \@EA\let\@EA\relativefontid\csname\??tf\fontclass\s!rname\endcsname + \else + \@EA\xdef\csname\??tf\fontclass\s!rname\endcsname{\the\lastfontid}% + \let\relativefontid\empty + \fi} + +\def\checkrelativefontsize#1% + {\edef\relativefontsize + {\ifcsname\fontclass#1\s!rscale\endcsname + \csname\fontclass#1\s!rscale\endcsname + \else\ifcsname\defaultfontclass#1\s!rscale\endcsname + \csname\defaultfontclass#1\s!rscale\endcsname + \else + \defaultrelativefontsize + \fi\fi}% + \ifx\relativefontsize\v!auto + \let\relativefontsize\plusone + \checkrelativefontid + \else + \let\relativefontid\minusone + \fi} + +%D Scaling macros: +%D +%D This system is somewhat complicated by two (possible conflicting) +%D demands: +%D +%D \startitemize +%D \item We support wildcards like \type {sa *} which will adapt +%D to the current size. This is also the default specification. +%D \item We support named scales like \type {sa d}; beware: \type +%D {x} and \type {xx} are valid scales but they are not alway +%D the same as the ones used in for instance \type {\bfx} because +%D there the sized come from the bodyfont environment. In the +%D future there maybe a switch that also honors the environment +%D in named scales. +%D \stopitemize + +%D Keep in mind that the smaller sizes are just for text super and +%D subscripts while larger sizes can be used in titles where for +%D instance math follows the size. + +% b:x{\definedfont[SerifBold sa b]x}{\bfb x $x^x$}\par +% 1:x{\definedfont[SerifBold sa 1]x}{\bf x $x^x$}\par +% x:x{\definedfont[SerifBold sa x]x}{\bfx x $x^x$}\par +% xx:x{\definedfont[SerifBold sa xx]x}{\bfxx x $x^x$}\par +% +% *:x{\definedfont[Serif sa *]x}\par +% 1:x{\definedfont[Serif sa 1]x}\par +% 2:x{\definedfont[Serif sa 2]x}\par +% 3:x{\definedfont[Serif sa 3]x}\par +% 4:x{\definedfont[Serif sa 4]x}\par +% 5:x{\definedfont[Serif sa 5]x}\par +% +% {\definedfont[cmbx10 at 10pt]x\definedfont[cmbx8 at 10pt]x} + +\def\safontscale{\number\dimexpr\localabsolutefontsize\relax} +\def\mofontscale{\number\dimexpr\setmappedfontsize\localabsolutefontsize\relax} + +\let\somefontname\s!unknown +\let\somefontspec\s!unknown +\let\somefontsize\zerocount + +\newcount\scaledfontmode +\newdimen\scaledfontsize +\newtoks \everydefinefont +\newcount\lastfontid + +\def\currentfontbodysize + {\ifcsname\??ft\s!default\somefontsize\endcsname + \csname\??ft\s!default\somefontsize\endcsname + \else + \somefontsize + \fi} + +\let\relativefontid\empty + +\def\lowleveldefinefont#1#2% #2 = cs + {% + \ctxlua{fonts.define.command_1("\luaescapestring{#1}")}% the escapestring catches at \somedimen + % sets \scaledfontmode and \somefontname and \somefontsize + \ifcase\scaledfontmode\relax + % none, avoid the designsize if possible + \scaledfontsize-1000\scaledpoint + \or + % at + \scaledfontsize\somefontsize + \or + % sa + \scaledfontsize\localabsolutefontsize\relax + \scaledfontsize\currentfontbodysize\scaledfontsize + \or + % mo + \scaledfontsize\setmappedfontsize\localabsolutefontsize + \scaledfontsize\currentfontbodysize\scaledfontsize + \or + % scaled, don't use this one as it's unpredictable + \scaledfontsize-\somefontsize\scaledpoint + \fi + \scaledfontsize\localrelativefontsize\scaledfontsize + \ifautofontsize + \scaledfontsize\currentfontbodyscale\scaledfontsize + \fi + \edef\somefontspec{at \number\scaledfontsize sp}% + \edef\somefontfile{\truefontname\somefontname}% + \ifx\somefontfile\s!unknown + \edef\somefontfile{\defaultfontfile}% + \fi + \updatefontparameters + \updatefontclassparameters + \ctxlua{fonts.define.command_2( + \ifx\fontclass\empty false\else true\fi, + "#2", % cs, trailing % is gone + "\somefontfile", + \number\scaledfontsize, + "\@@fontclassfeatures", + "\@@fontfeatures", + "\@@fontclassfallbacks", + "\@@fontfallbacks", + 0\currentmathsize, + \number\dimexpr\textface\relax, + "\relativefontid" % experiment + )}% + \edef\somefontspec{at \somefontsize}% we need the resolved designsize (for fallbacks) + \expandafter\let\expandafter\lastrawfontcall\csname#2\endcsname + \the\everydefinefont} + +\def\updatefontclassparameters + {\edef\@@fontclassfeatures {\ifcsname\fontclass\fontstyle\s!features \endcsname\csname\fontclass\fontstyle\s!features \endcsname\fi}% + \edef\@@fontclassfallbacks{\ifcsname\fontclass\fontstyle\s!fallbacks\endcsname\csname\fontclass\fontstyle\s!fallbacks\endcsname\fi}} + +% resolve + +\def\@@thefeaturesyes#1% + {\ifcsname\??ff\fontclass#1\s!features \endcsname\@EA\let\@EA\@@fontfeatures \csname\??ff\fontclass#1\s!features \endcsname\else + \ifcsname\??ff #1\s!features \endcsname\@EA\let\@EA\@@fontfeatures \csname\??ff #1\s!features \endcsname\else + \ifcsname\??ff\fontclass #1\endcsname\@EA \@@thefeaturesyes \csname\??ff\fontclass #1\endcsname\else + \ifcsname\??ff #1\endcsname\@EA \@@thefeaturesyes \csname\??ff #1\endcsname\else + \let \@@fontfeatures \empty \fi\fi\fi\fi} + +\def\@@thefallbacksyes#1% + {\ifcsname\??ff\fontclass#1\s!fallbacks\endcsname\@EA\let\@EA\@@fontfallbacks \csname\??ff\fontclass#1\s!fallbacks\endcsname\else + \ifcsname\??ff #1\s!fallbacks\endcsname\@EA\let\@EA\@@fontfallbacks \csname\??ff #1\s!fallbacks\endcsname\else + \ifcsname\??ff\fontclass #1\endcsname\@EA \@@thefallbacksyes\csname\??ff\fontclass #1\endcsname\else + \ifcsname\??ff #1\endcsname\@EA \@@thefallbacksyes\csname\??ff #1\endcsname\else + \let \@@fontfallbacks \empty \fi\fi\fi\fi} + +\def\@@thefeaturesnop#1% + {\ifcsname\??ff#1\s!features \endcsname\@EA\let\@EA\@@fontfeatures \csname\??ff#1\s!features \endcsname\else + \ifcsname\??ff #1\endcsname\@EA \@@thefeaturesnop \csname\??ff #1\endcsname\else + \let \@@fontfeatures \empty \fi\fi} + +\def\@@thefallbacksnop#1% + {\ifcsname\??ff#1\s!fallbacks\endcsname\@EA\let\@EA\@@fontfallbacks \csname\??ff#1\s!fallbacks\endcsname\else + \ifcsname\??ff #1\endcsname\@EA \@@thefallbacksnop\csname\??ff #1\endcsname\else + \let \@@fontfallbacks \empty \fi\fi} + +\def\updatefontparametersyes + {\@@thefeaturesyes \somefontname + \@@thefallbacksyes\somefontname} + +\def\updatefontparametersnop + {\@@thefeaturesnop \somefontname + \@@thefallbacksnop\somefontname} + +\def\updatefontparameters + {\ifx\fontclass\empty\updatefontparametersnop\else\updatefontparametersyes\fi} + +\let\@@fontclassfeatures \empty +\let\@@fontclassfallbacks\empty + +\let\@@fontfallbacks\empty +\let\@@fontfeatures \empty +\let\@@hyphenchar \empty % todo, will go to encoding + +%D This brings down maps processing from 466 to 309 seconds +%D ($-33$\%) and mfonts from 42 to 34 seconds ($-15$\%). + +\newif\ifskipfontcharacteristics \skipfontcharacteristicstrue + +%D When fontclasses are used, we define the font global, +%D since namespaces are used. Otherwise we parse the specs +%D each time. + +\let\fontfile\s!unknown + +% \definefontfeature[slanted] [default][slant=.25] +% \definefontfeature[stretched][default][stretch=2] +% +% \start \definedfont[SerifBold*slanted at 20pt] \ruledhbox{Test!} \stop +% \start \definedfont[SerifBold*stretched at 20pt] \ruledhbox{Test!} \stop + +% \definefontfeature[default] [liga=yes,texligatures=yes,texquotes=yes] +% \definefontfeature[default-caps][liga=yes,texligatures=yes,texquotes=yes,smcp=yes,script=latn] +% +% \starttypescript [serif] [palatino-nova-regular] [name] +% \definefontsynonym[Serif] [palatinonova-regular][features=default] +% \definefontsynonym[SerifCaps][palatinonova-regular][features=default-caps] % also sets Serif +% \stoptypescript +% +% \starttypescript [serif] [palatino-nova-regular] [name] +% \definefontsynonym[Serif] [palatinonova-regular*default] +% \definefontsynonym[SerifCaps] [palatinonova-regular*default-caps] +% \stoptypescript + +% \definetypeface[mainface][rm][serif][palatino-nova-regular][default] \setupbodyfont[mainface] +% +% \starttext +% ``Test'' -- --- ff fi fl \sc ``Test'' -- --- ff fi fl +% \stoptext + +% \starttext +% \definefont +% [blabla] +% [name:Latin Modern Something] +% \definefont +% [blabla] +% [file:texnansi-lmr10] +% \blabla test +% \definefont +% [blabla] +% [texnansi-lmtt10] +% \blabla test +% \stoptext + +% \starttext +% +% \setupcolors[state=start] +% +% \definefontfeature +% [default-base] +% [script=latn,language=dflt,liga=yes,kern=yes,tlig=yes,trep=yes] +% \definefontfeature +% [default-node] +% [script=latn,language=dflt,liga=yes,kern=yes,tlig=yes,trep=yes,mode=node] +% \definefontfeature +% [default-none] +% [script=latn,language=dflt,liga=yes,kern=no, tlig=yes,trep=yes] +% +% \startoverlay +% {\vtop{\color[red] {\font\test=name:lmroman12regular*default-node \test \input ward \input zapf \input linden }}} +% {\vtop{\color[green]{\font\test=name:lmroman12regular*default-base \test \input ward \input zapf \input linden }}} +% {\vtop{\color[blue] {\font\test=name:lmroman12regular*default-none \test \input ward \input zapf \input linden }}} +% \stopoverlay +% +% \blank +% +% \startoverlay +% {\vtop{\color[red] {\font\test=name:texgyrepagella*default-node \test \input ward \input zapf \input linden }}} +% {\vtop{\color[green]{\font\test=name:texgyrepagella*default-base \test \input ward \input zapf \input linden }}} +% {\vtop{\color[blue] {\font\test=name:texgyrepagella*default-none \test \input ward \input zapf \input linden }}} +% \stopoverlay +% +% \blank +% +% \startoverlay +% {\vtop{\color[red] {\font\test=name:palatinonovaregular*default-node \test \input ward \input zapf \input linden }}} +% {\vtop{\color[green]{\font\test=name:palatinonovaregular*default-base \test \input ward \input zapf \input linden }}} +% {\vtop{\color[blue] {\font\test=name:palatinonovaregular*default-none \test \input ward \input zapf \input linden }}} +% \stopoverlay +% +% \startoverlay +% {\vtop{\color[red] {\font\test=name:OfficinaSerifBookITC*default-node \test \input ward \input zapf \input linden }}} +% {\vtop{\color[green]{\font\test=name:OfficinaSerifBookITC*default-base \test \input ward \input zapf \input linden }}} +% {\vtop{\color[blue] {\font\test=name:OfficinaSerifBookITC*default-none \test \input ward \input zapf \input linden }}} +% \stopoverlay +% +% \definefontfeature[superdefault][default][compose=yes] +% +% {\font\oeps=name:lmroman10regular*default at 30pt \oeps test \char7683} +% {\font\oeps=name:lmroman10regular*superdefault at 30pt \oeps test \char7683} +% +% \stoptext + +\def \defaultfontfile{\truefontname{Normal}} % was cmtt10, but that one is gone +\edef\nullfontname {\fontname\nullfont} + +%D \macros +%D {everyfont,everyfontswitch} +%D +%D For special purposes, we provide a hook. + +% \newevery \everyfont \relax +% \newevery \everyfontswitch \relax + +%D We also accept \type{sa a}||\type{sa d} as specification. + +%D \macros +%D {definefontsynonym, doifelsefontsynonym, +%D expandfontsynonym, truefontname, truefontdata} +%D +%D While looking for fonts, fontsynonyms are used for accessing +%D the files! +%D +%D \starttyping +%D \definefontsynonym[Serif][Lucida-Bright] +%D \definefontsynonym[Lucida-Bright][lbr][encoding=texnansi] +%D \stoptyping + +\def\classfont#1#2{#1#2} % \definefont[whatever][\classfont{xx}{yy} at 10pt] + +% We need to move the feature into the filename else it may be +% overloaded by another reference. For instance the definition of +% a regular and caps variant can use the same font. + +% We could use an indirect method ... store in 'array' and refer to +% slot. + +\unexpanded\def\definefontsynonym[#1]#2[#3]% + {\edef\@@fontname{#1}% + \edef\@@fontfile{#3}% + \ifx\fontclass\empty + \expandafter\dodefinefontsynonymnop + \else + \expandafter\dodefinefontsynonymyes + \fi} + +\def\dodefinefontsynonymnop + {\@EA\let\csname\??ff\@@fontname\endcsname\@@fontfile % maybe just #1 #3, saves expansion + \doifnextoptionalelse\dododefinefontsynonymnop\nonodefinefontsynonymnop} + +\def\dodefinefontsynonymyes + {\@EA\let\csname\??ff\fontclass\@@fontname\endcsname\@@fontfile % maybe just #1 #3, saves expansion + \doifnextoptionalelse\dododefinefontsynonymyes\nonodefinefontsynonymyes} + +\def\dododefinefontsynonymnop[#1]% + {\let\@@ff@@features \undefined + \let\@@ff@@fallbacks\undefined + \expandafter\dogetfontparameternop#1,]=,} + +\def\dododefinefontsynonymyes[#1]% + {\let\@@ff@@features \undefined + \let\@@ff@@fallbacks\undefined + \expandafter\dogetfontparameteryes#1,]=,} + +\def\dogetfontparameternop#1=#2,% + {\if]#1% + \dodododefinefontsynonymnop + \else + \expandafter\def\csname @@ff@@#1\endcsname{#2}% + \expandafter\dogetfontparameternop + \fi} + +\def\dogetfontparameteryes#1=#2,% + {\if]#1% + \dodododefinefontsynonymyes + \else + \expandafter\def\csname @@ff@@#1\endcsname{#2}% + \expandafter\dogetfontparameteryes + \fi} + +% hm, was wrong, class/global reversed + +\let\fcglobal\global +\let\fcxdef \xdef +\let\fcglet \glet + +\def\nonodefinefontsynonymnop + {\@EA\let\csname\??ff\@@fontname\s!features \endcsname\undefined + \@EA\let\csname\??ff\@@fontname\s!fallbacks\endcsname\undefined} + +\def\nonodefinefontsynonymyes + {\fcglobal\@EA\let\csname\??ff\fontclass\@@fontname\s!features \endcsname\undefined + \fcglobal\@EA\let\csname\??ff\fontclass\@@fontname\s!fallbacks\endcsname\undefined} + +\def\dodododefinefontsynonymnop + {\@EA\let\csname\??ff\@@fontname\s!features \endcsname\@@ff@@features + \@EA\let\csname\??ff\@@fontname\s!fallbacks\endcsname\@@ff@@fallbacks} + +\def\dodododefinefontsynonymyes + {\fcglobal\@EA\let\csname\??ff\fontclass\@@fontname\s!features \endcsname\@@ff@@features + \fcglobal\@EA\let\csname\??ff\fontclass\@@fontname\s!fallbacks\endcsname\@@ff@@fallbacks} + +\let\definefontfile\definefontsynonym % dedicated to Taco Hoekwater + +\unexpanded\def\setupfontsynonym + {\dodoubleempty\dosetupfontsynonym} + +\def\dosetupfontsynonym[#1][#2]% not yet supported, will do when needed + {} + +\def\truefontname#1% + {\@EA\dotruefontname#1*\empty*\relax} + +\def\dotruefontname#1*#2#3*#4\relax + {\ifcsname\??ff\fontclass#1\endcsname + \ifx#2\empty + \@EA\truefontname\csname\??ff\fontclass#1\endcsname + \else + \@EA\redotruefontname\csname\??ff\fontclass#1\endcsname*#2#3% + \fi + \else\ifcsname\??ff\defaultfontclass#1\endcsname + \ifx#2\empty + \@EA\truefontname\csname\??ff\defaultfontclass#1\endcsname + \else + \@EA\redotruefontname\csname\??ff\defaultfontclass#1\endcsname*#2#3% + \fi + \else\ifcsname\??ff#1\endcsname + \ifx#2\empty + \@EA\truefontname\csname\??ff#1\endcsname + \else + \@EA\redotruefontname\csname\??ff#1\endcsname*#2#3% + \fi + \else + #1\ifx#2\empty\else*#2#3\fi + \fi\fi\fi} + +\def\redotruefontname#1% + {\@EA\dodotruefontname#1*\relax} + +\def\dodotruefontname#1*#2\relax + {\ifcsname\??ff\fontclass#1\endcsname + \@EA\redotruefontname\csname\??ff\fontclass#1\endcsname + \else\ifcsname\??ff\defaultfontclass#1\endcsname + \@EA\redotruefontname\csname\??ff\defaultfontclass#1\endcsname + \else\ifcsname\??ff#1\endcsname + \@EA\redotruefontname\csname\??ff#1\endcsname + \else + #1% + \fi\fi\fi} + +\def\expandfontsynonym#1#2% #2 := onelevelexpansion(#1) + {\ifcsname\??ff\fontclass#2\endcsname + \expandafter\def\expandafter#1\expandafter{\csname\??ff\fontclass#2\endcsname}% + \else\ifcsname\??ff\defaultfontclass#2\endcsname + \expandafter\def\expandafter#1\expandafter{\csname\??ff\defaultfontclass#2\endcsname}% + \fi\fi} + +\def\doifelsefontsynonym#1% + {\ifcsname\??ff\fontclass#1\endcsname + \@EA\firstoftwoarguments + \else\ifcsname\??ff\defaultfontclass#1\endcsname + \@EAEAEA\firstoftwoarguments + \else + \@EAEAEA\secondoftwoarguments + \fi\fi} + +% \definetypeface[palatino][rm][serif][palatino,allbold][default] +% +% \startfontclass[palatino] +% \definefontsynonym [Serif] [SerifBold] +% \definefontsynonym [SerifItalic] [SerifBoldItalic] +% \definefontsynonym [SerifSlanted] [SerifBoldSlanted] +% \definefontsynonym [SerifCaps] [SerifBold] +% \stopfontclass +% +% \setupbodyfont[palatino] + +\unexpanded\def\startfontclass + {\dosingleempty\dostartfontclass} + +\def\dostartfontclass[#1]% + {\pushmacro\fontclass + \doifelse{#1}\v!each + {\let\fontclass\empty} + {\doifsomething{#1}{\def\fontclass{#1}}}} + +\unexpanded\def\stopfontclass + {\popmacro\fontclass} + +%D \macros +%D {tracedfontname} +%D +%D A goody: + +\def\tracedfontname#1% + {#1\ifcsname\??ff\fontclass#1\endcsname + \@EA\tracedfontname\csname\??ff\fontclass#1\endcsname + \else\ifcsname\??ff#1\endcsname + \@EA\tracedfontname\csname\??ff#1\endcsname + \fi\fi} + +%D \macros +%D {definefont} +%D +%D Before we implement the main definition macro, we first show +%D one for local use: +%D +%D \starttyping +%D \definefont[Some][LucidaBright at 100pt] \Some some +%D \definefont[More][LucidaBright scaled 3000] \More more +%D \definefont[Nice][LucidaBright mo 2] \Nice nice +%D \definefont[Text][LucidaBright sa 5.4] \Text last +%D \stoptyping +%D +%D The implementation looks as follows: + +\unexpanded\def\definefont + {\dotripleempty\dodefinefont} + +\def\dodefinefont[#1][#2][#3]% [name][spec][1.6 | line=10pt | setup_id] + {\ifthirdargument + \setuvalue{#1}{\redodefinefont{#1}{#2}{#3}}% + \else + \setuvalue{#1}{\dododefinefont{#1}{#2}}% + \fi} + +\def\redodefinefont#1#2#3% + {\dododefinefont{#1}{#2}% + \doifsetupselse{#3} + {\setups[#3]} % don't forget to document this ! + {\setuplocalinterlinespace[#3]% + \setupspacing}} % needed ? + +\unexpanded\def\definefrozenfont + {\dotripleempty\dodefinefrozenfont} + +\def\dodefinefrozenfont[#1][#2][#3]% + {\dodefinefont[#1][#2][#3]% + \expandafter\let\csname\lastfontidentifier\expandafter\endcsname\csname\rawfontidentifier\endcsname} + +%D The \type {*} makes the switch local, so that we can redefine a +%D logical name and/or change the size in between. + +\newif\ifautofontsize \autofontsizetrue + +\let\lastfontidentifier\empty + +\def\rawfontidentifier{**\lastfontidentifier\fontsize\currentmathsize**} +\def\newfontidentifier{*\fontclass\lastfontidentifier\fontsize\currentmathsize*} + +\let\oldrawfontidentifier\rawfontidentifier +\let\oldnewfontidentifier\newfontidentifier + +\def\newfontidentifier{*\fontclass\lastfontidentifier\fontstyle\fontsize*} + +\def\dododefinefont#1#2% + {\edef\lastfontidentifier{#1}% + \let\localrelativefontsize\defaultrelativefontsize + \let\localabsolutefontsize\fontbody + \lowleveldefinefont{#2}\rawfontidentifier + \csname\rawfontidentifier\endcsname + \autofontsizefalse + \setfontcharacteristics + \the\everyfontswitch + \let\rawfontidentifier\oldrawfontidentifier} + +\def\xxdododefinefont#1#2#3#4% \autofontsizetrue is set by calling routine + {\edef\lastfontidentifier{#3}% + \ifcsname\newfontidentifier\endcsname\else + \def\localrelativefontsize{#1}% + \def\localabsolutefontsize{#2}% + \lowleveldefinefont{#4}\newfontidentifier + \fi + \csname\newfontidentifier\endcsname + \autofontsizefalse + %\edef\lastfontidentifier{#3}% + \ifskipfontcharacteristics \else + \setfontcharacteristics + \the\everyfontswitch + \fi + \let\newfontidentifier\oldnewfontidentifier} + +%D \macros +%D {mapfontsize} +%D +%D For special purposes, like in math, you may want to use +%D slightly different sizes than the ones given. This happens +%D for instance with the Math Times fonts. Mapped font sizes +%D can be specified by using the \type {mo} key instead of +%D \type {sa} in font definitions. +%D +%D \startbuffer +%D \mapfontsize[10pt][11pt] +%D \mapfontsize[11pt][12pt] +%D \mapfontsize[12pt][13pt] +%D +%D \definefont[test][Serif]\test TEST \par +%D \definefont[test][Serif sa 5]\test TEST \par +%D \definefont[test][Serif mo 5]\test TEST \par +%D \definefont[test][Serif sa d]\test TEST \par +%D \definefont[test][Serif at 60pt]\test TEST \par +%D \definefont[test][Serif scaled 6000]\test TEST \par +%D \stopbuffer +%D +%D \typebuffer +%D +%D \startpacked +%D \getbuffer +%D \stoppacked + +\def\mapfontsize + {\dodoubleargument\domapfontsize} + +\def\domapfontsize[#1][#2]% + {\setvalue{\??ft*\the\dimexpr#1\relax}{#2}} + +\def\setmappedfontsize#1% + {\ifcsname\??ft*#1\endcsname + \csname\??ft*#1\endcsname + \else + #1% + \fi} + +%D \macros +%D {getfontname} +%D +%D The names of the fonts can be called with the rather simple +%D macro \type{\getfontname}. When for instance we pass +%D \type{12ptrmtf} as argument, we get \getfontname{12ptrmtf}. + +\def\getfontname#1% + {\csname\??ft#1\endcsname} + +%D To be documented. + +\let\fontsizelist \empty +\let\fontalternativelist\empty +\let\fontstylelist \empty + +\def\checkfontnamecombinations % we need to split math and text here ... todo (math only has mr and mb) + {\def\docommand##1% + {\def\dodocommand####1% + {\def\dododocommand########1{\checkbodyfont{########1}{####1}{##1}}% + \processcommacommand[\fontstylelist]\dododocommand}% + \processcommacommand[\fontalternativelist]\dodocommand}% + \processcommacommand[\fontsizelist]\docommand} + +\unexpanded\def\definefontsize[#1]% sneller met toks + {\addtocommalist{#1}\fontsizelist + \checkfontnamecombinations} + +\unexpanded\def\definefontalternative[#1]% + {\addtocommalist{#1}\fontalternativelist + \checkfontnamecombinations} + +%D \macros +%D {currentfontscale,currentfontbodyscale} +%D +%D Sometimes we need to have access to the font scale +%D including the \type{a}||\type{d} sizes. The next macro +%D returns the current scaling factor. Take a look at +%D \type {cont-log.tex} for an example of its use. + +\def\currentfontscale + {\csname\??ft\s!default + \ifcsname\??ft\s!default\xfontsize\endcsname \xfontsize \else + \ifcsname\??ft\s!default\s!text \endcsname \s!text \fi\fi + \endcsname} + +\def\currentfontbodyscale + {\csname\??ft\s!default + \ifcsname\??ft\s!default\fontsize\endcsname \fontsize \else + \ifcsname\??ft\s!default\s!text \endcsname \s!text \fi\fi + \endcsname} + +\setvalue{\??ft\s!default}{1} + +%D Both alternatives use \type {\xfontsize}, a macro that +%D expands to the current size in terms of \type {a} \unknown +%D \type {d}, nothing, or \type {x} \unknown \type {xx}. + +\def\xfontsize{\ifcase\currentxfontsize\fontsize\or\c!x\else\c!xx\fi} + +%D A typical use of this command is in commands that switch +%D to another font bypassing the font mechanism: +%D +%D \starttyping +%D \font\myfont=\truefontname{MyFont} at \the\dimexpr\currentfontscale\bodyfontsize\relax +%D \stoptyping + +%D Now we enter the area of font switching. The switching +%D mechanism has to take care of several situations, like: +%D +%D \startitemize[packed] +%D \item changing the overal document fonts (including margins, +%D headers and footers) +%D \item changing local fonts (only the running text) +%D \item smaller and even more smaller alternatives (super- +%D and subscripts) +%D \stopitemize +%D +%D \TEX\ offers a powerfull family mechanism for super- and +%D subscripts in math mode. In text mode however, we don't use +%D families for the smaller alternatives, and therefore have +%D to take care of it otherwise. + +%D \macros +%D {definebodyfontenvironment,setupbodyfontenvironment} +%D +%D The relationship between the several sizes of a font, is +%D defined by: +%D +%D \showsetup{definebodyfontenvironment} +%D +%D Later on we will see how these parameters are used, so for +%D the moment we stick with an example: +%D +%D \starttyping +%D \definebodyfontenvironment +%D [12pt] +%D [ text=12pt, +%D script=9pt, +%D scriptscript=7pt, +%D x=10pt, +%D xx=8pt, +%D big=12pt, +%D small=10pt] +%D \stoptyping +%D +%D The first argument specifies the bodyfont size to which the +%D settings apply. All second parameters are specified in +%D dimensions and tell us more about related sizes. +%D +%D Afterwards, one can change values with +%D +%D \showsetup{setupbodyfontenvironment} +%D +%D Due to the fact that \type{\c!text} and \type{\s!text} can +%D have a similar meaning, and therefore can lead to an +%D unwanted loop, we temporary redefine \type{\c!text}. For +%D the moment this in only place that some trickery is needed +%D to fool the multilingual interface. However, long ago +%D we decided to stick to \s!text in definitions as it closely +%D relates to the math model where text, script and scriptscript +%D are used untranslated. Also, we are now english at the low +%D level so it cannot go wrong anymore. +%D +%D When instead of a size the keyword \type{unknown} is +%D passed, fractions (relations) are used instead of fixed +%D sizes. + +\let\bodyfontenvironmentlist\empty + +\newcount\@@fontdefhack % check if this is still needed + +\def\@@beginfontdef + {\ifcase\@@fontdefhack + \let\k!savedtext \k!text \let\k!text \s!text + \let\k!k!savedtext \k!k!text \let\k!k!text \!!plusone + \let\k!saveddefault \k!default \let\k!default \s!default + \let\k!k!saveddefault\k!k!default \let\k!k!default \!!plusone + \fi + \advance\@@fontdefhack \plusone } + +\def\@@endfontdef + {\advance\@@fontdefhack \minusone + \ifcase\@@fontdefhack + \let\k!k!default\k!k!saveddefault + \let\k!default \k!saveddefault + \let\k!k!text \k!k!savedtext + \let\k!text \k!savedtext + \fi} + +\unexpanded\def\definebodyfontenvironment + {\dotripleempty\dodefinebodyfontenvironment} + +\def\dodefinebodyfontenvironment[#1][#2][#3]% class size settings + {\ifthirdargument + \@@beginfontdef + \doifelse{#2}\s!default + {\getparameters[\??ft\s!default][#3]} + {\normalizebodyfontsize#2\to\tempbodyfontsize + \addtocommalist\tempbodyfontsize\bodyfontenvironmentlist + \@EA\dododefinebodyfontenvironment\@EA[\tempbodyfontsize][#1][#3]}% + \@@endfontdef + \else + \ifx\fontclass\empty\else + \writestatus\m!fonts{beware: fontclass ignored (if needed use: [fontclass][size][settings])}% + \fi + \pushmacro\fontclass + \let\fontclass\empty + \definebodyfontenvironment[\fontclass][#1][#2]% change */* + \popmacro\fontclass + \fi} + +\def\dododefinebodyfontenvironment[#1][#2][#3]% size class settings + {\@@beginfontdef % \s!text goes wrong in testing because the 12pt alternative will called when typesetting the test (or so) + \ifcsname\??ft#2#1\c!em\endcsname + % we test for em as we assume it to be set + \else + \def\docommand##1% + {\scratchdimen\csname\??ft\s!default##1\endcsname\dimexpr#1\relax + \normalizebodyfontsize\scratchdimen\to\tempbodyfontsize + \letvalue{\??ft#2#1##1}\tempbodyfontsize}% + \processcommacommand[\fontrelativesizelist]\docommand + \copyparameters + [\??ft#2#1][\??ft\s!default] + [\c!interlinespace,\c!em]% + \fi + \getparameters[\??ft#2#1][#3]% + \@@endfontdef + % new code, see remark + \ifloadingfonts + % only runtime + \else\ifcsname\@size@#1\endcsname + % only once + \else + % prevent loop (hence \empty) + \letvalue{\@size@#1}\empty + \pushmacro\fontclass % new per 26102009 + \edef\fontclass{#2}% % new per 26102009 + \defineunknownfont{#1}% + \popmacro\fontclass % new per 26102009 + \fi\fi + % so far + \setvalue{\@size@#1}{\docompletefontswitch[#1]}} + +%D {\bf Remark:} We need to cover the following cases, +%D otherwise users can get confused: +%D +%D \starttyping +%D \setupbodyfont[23pt] +%D +%D \definebodyfontenvironment[23pt] +%D \setupbodyfont[23pt] +%D +%D \definebodyfontenvironment[23pt] +%D \definebodyfont[23pt][rm,ss,tt][default] +%D \setupbodyfont[23pt] +%D \stoptyping + +%D Beware: while some font defs can be global, the bodyfont +%D environment checks local. This means that multiple local +%D checks resulting in definitions are not that efficient. +%D So, apart from an occasional switch, one should define an +%D environment at the outer level. + +\def\checkbodyfontenvironment[#1]% + {\definebodyfontenvironment[\fontclass][#1][]} + +\def\checkbodyfontenvironment[#1]% + {\ifcsname\??ft\fontclass#1\c!em\endcsname + % we test for em as we assume it to be set + \else + \definebodyfontenvironment[\fontclass][#1][]% + \fi} + +% this one already catches both define/setup + +\let\setupbodyfontenvironment\definebodyfontenvironment + +%D Just a couple of interface macros: + +\def\bodyfontvariable#1% + {\??ft\ifcsname\??ft\fontclass#1\endcsname\fontclass\fi#1} + +\def\bodyfontinterlinespecs + {\bodyfontvariable{\normalizedbodyfontsize\c!interlinespace}} + +\def\bodyfontinterlinespace + {\csname\bodyfontinterlinespecs\endcsname} + +%D We default all parameters to the main bodyfont size (begin +%D \type{#1}), so the next setup is valid too: +%D +%D \starttyping +%D \definebodyfontenvironment[24pt] +%D \stoptyping +%D +%D All parameters can be redefined when needed, so one does +%D not have to stick to the default ones. + +%D \macros +%D {definebodyfont} +%D +%D The next step in defining a bodyfont involves the actual font +%D files, which can be recognized by their extension +%D \type{tfm}. Installing those file is often beyond the +%D scope of the user and up to the system administrator. +%D +%D \showsetup{definebodyfont} +%D +%D This commands takes three arguments: a (series of) bodyfont +%D size(s), the style group to which the definitions belong, +%D and an alternative, as specified by the \TEX\ (math) families, +%D extended with~a, b~\unknown. +%D +%D We show two examples, that show all the alternative +%D scaling options. The \type{\tfa} alternatives can be +%D extended with \type{\bfa}, \type{\slb}, etc. or even +%D \type{e} and higher alternatives. The magic scaled +%D values are derived from plain \TEX's \type {\magstep}: +%D +%D \starttyping +%D \definebodyfont [12pt] [rm] +%D [tf=cmr12, +%D bf=cmbx12, +%D it=cmti12, +%D sl=cmsl12, +%D bi=cmbxti10 at 12pt, +%D bs=cmbxsl10 at 12pt, +%D tfa=cmr12 scaled 1.200, +%D tfb=cmr12 scaled 1.440, +%D tfc=cmr12 scaled 1.728, +%D tfd=cmr12 scaled 2.074, +%D sc=cmcsc10 at 12pt] +%D +%D \definebodyfont [12pt,11pt,10pt,9pt,8pt] [rm] +%D [tf=lbr sa 1, +%D bf=lbd sa 1, +%D it=lbi sa 1, +%D sl=lbsl sa 1, +%D bi=lbdi sa 1, +%D bs=lbdi sa 1, +%D tfa=lbr sa 1.200, +%D tfb=lbr sa 1.440, +%D tfc=lbr sa 1.728, +%D tfd=lbr sa 2.074, +%D sc=lbr sa 0.833] +%D \stoptyping +%D +%D The second example shows that we can define more sizes at +%D once. The main difference between these examples is that the +%D Computer Modern Roman come in many design sizes. This means +%D that there we cannot define them in bulk using \type{sa}. +%D Instead of \type{rm} (roman) one can define \type{ss} (sans +%D serif), \type{tt} (teletype), \type{hw} (hand written), +%D \type{cg} (calygraphic) and whatever styles. +%D +%D The first argument may be a comma separated list. This, +%D combined with specifications using \type{sa} can save a lot +%D of typing. Although all arguments should be specified, we +%D treat the second argument as optional. +%D +%D Defining a bodyfont involves two actions: defining the +%D specific style related alternatives, like \type{\rma}, +%D \type{\bfa} and \type{\rmsla}, and storing the definitions +%D of their bodyfont size related fonts. The first step is +%D bodyfont independant but executed every time. This permits +%D user definitions like \type{\tfw} or \type{\bfq} for real +%D large alternatives. + +\unexpanded\def\definebodyfont + {\doquadrupleempty\redefinebodyfont} + +\def\redefinebodyfont[#1][#2][#3][#4]% + {\iffourthargument + \processcommacommand[#1]{\reredefinebodyfont[#2][#3][#4]}% + \else + \dodefinebodyfont[#1][#2][#3]% + \fi} + +\def\reredefinebodyfont[#1][#2][#3]#4% + {\pushmacro\fontclass + \doifelse{#4}\s!default + {\let\fontclass\empty} + {\def\fontclass{#4}}% + \definebodyfont[#1][#2][#3]% + \popmacro\fontclass} + +\def\dodefinebodyfont[#1][#2][#3]% body|identifier style defs|identifier + {\ifthirdargument + \doifnumberelse{#1} + {\doifassignmentelse{#3} + {% [12pt] [style] [settings] + \ifcsname#2\endcsname\else\normalexpanded{\noexpand\definefontstyle[#2][#2]}\fi % new + \processcommalist[#1]{\dododefinebodyfont{#2}{#3}}} + {% [12pt] [style] [identifier] + \dodefinedefaultbodyfont[#1][#2][#3]}} % body style identifier + {% [identifier] [style] [settings] % see *** + \setvalue{\s!default#1#2}##1##2{\normalexpanded{\noexpand\xdodefinebodyfont[##1][##2][#3]}}}% + \else\ifsecondargument + \definebodyfont[#1][\c!rm][#2]% + \else + % Maybe there are default dependencies defined which we can use ([unknown]) and + % if not, then we have at least to make sure some basics are set up. + \ifcsname\@size@#1\endcsname \else + \defineunknownfont{#1}% + \fi + \ifcsname\@size@#1\endcsname \else + \definebodyfont[#1][\c!rm][]% + \fi + \fi\fi} + +\def\xdodefinebodyfont[#1][#2][#3]% body|identifier style defs|identifier + {\checkrelativefontsize{#2}% rather new, inherit from other defs + \ifcsname#2\endcsname\else\normalexpanded{\noexpand\definefontstyle[#2][#2]}\fi % new + \processcommalist[#1]{\dododefinebodyfont{#2}{#3}}% + \let\relativefontsize\defaultrelativefontsize} + +\def\dododefinebodyfont#1#2#3% style defs body + {\checkbodyfontenvironment[#3]% just to be sure. + \processcommalist[#2]{\dodododefinebodyfont{#1}{#3}}} + +\def\dodododefinebodyfont#1#2#3% style body def + {\dododododefinebodyfont{#1}{#2}[#3]} + +\def\dododododefinebodyfont + {\ifx\fontclass\empty + \expandafter\dododododefinebodyfontnop + \else + \expandafter\dododododefinebodyfontyes + \fi} + +\def\dododododefinebodyfontyes#1% style body def + {\edef\askedbodyfontstyle{#1}% + \ifx\askedbodyfontstyle\c!mm + \expandafter\dodefinebodyfontyesmm + \else + \expandafter\dodefinebodyfontyesxx + \fi\askedbodyfontstyle} % we can get rid of #1 + +\def\dododododefinebodyfontnop#1% style body def + {\edef\askedbodyfontstyle{#1}% + \ifx\askedbodyfontstyle\c!mm + \expandafter\dodefinebodyfontnopmm + \else + \expandafter\dodefinebodyfontnopxx + \fi\askedbodyfontstyle} % we can get rid of #1 + +\def\dodefinebodyfontnopxx#1#2[#3#4#5=#6]% style body def + {\ifcsname#1#3#4#5\endcsname\else\checkbodyfont{#1}{#3#4}{#5}\fi% not \definefontsize[#5] + \@EA\let\csname*#2#1#3#4#5*\endcsname\undefined + \unexpanded\@EA\edef\csname#2#1#3#4#5\endcsname{\noexpand\xxdododefinefont{\number\relativefontsize}{#2}{#2#1#3#4#5}{\normalunexpanded{#6}}}} + +\def\dodefinebodyfontyesxx#1#2[#3#4#5=#6]% style body def + {\ifcsname#1#3#4#5\endcsname\else\checkbodyfont{#1}{#3#4}{#5}\fi% not \definefontsize[#5] + \fcglobal\@EA\let\csname*\fontclass#2#1#3#4#5*\endcsname\undefined + \unexpanded\@EA\fcxdef\csname\fontclass#2#1#3#4#5\endcsname{\noexpand\xxdododefinefont{\number\relativefontsize}{#2}{#2#1#3#4#5}{\normalunexpanded{#6}}}} + +\def\dodefinebodyfontnopmm#1#2[#3#4#5=#6]% style body def + {\ifcsname#1#3#4#5\endcsname\else\checkbodyfont{#1}{#3#4}{#5}\fi% not \definefontsize[#5] + \@EA\let\csname*#2#1#3#4#51*\endcsname\undefined + \@EA\let\csname*#2#1#3#4#52*\endcsname\undefined + \@EA\let\csname*#2#1#3#4#53*\endcsname\undefined + \unexpanded\@EA\edef\csname#2#1#3#4#51\endcsname{\noexpand\xxdododefinefont{\number\relativefontsize}{#2}{#2#1#3#4#51}{\normalunexpanded{#6}}}% + \unexpanded\@EA\edef\csname#2#1#3#4#52\endcsname{\noexpand\xxdododefinefont{\number\relativefontsize}{#2}{#2#1#3#4#52}{\normalunexpanded{#6}}}% + \unexpanded\@EA\edef\csname#2#1#3#4#53\endcsname{\noexpand\xxdododefinefont{\number\relativefontsize}{#2}{#2#1#3#4#53}{\normalunexpanded{#6}}}} + +\def\dodefinebodyfontyesmm#1#2[#3#4#5=#6]% style body def + {\ifcsname#1#3#4#5\endcsname\else\checkbodyfont{#1}{#3#4}{#5}\fi% not \definefontsize[#5] + \fcglobal\@EA\let\csname*\fontclass#2#1#3#4#51*\endcsname\undefined + \fcglobal\@EA\let\csname*\fontclass#2#1#3#4#52*\endcsname\undefined + \fcglobal\@EA\let\csname*\fontclass#2#1#3#4#53*\endcsname\undefined + \unexpanded\@EA\fcxdef\csname\fontclass#2#1#3#4#51\endcsname{\noexpand\xxdododefinefont{\number\relativefontsize}{#2}{#2#1#3#4#51}{\normalunexpanded{#6}}}% + \unexpanded\@EA\fcxdef\csname\fontclass#2#1#3#4#52\endcsname{\noexpand\xxdododefinefont{\number\relativefontsize}{#2}{#2#1#3#4#52}{\normalunexpanded{#6}}}% + \unexpanded\@EA\fcxdef\csname\fontclass#2#1#3#4#53\endcsname{\noexpand\xxdododefinefont{\number\relativefontsize}{#2}{#2#1#3#4#53}{\normalunexpanded{#6}}}} + +\def\checkbodyfont#1% tests for ttsl mmbf + {\edef\c!!mm{#1}% + \ifx\c!!mm\c!mm % prevents \max and alike (re)defs + \expandafter\checkmathbodyfont + \else + \expandafter\checktextbodyfont + \fi{#1}} % no \c!!mm, not expanded later on + +% some day we can do an auto-fam if needed + +\def\checkmathbodyfont#1#2#3% style alt size / gdef % #3 can be empty + {%\message{!m #1 #2 #3!}% + % #1 #2 #3 = signal + %setugvalue {#2}{\setcurrentfontalternative {#2}}% \mr \mb + \setugvalue {#1}{\setcurrentfontstyle {#1}}}% \mm + +\def\checktextbodyfont#1#2#3% style alt size / gdef % #3 can be empty + {%\message{!t #1 #2 #3!}% + \setugvalue {#1#3}{\setcurrentfontstylesize {#1}{#3}}% \rma + \setugvalue {#2#3}{\setcurrentfontalternativesize {#2}{#3}}% \sla + \setugvalue {#1#2#3}{\setcurrentfontstylealternativesize{#1}{#2}{#3}}% \rmsla + \setugvalue {#1}{\setcurrentfontstyle {#1}}% \rm + \setugvalue {#2}{\setcurrentfontalternative {#2}}% \sl + \setugvalue {#1\c!x}{\setcurrentfontxstylealternative {#1}}% \rmx + \setugvalue{#1\c!xx}{\setcurrentfontxxstylealternative {#1}}% \rmxx + \setugvalue {#2\c!x}{\setcurrentfontxalternative {#2}}% \slx + \setugvalue{#2\c!xx}{\setcurrentfontxxalternative {#2}}% \slxx + \setugvalue {#1#2}{\setcurrentfontstylealternative {#1}{#2}}}% \rmsl + +\def\dodefinedefaultbodyfont[#1][#2][#3]% sizes styles identifier + {\def\dododefinedefaultbodyfont##1% + {\def\dodododefinedefaultbodyfont####1% + {\def\dododododefinedefaultbodyfont########1% + {\ifcsname\s!default########1####1\endcsname + % [12pt] [style] [identifier] + \csname\s!default########1####1\endcsname{##1}{####1}% + \fi}% + \processcommalist[#3]\dododododefinedefaultbodyfont}% + \processcommalist[#2]\dodododefinedefaultbodyfont}% + \processcommalist[#1]\dododefinedefaultbodyfont} + +%D Unknown families are added to the family list! For the +%D moment we also set the direct calls here. Some day a better +%D solution will be implemented. The good news is that unknown +%D fonts are defined automatically. + +\newif\ifdefiningunknownfont + +\def\dodefineunknownfont#1#2% + {\ifcsname\??ft\s!default#2\endcsname + \donetrue + \normalizebodyfontsize\csname\??ft\s!default#2\endcsname\dimexpr#1\relax\to\tempbodyfontsize + \letvalue{\??ft#1#2}\tempbodyfontsize + \fi} + +\def\dodefineunknownbodyfont#1#2% see *** + {\ifcsname\s!default\s!default#2\endcsname % somehow related to */* + \donetrue + \csname\s!default\s!default#2\endcsname{#1}{#2}% + \fi} + +\def\dodefineunknownsubfont#1#2% + {\ifcsname\@size@\csname\??ft#1#2\endcsname\endcsname + \else + \donetrue + \defineunknownfont{\csname\??ft#1#2\endcsname}% + \fi} + +\unexpanded\def\defineunknownfont#1% + {\let\c!savedtext\c!text + \let\c!text\s!text + \donefalse + \processcommacommand[\fontrelativesizelist]{\dodefineunknownfont{#1}}% + \let\c!text\c!savedtext + \ifdone + \donefalse + \processcommacommand + [\fontstylelist] + {\dodefineunknownbodyfont{#1}}% + \ifdone + \donefalse + \setvalue{\@size@#1}{\docompletefontswitch[#1]}% + \ifdefiningunknownfont \else + \definingunknownfonttrue + \processcommacommand[\fontrelativesizelist]{\dodefineunknownsubfont{#1}}% + \definingunknownfontfalse + \fi + \fi + \ifdone + \showmessage\m!fonts{14}{#1}% + \fi + \fi} + +%D These macros show that quite some definitions take place. +%D Fonts are not loaded yet! This means that at format +%D generation time, no font files are preloaded. + +%D \macros +%D {everybodyfont,Everybodyfont,everyglobalbodyfont} +%D +%D Every change in bodyfont size has conseqences for the baseline +%D distance and skips between paragraphs. These are initialized +%D in other modules. Here we only provide the hooks that +%D garantees their handling. + +%D At the system level one can initialize thing like: +%D +%D \starttyping +%D \appendtoks \setupspacing \to \everybodyfont +%D \stoptyping +%D +%D While users can add their own non standard commands like: +%D +%D \starttyping +%D \EveryBodyFont{\message{changing to bodyfont \the\bodyfontsize}} +%D \stoptyping +%D +%D Personnaly I never felt the need for such extensions, but +%D at least its possible. + +%D \macros +%D {globalbodyfontsize,localbodyfontsize} +%D +%D Next we'll do the tough job of font switching. Here we +%D have to distinguish between the global (overal) bodyfont +%D size and the local (sometimes in the textflow) size. We +%D store these dimensions in two \DIMENSION\ registers. + +\ifdefined\globalbodyfontsize\else \newdimen\globalbodyfontsize \fi \globalbodyfontsize=12pt +\ifdefined\localbodyfontsize \else \newdimen\localbodyfontsize \fi \localbodyfontsize =\globalbodyfontsize + +%D \macros +%D {bodyfontsize} +%D +%D These two registers are not to be misused in calculations. +%D For this purpose we keep a copy: + +\newdimen\bodyfontsize \bodyfontsize=\globalbodyfontsize + +%D \macros +%D {bodyfontfactor,bodyfontpoints} +%D +%D For multiplication purposes we keep an auxiliary counter +%D and macro (here the expansion is not explicitly needed): + +\newcount\bodyfontpoints \dimensiontocount\bodyfontsize\bodyfontpoints + +\edef\bodyfontfactor{\withoutpt\the\bodyfontsize} + +%D When we assign for instance 12pt to a \DIMENSION\ register +%D the \type{\the}'d value comes out as 12.0pt, which is +%D often not the way users specify the bodyfont size. Therefore +%D we also store the normalized value. + +\chardef\fontdigits=2 % was 1 + +% \def\normalizebodyfontsize#1\to#2% +% {\@EA\@EA\@EA\donormalizedbodyfontsize\@EA\WITHOUTPT\the\dimexpr#1+\ifcase\fontdigits.5\or.05\or.005\fi\points\relax000\to#2} +% +% \def\donormalizedbodyfontsize#1.#2#3#4#5\to#6% \points ? +% {\edef#6% not \ifcase#2\else due to \relax adding +% {#1% +% \ifcase\fontdigits +% \or \ifcase#2 \else .#2\fi % 1 +% \or \ifcase#2#3 \else .#2\ifcase#3 \else #3\fi\fi % 2 +% \else \ifcase#2#3#4 \else .#2\ifcase#4 \ifcase#3 \else#3\fi \else#3#4\fi\fi % 3 +% \fi +% \s!pt}} + +\def\normalizebodyfontsize#1\to#2% + {\edef#2{\ctxlua{fonts.nbfs(\number\dimexpr#1\relax,\number\fontdigits)}}} + +\def\thenormalizedbodyfontsize#1% + {\ctxlua{fonts.nbfs(\number\dimexpr#1\relax,\number\fontdigits)}} + +\normalizebodyfontsize\bodyfontsize\to\normalizedglobalbodyfontsize +\normalizebodyfontsize\bodyfontsize\to\normalizedlocalbodyfontsize +\normalizebodyfontsize\bodyfontsize\to\normalizedbodyfontsize + +%D \macros +%D {fontstyle,fontalternative,fontsize} +%D +%D Within a bodyfont, fonts can come in different sizes. For +%D instance \type{\tf} is accompanied by \type{\tfa}, +%D \type{\tfb} etc. The first two characters denote the +%D alternative, while the third character in these sequences +%D represents the size. The actual size is saved in a macro +%D +%D The style, being roman (\type{\rm}), sans serif (\type{\ss}) +%D etc. is also available in a macro in \type{rm}, \type{ss} +%D etc. form: + +\let\defaultfontalternative = \c!tf +\let\defaultfontstyle = \empty +\let\defaultfontsize = \empty + +\let\fontalternative = \defaultfontalternative +\let\fontstyle = \defaultfontstyle +\let\fontsize = \defaultfontsize + +%D When \type {\loadfontfileoncetrue}, such files are +%D only loaded once! This permits redundant loading, but at +%D the same time forced grouping when we want continuously mix +%D all kind of font, which of course is a kind of +%D typographically sin. The \type{"} is made inactive if +%D needed to prevent problems with loading files that use this +%D character in numbers. + +% can be made faster (only used internally now) + +\def\doswitchpoints[#1]% + {\normalexpanded{\dodoswitchpoints{#1}}} + +\unexpanded \def\dodoswitchpoints#1% + {\ifcsname\@size@#1\endcsname \else + \defineunknownfont{#1}% + \fi% + %\defineunknownfontstyles{#1}% + \ifcsname\@size@#1\endcsname + \csname\@size@#1\endcsname + \localbodyfontsize#1\relax + \normalizebodyfontsize\localbodyfontsize\to\normalizedbodyfontsize + % \edef\fontbody{\fontbody}% to be tested but we can clean up mkiv further + \checkbodyfontenvironment[\normalizedbodyfontsize]% + \else + \showmessage\m!fonts4{#1}% + \fi} + +\unexpanded \def\doswitchstyle[#1]% + {\ifcsname\@style@#1\endcsname + \csname\@style@#1\endcsname + \edef\fontstyle{#1}% + \ifmmode\mr\fi % in order to be compatible with \rm in math mode + % \the\everybodyfont % cleaner, in setting size as well as style + \else + \showmessage\m!fonts5{#1}% + \fi} + +%D \TEX\ loads font metric files like \type{cmr10.tfm} and +%D \type{tir.tfm} only once. In \PLAIN\ \TEX\ some font files +%D are {\em preloaded}. This means that the font files are +%D loaded, but not accessible yet by name. This is accomplished +%D by saying: +%D +%D \starttyping +%D \font\preloaded=cmr10 at 11pt +%D \stoptyping +%D +%D and using the name \type{\preloaded} again and again, so +%D fonts are indeed loaded, but unnamed, and therefore +%D unaccessible. In \CONTEXT\ we don't preload fonts, not even +%D the \PLAIN\ \TEX\ ones, although users can access them. Now +%D why is this done? + +%D Defining fonts using \type{\definebodyfont} takes time, so we +%D prefer to predefine at least the Computer Modern Roman +%D fonts. However, loading all those fonts at definition time +%D would take both time and space. But even worse, once fonts +%D are loaded into memory, their encoding vector is fixed, +%D which is a handicap when we want to distribute the compact +%D \type{fmt} files. So what we want to do is defining fonts in +%D a way that postpones the loading. We accomplish this by only +%D loading the fonts when we switch to another bodyfont size. +%D Among the other alternatives, such as loading the font at +%D the moment of activation and redefining the activation +%D macro afterwards, this proved to be the most efficient +%D alternative. +%D +%D The next few macros take care of the one exeption on this +%D scheme. When at format generation time we load the default +%D font file, the one that defines the Computer Modern Fonts, +%D we don't want the fonts metrics to end up in the format +%D file, so we temporary prohibit loading. This means that at +%D runtime we have to load the default bodyfont size just before +%D we start typesetting. +%D +%D Therefore we have to signal the font switching macros that +%D we are preloading fonts. As long as the next boolean is, +%D true, no loading is done. + +\newif\ifloadingfonts \loadingfontstrue + +%D \macros +%D {preloadfonts} +%D +%D Preloading is only called for once, during the startup +%D sequence of a session. After the loading job is done, the +%D macro relaxes itself and reset the signal. + +% \appendtoks +% \to \everysetupdocument + +\newconditional\fontsareloaded + +\def\preloadfonts % never called, needs a clean up + {\global\loadingfontsfalse + \ifconditional\fontsareloaded \else + \doifmodeelse {*nofonts} + {\writestatus\m!fonts{latin modern fonts are not preloaded}} + {\writestatus\m!fonts{preloading latin modern fonts}% + \usetypescript[modern]% + \setuptypeface[modern]% + \showmessage\m!fonts6{\normalizedbodyfontsize\normalspace\fontstyle}}% + \fi + \global\let\preloadfonts\relax} + +% maybe add this to \everystarttext +% +% \ifconditional\fontsareloaded\else +% \usetypescript[modern]% +% \setuptypeface[modern]% +% \fi + +%D Here comes the main font switching macros. These macros +%D handle changes in size as well as returning to the global +%D bodyfont size. + +\def\dosetfont#1#2% #1 = set/switch state + {\doifelse{#2}\v!global + {\restoreglobalbodyfont} + {\processcommacommand[#2]{\dodosetfont{#1}}% ##1 get also passed + \ifloadingfonts\else + \global\settrue\fontsareloaded + \doswitchpoints[\normalizedbodyfontsize]% + \doswitchstyle[\fontstyle]% + \ifx\defaultfontclass\empty + \let\defaultfontclass\fontclass + \fi + \fi}% + \chardef\currentxfontsize\zerocount} + +\def\dodosetfont#1#2% #1 = set/switch state | check fo rempty, else space + {\doifsomething{#2}{\dododosetfont{#1}{#2}{\showmessage\m!fonts4{#2}}}} + +% % % this can be retrofitted in mkii code % % % + +% \def\normalizebodyfontsize#1\to#2% +% {\@EA\@EA\@EA\donormalizedbodyfontsize\@EA\WITHOUTPT\the\dimexpr#1+\ifcase\fontdigits.5\or.05\or.005\fi\points\relax000\to#2} + +\def\dododosetfont#1#2#3% #1 = set/switch state ! ! ! !could also be used for mkii + {\doifnumberelse{#2}\dodododosetfont\redododosetfont{#1}{#2}{#3}} + +\def\redododosetfont#1#2#3% #1 = set/switch state ! ! ! !could also be used for mkii + {\edef\expandedfontthing{#2}% + \def\interfacedfontsize{\normalizedbodyfontsize\interfaced\expandedfontthing}% + \ifcsname\??ft\interfacedfontsize\endcsname + \edef\fontstep{\csname\bodyfontvariable\interfacedfontsize\endcsname}% + \normalexpanded{\noexpand\dodododosetfont{#1}{\fontstep}}{#3}% + \else\ifx\expandedfontthing\v!reset + \let\fontstyle\empty % new 31/7/2006 + \let\fontsize \empty + \else + \ifcsname\@style@\expandedfontthing\endcsname + \let\fontstyle\expandedfontthing + \else + \setcurrentfontclass\expandedfontthing + \ifcase#1\relax + \let\globalfontclass\globalfontclass + \else + \let\globalfontclass\fontclass + \fi + \ifx\fontclass\empty + \let\fontstyle\c!rm + \else\ifcsname\??tf\fontclass\s!default\endcsname + \edef\fontstyle{\csname\??tf\fontclass\s!default\endcsname}% + \else + \let\fontstyle\c!rm + \fi\fi + \fi + \fi\fi} + +\def\dodododosetfont#1#2#3% #1 = set/switch state + {\normalizebodyfontsize#2\to\normalizedsetfont + \ifcsname\@size@\normalizedsetfont\endcsname \else + \defineunknownfont{#2}% + \fi + \ifcsname\@size@\normalizedsetfont\endcsname + \localbodyfontsize\normalizedsetfont + \let\normalizedbodyfontsize\normalizedsetfont + \else + #3\dosetsubstitutefont{#1}{#2}% + \fi} + +% % % + +%D In the previous macros we use \type{\currentxfontsize} to +%D hold the current x||size of the font. This enables us to +%D support for instance \type{\sl} inside a \type{\tx} switch. + +\chardef\currentxfontsize=0 + +%D When users specify for instance a 13 point bodyfont while no +%D such bodyfont is defined, the system automatically tries to +%D find a best fit, that is the nearest smaller defined +%D bodyfontzize. A smaller one is definitely better than a larger +%D one, simply because otherwise a lot of overfull box messages +%D are more probable to occur. By taking a value slightly +%D smaller than half a point, we can use the next method. + +\def\dosetsubstitutefont#1#2% #1 = set/switch state + {\scratchdimen#2\relax + \advance\scratchdimen .499\points + \dimensiontocount\scratchdimen\scratchcounter + \advance\scratchcounter \minusone + \ifnum\scratchcounter>\plusthree + \dododosetfont{#1}{\the\scratchcounter\s!pt}{}% + \fi} + +% The following bunch of macros deals with the (run time) +% expansion of names onto the definitions made by \type +% {\definebodyfont}. + +% \let\fontbody \empty % ... 10pt 11pt 12pt ... +% \let\fontstyle \empty % rm ss tt mm hw cg ... +% \let\fontalternative\empty % tf bf sl it bs bi sc ... +% \let\fontsize \empty % xy-abcd ... + +\def\defaultfontbody{\normalizedbodyfontsize} + +\let\fontbody\defaultfontbody + +\let\fontclass\empty \let\globalfontclass\fontclass + +% we need to check the fontclass + +\def\registerfontclass#1% + {\letgvalue{\@fontclass@#1}\v!yes} % global ? + +\edef\@no@fontclass@{\@fontclass@:?:} + +\def\setcurrentfontclass#1% + {\ifcsname\@fontclass@#1\endcsname + \edef\fontclass{#1}% + \else\ifcsname\@no@fontclass@#1\endcsname + % already tried + \else\ifcase\currentgrouplevel + \trycurrentfontclass{#1}% + \fi\fi\fi} + +\def\savefontclassparameters#1#2#3#4% #1=rm|ss|.. rscale features fallbacks + {\setxvalue{\fontclass#1\s!rscale }{#2}% + \setxvalue{\fontclass#1\s!features }{#3}% + \setxvalue{\fontclass#1\s!fallbacks}{#4}} + +\settrue\autotypescripts + +% \def\trycurrentfontclass#1% +% {\ifconditional\autotypescripts +% \usetypescript[#1]% +% \ifcsname\@fontclass@#1\endcsname +% \edef\fontclass{#1}% +% \else +% \letvalue{\@no@fontclass@#1}\empty +% \fi +% \else +% \letvalue{\@no@fontclass@#1}\empty +% \fi} + +\def\trycurrentfontclass#1% + {\ifconditional\autotypescripts + % try to load typescript #1 + \usetypescript[#1]% + \ifcsname\@fontclass@#1\endcsname + \edef\fontclass{#1}% + \else + % try to load type-#1.mkiv + \usetypescriptfile[\f!typeprefix#1]% + % try to load typescript #1 + \usetypescript[#1]% + \ifcsname\@fontclass@#1\endcsname + \edef\fontclass{#1}% + \else + % todo: message + \letvalue{\@no@fontclass@#1}\empty + \fi + \fi + \else + % todo: message + \letvalue{\@no@fontclass@#1}\empty + \fi} + +\let\defaultfontstyle \c!rm +\let\defaultfontalternative \c!tf +\let\defaultfontsize \empty + +%D \macros +%D {bigmath,nobigmath} +%D +%D We can inhibit this slow||downer with: + +% these can best be combined + +% 0=never 1=everymath 2=always + +\chardef\synchronizebigmathflag=1 + +\appendtoks + \ifcase\synchronizebigmathflag + % never + \or + \synchronizebigmath + \or + % always + \fi +\to \everymathematics + +\def\nobigmath {\chardef\synchronizebigmathflag\zerocount} +\def\autobigmath{\chardef\synchronizebigmathflag\plusone\synchronizebigmath} +\def\bigmath {\chardef\synchronizebigmathflag\plustwo\synchronizebigmath} + +\let\bigmathfontsize\empty + +\def\synchronizebigmath + {\ifx\bigmathfontsize\fontsize + % already in sync + \else + \let\bigmathfontsize\fontsize + \synchronizemath + \fi} + +\def\checkbigmathsynchronization + {\ifcase\synchronizebigmathflag + % never + \or + \ifmmode \synchronizebigmath \fi + \or + \synchronizebigmath + \fi} + +%D So far for synchronisation. (We can inline the following macros.) + +\def\dosetcurrentfontsize#1% + {\edef\fontsize{#1}% + \checkbigmathsynchronization} + +\def\dosetcurrentfontalternative#1% + {\edef\fontalternative{#1}} + +\def\setcurrentfont#1#2#3#4% + {%\message{[1 #1 #2 #3 #4]}% + \edef\fontbody{#1}% + \edef\fontstyle{#2}% + \dosetcurrentfontalternative{#3}% + \dosetcurrentfontsize{#4}% + \synchronizefont} + +\def\setcurrentfontbody#1% + {%\message{[2 #1]}% + \edef\fontbody{#1}% + \synchronizefont} + +% For Taco: optional fall backs: + +\ifx\checkfontclass\undefined \let\checkfontclass\gobbleoneargument \fi % implemented in type-ini + +% \def\setcurrentfontstyle#1% +% {%\message{[3 #1]}% +% \checkfontclass{#1}% +% \edef\fontstyle{#1}% +% \ifmmode\mr\fi % otherwise \rm not downward compatible +% \synchronizefont} + +\def\setcurrentfontstyle#1% + {%\message{[3 #1]}% + \edef\fontstyle{#1}% + \checkfontclass\fontstyle + \ifmmode\mr\fi % otherwise \rm not downward compatible + \synchronizefont} + +\def\setcurrentfontbodyalternative#1#2% + {%\message{[4 #1 #2]}% + \edef\fontbody{#1}% + \dosetcurrentfontalternative{#2}% + \synchronizefont} + +\def\setcurrentfontalternative#1% + {%\message{[5 #1]}% + \dosetcurrentfontalternative{#1}% + \synchronizefont} + +\def\setcurrentfontsize#1% + {%\message{[6 #1]}% + \dosetcurrentfontsize{#1}% + \synchronizefont} + +\def\setcurrentfontstylealternative#1#2% \rmsl + {%\message{[7 #1 #2]}% + \edef\fontstyle{#1}% + \dosetcurrentfontalternative{#2}% + \synchronizefont} + +\def\setcurrentfontstylesize#1#2% \rmsla + {%\message{[8 #1 #2]}% + \edef\fontstyle{#1}% + \dosetcurrentfontsize{#2}% + \synchronizefont} + +\def\setcurrentfontalternativesize#1#2% \sla + {%\message{[9 #1 #2]}% + \dosetcurrentfontalternative{#1}% + \dosetcurrentfontsize{#2}% + \synchronizefont} + +\def\setcurrentfontstylealternativesize#1#2#3% \rmsla + {%\message{[10 #1 #2 #3]}% + \edef\fontstyle{#1}% + \dosetcurrentfontalternative{#2}% + \dosetcurrentfontsize{#3}% + \synchronizefont} + +%D In principle one can assign alternative fallback routines. +%D Some day we will. + +\newtoks\fontstrategies +\newif\iftryingfont + +\let\fontstrategy\relax + +\def\synchronizefont % we can have dups i.e. no need to let fontstrategy + {\tryingfonttrue + \ifx\fontclass\empty + \applyfontstrategies + \else + \applyfontclassstrategies + \fi + \autofontsizefalse + \ifskipfontcharacteristics + \setfontcharacteristics + \the\everyfontswitch + \fi} + +\def\fontclassstrategiesa % --- --- --- --- % pt tt bf a + {\ifcsname\fontclass\fontbody \fontstyle \fontalternative \fontsize\endcsname + \autofontsizefalse + \csname\fontclass\fontbody \fontstyle \fontalternative \fontsize\endcsname + \else + \expandafter\fontclassstrategiesb + \fi} + +\def\fontclassstrategiesb % --- --- --- def % pt tt bf + {\ifcsname\fontclass\fontbody \fontstyle \fontalternative \defaultfontsize\endcsname + \autofontsizetrue + \csname\fontclass\fontbody \fontstyle \fontalternative \defaultfontsize\endcsname + \else + \expandafter\fontclassstrategiesc + \fi} + +\def\fontclassstrategiesc % --- --- def --- % pt tt tf a + {\ifcsname\fontclass\fontbody \fontstyle \defaultfontalternative \fontsize\endcsname + \autofontsizetrue + \csname\fontclass\fontbody \fontstyle \defaultfontalternative \fontsize\endcsname + \else + \expandafter\fontclassstrategiesd + \fi} + +\def\fontclassstrategiesd % --- --- def def % pt tt tf + {\ifcsname\fontclass\fontbody \fontstyle \defaultfontalternative \defaultfontsize\endcsname + \autofontsizetrue + \csname\fontclass\fontbody \fontstyle \defaultfontalternative \defaultfontsize\endcsname + \else + \expandafter\fontclassstrategiese + \fi} + +\def\fontclassstrategiese % --- def def def % pt rm tf + {\ifcsname\fontclass\fontbody \defaultfontstyle \defaultfontalternative \defaultfontsize\endcsname + \autofontsizefalse + \csname\fontclass\fontbody \defaultfontstyle \defaultfontalternative \defaultfontsize\endcsname + \else + \expandafter\fontclassstrategiesf + \fi} + +\def\fontclassstrategiesf % def def def def % rm tf + {\ifcsname\fontclass\defaultfontbody \defaultfontstyle \defaultfontalternative \defaultfontsize\endcsname + \autofontsizetrue + \csname\fontclass\defaultfontbody \defaultfontstyle \defaultfontalternative \defaultfontsize\endcsname + \else + \expandafter\fontstrategiesa + \fi} + +% no class + +\def\fontstrategiesa % --- --- --- --- % pt tt bf a + {\ifcsname\fontbody \fontstyle \fontalternative \fontsize\endcsname + \autofontsizefalse + \csname\fontbody \fontstyle \fontalternative \fontsize\endcsname + \else + \expandafter\fontstrategiesb + \fi} + +\def\fontstrategiesb % --- --- --- --- % pt tt bf a + {\ifcsname\fontbody \fontstyle \fontalternative \defaultfontsize\endcsname + \autofontsizetrue + \csname\fontbody \fontstyle \fontalternative \defaultfontsize\endcsname + \else + \expandafter\fontstrategiesc + \fi} + +\def\fontstrategiesc % --- --- --- --- % pt tt bf a + {\ifcsname\fontbody \fontstyle \defaultfontalternative \fontsize\endcsname + \autofontsizetrue + \csname\fontbody \fontstyle \defaultfontalternative \fontsize\endcsname + \else + \expandafter\fontstrategiesd + \fi} + +\def\fontstrategiesd % --- --- --- --- % pt tt bf a + {\ifcsname\fontbody \fontstyle \defaultfontalternative \defaultfontsize\endcsname + \autofontsizetrue + \csname\fontbody \fontstyle \defaultfontalternative \defaultfontsize\endcsname + \else + \expandafter\fontstrategiese + \fi} + +\def\fontstrategiese % --- --- --- --- % pt tt bf a + {\ifcsname\fontbody \defaultfontstyle \defaultfontalternative \defaultfontsize\endcsname + \autofontsizefalse + \csname\fontbody \defaultfontstyle \defaultfontalternative \defaultfontsize\endcsname + \else + \expandafter\fontstrategiesf + \fi} + +\def\fontstrategiesf % --- --- --- --- % pt tt bf a + {\ifcsname\defaultfontbody \defaultfontstyle \defaultfontalternative \defaultfontsize\endcsname + \autofontsizetrue + \csname\defaultfontbody \defaultfontstyle \defaultfontalternative \defaultfontsize\endcsname + \fi} + +\let\applyfontstrategies \fontstrategiesa +\let\applyfontclassstrategies\fontclassstrategiesa + +%D Let's synchronize: + +\newif\ifsynchronizefonts \synchronizefontstrue + +\prependtoks + \ifsynchronizefonts + \synchronizemath + \synchronizefont % problem: syncs last font + \fi +\to \everybodyfont + +%D Setting the normal sizes as well as the x and xx smaller +%D sizes is accomplished by the next set of macros. When in +%D math mode, the commands \type{\tx} and \type{\txx} are +%D just a switch to the script and double script styles, but +%D in text mode the values defined by the bodyfontenvironment are +%D used. Here we also set \type{\currentxfontsize}. + +\def\dosetcurrentfontxxxalternative#1#2#3#4% + {\chardef\currentxfontsize#2\relax + \ifmmode + #4% + \else\ifcsname\bodyfontvariable{\normalizedbodyfontsize#3}\endcsname + \setcurrentfontbodyalternative{\csname\bodyfontvariable\normalizedbodyfontsize#3\endcsname}{#1}% + \fi\fi} + +\def\setcurrentfontxalternative#1% + {\dosetcurrentfontxxxalternative{#1}1\c!x\scriptstyle + \let\tx\txx} + +\def\setcurrentfontxxalternative#1% + {\dosetcurrentfontxxxalternative{#1}2\c!xx\scriptscriptstyle + \let\tx\empty + \let\txx\empty} + +\def\checknestedxfontsize % option + {\ifcase\currentxfontsize\else\ifx\fontsize\empty\else + \chardef\currentxfontsize\zeropoint + \let\fontsize\empty + \let\tx\normaltx + \let\txx\normaltxx + \fi\fi} + +\def\setcurrentfontxalternative#1% + {\checknestedxfontsize + \dosetcurrentfontxxxalternative{#1}1\c!x\scriptstyle + \let\tx\txx} + +\def\setcurrentfontxxalternative#1% + {\checknestedxfontsize + \dosetcurrentfontxxxalternative{#1}2\c!xx\scriptscriptstyle + \let\tx\empty + \let\txx\empty} + +% This alterative is not really needed, but for old time's sake +% we keep it there. We can speed it up when needed. + +\def\setcurrentfontxstylealternative #1{\csname#1\endcsname\tx} +\def\setcurrentfontxxstylealternative#1{\csname#1\endcsname\txx} + +%D These macros also show us that when we call for \type{\tx}, +%D this macro is redefined to be \type{\txx}. Therefore calls +%D like: +%D +%D \startbuffer +%D {small \tx is \tx beautiful} +%D {small \tx is \txx beautiful} +%D {small \txx is \tx beautiful} +%D {small \txx is \txx beautiful} +%D \stopbuffer +%D +%D \typebuffer +%D +%D result in: +%D +%D \startvoorbeeld +%D \startlines +%D \getbuffer +%D \stoplines +%D \stopvoorbeeld +%D +%D Setting the main size involves the style list and therefore +%D takes a bit more time. Keep in mind that the fontsize is +%D represented by a character or empty. + +\unexpanded\def\tx {\setcurrentfontxalternative \fontalternative} +\unexpanded\def\txx{\setcurrentfontxxalternative\fontalternative} + +\let\normaltx \tx +\let\normaltxx\txx + +%D \macros +%D {definefontstyle} +%D +%D When setting of switching the overall style we can use the +%D short identifier like rm and ss, but when defined we can +%D also use more verbose names like roman or sansserif. Such +%D names are defined by: +%D +%D \starttyping +%D \definefontstyle [serif,rm] [rm] +%D \definefontstyle [sansserif,ss] [ss] +%D \stoptyping + +\def\dodefinefontstyle[#1][#2]% + {\rawdoifinsetelse{#2}{\fontstylelist} + {%\debuggerinfo\m!fonts{unknown style #2}% + } + {%\debuggerinfo\m!fonts8{#2\space (#1)}% + \addtocommalist{#2}\fontstylelist}% + % check kan hier + \def\docommand##1% + {\setvalue{\@shortstyle@##1}{#2}% + \setvalue{\@style@##1}{\csname#2\endcsname}}% + \processcommalist[#1]\docommand} + +\unexpanded\def\definefontstyle + {\dodoubleargument\dodefinefontstyle} + +\def\setfontstyle#1#2% #1:name (roman, romaan) #2:style (rm) + {\edef\fontstyle{#1}% + \checkfontnamecombinations + \setcurrentfontstyle\normalizedbodyfontsize} + +%D When asking for a complete font switch, for instance from 10 +%D to 12~points, the next macro does the job. First we +%D normalize the size, next we define the current range of +%D text, script and scriptscript sizes, then we set the text +%D fonts and the math families and finally we activate the +%D default typeface and also set the font specific parameters +%D assigned to \type{\everybodyfont} + +\def\dosetbodyfontface#1#2% + {\edef#1{\csname\bodyfontvariable\normalizedbodyfontsize#2\endcsname}} + +\def\docompletefontswitch[#1]% + {\bodyfontsize#1\relax + \dimensiontocount\bodyfontsize\bodyfontpoints % rounded, still used in m-chart + \edef\bodyfontfactor{\withoutpt\the\bodyfontsize}% + \normalizebodyfontsize\bodyfontsize\to\normalizedbodyfontsize + \dosetbodyfontface \textface \s!text + \dosetbodyfontface \scriptface \s!script + \dosetbodyfontface \scriptscriptface \s!scriptscript} + +\docompletefontswitch[12pt] % init + +%D \macros +%D {setupbodyfont,switchtobodyfont} +%D +%D The next two macros are user ones. With \type{\setupbodyfont} +%D one can set the document bodyfont size, font family, style +%D and/or options defined in files, for example: +%D +%D \starttyping +%D \setupbodyfont[modern,12pt,roman] +%D \stoptyping +%D +%D This command affects the document as a whole: text, headers +%D and footers. The second macro however affects only the text: +%D +%D \starttyping +%D \switchtobodyfont[10pt] +%D \stoptyping +%D +%D So we've got: +%D +%D \showsetup{setupbodyfont} +%D \showsetup{switchtobodyfont} +%D +%D Both macros look alike. The second one also has to take +%D all kind of keywords into account. + +\ifx\saveinterlinespace \undefined \let\saveinterlinespace \relax \fi +\ifx\restoreinterlinespace\undefined \let\restoreinterlinespace\relax \fi + +% \newtoks \everysetupbodyfont +% \newtoks \everyswitchtobodyfont + +\chardef\bodyfontsetstate=0 + +\definecomplexorsimple\setupbodyfont + +\def\simplesetupbodyfont + {\restoreglobalbodyfont + \saveinterlinespace} + +\def\complexsetupbodyfont[#1]% + {\doifsomething{#1} + {\dosetfont1{#1}% + \globalbodyfontsize\localbodyfontsize + \normalizebodyfontsize\globalbodyfontsize\to\normalizedglobalbodyfontsize + \let\globalfontstyle\fontstyle + \ifloadingfonts\else + \the\everybodyfont + \the\everyglobalbodyfont + \saveinterlinespace + \fi + \the\everysetupbodyfont}} + +\unexpanded\def\switchtobodyfont[#1]% + {\doifsomething{#1} + {\ifcsname\??ft\normalizedbodyfontsize\interfaced{#1}\endcsname + \setbodyfontstep{#1}% so we have a fast [small] switch + \else + \dosetfont0{#1}% + \fi + \the\everybodyfont + \the\everyswitchtobodyfont}} + +%D The following alternative is meant for math||to||text +%D switching and will be optimized. + +\def\fastswitchtobodyfont#1% + {\ifcsname\??ft\normalizedbodyfontsize#1\endcsname + \edef\futurebodyfontsize{\csname\??ft\normalizedbodyfontsize#1\endcsname}% + \ifcsname\@size@\futurebodyfontsize\endcsname + \csname\@size@\futurebodyfontsize\endcsname + \localbodyfontsize\futurebodyfontsize\relax + \fi + \fi + \csname\@style@\fontstyle\endcsname + \the\everybodyfont} + +%D \starttyping +%D $\cases{& \ccaron}$ $x=\hbox{\ccaron $x=\hbox{\ccaron}$}$ +%D \stoptyping + +\def\setfontcharacteristics + {\the\everyfont} + +%D Predefined: + +% \installfontfeature[otf][tlig] +% \installfontfeature[otf][trep] + +%D tricky but ok: + +\appendtoks\ctxlua{fonts.tfm.cleanup()}\to\everyshipout + +%D Todo: + +% \def\os{\groupedcommand{\setfontfeature{oldstyle}}{}} + +%D Experimental: + +\unexpanded\def\definefontfeature + {\dotripleargument\dodefinefontfeature} + +\def\dodefinefontfeature[#1][#2][#3]% + {\global\expandafter\chardef\csname\??fq=#1\endcsname % beware () needed as we get two values returned + \ctxlua{tex.write((fonts.define.specify.preset_context("#1","#2","#3")))}\relax} + +\definefontfeature + [default] + [%mode=node, + liga=yes,kern=yes,tlig=yes,trep=yes] % texligatures=yes,texquotes=yes + +\definefontfeature + [smallcaps] + [%mode=node,liga=yes, + smcp=yes,kern=yes,tlig=yes,trep=yes] % texligatures=yes,texquotes=yes + +\definefontfeature + [oldstyle] + [%mode=node, + onum=yes,liga=yes,kern=yes,tlig=yes,trep=yes] % texligatures=yes,texquotes=yes + +\definefontfeature % == default unless redefined + [ligatures] + [%mode=node, + liga=yes,kern=yes,tlig=yes,trep=yes] + +\definefontfeature % can be used for type1 fonts + [complete] + [liga=yes,kern=yes,compose=yes,tlig=yes,trep=yes] + +\definefontfeature + [arabic] + [mode=node,language=dflt,script=arab,ccmp=yes, + init=yes,medi=yes,fina=yes,isol=yes, + liga=yes,dlig=yes,rlig=yes,clig=yes,calt=yes, + mark=yes,mkmk=yes,kern=yes,curs=yes] + +\definefontfeature + [none] + [mode=none,features=no] + +\definefontfeature + [virtualmath] + [mode=base,liga=yes,kern=yes,tlig=yes,trep=yes] + +% for the moment here, this will change but we need it for mk.tex + +\definefontfeature[math-text] [virtualmath][ssty=no] +\definefontfeature[math-script] [virtualmath][ssty=1,mathsize=yes] +\definefontfeature[math-scriptscript][virtualmath][ssty=2,mathsize=yes] + +\definefontfeature [math-nostack-text] [math-text] [nostackmath=yes] +\definefontfeature [math-nostack-script] [math-script] [nostackmath=yes] +\definefontfeature [math-nostack-scriptscript][math-scriptscript][nostackmath=yes] + +% \definefontfeature[mathtext] [math-text] +% \definefontfeature[mathscript] [math-script] +% \definefontfeature[mathscriptscript] [math-scriptscript] + +%D Also new, handy for manuals: + +\unexpanded\def\fontchar#1{\ctxlua{fonts.char("#1")}} + +\let\otfchar\fontchar % will disappear, for compatibility only +\let\afmchar\fontchar % will disappear, for compatibility only + +%D: We cannot yet inherit because no colors are predefined. + +\definecolor[font:init][r=.75] +\definecolor[font:medi][g=.75] +\definecolor[font:fina][b=.75] +\definecolor[font:isol][r=.75,g=.75] % [y=.75] +\definecolor[font:mark][r=.75,b=.75] % [m=.75] +\definecolor[font:rest][g=.75,b=.75] % [c=.75] + +%D Experimental! + +\def\installfontfeature + {\dodoubleargument\doinstallfontfeature} + +\def\doinstallfontfeature[#1][#2]% + {\writestatus\m!fonts{installing font features was experimental}} % \ctxlua{fonts.install_feature("#1","#2")}} + +%D Not yet in \MKII. + +\def\fontfeatureslist + {\dodoubleargument\dofontfeatureslist} + +\def\dofontfeatureslist[#1][#2]% todo: arg voor type + {\ctxlua{tex.sprint(tex.ctxcatcodes,fonts.define.specify.context_tostring("#1","otf","\luaescapestring{#2}","yes","no",true,{"number"}))}} + +\attribute\zerocount\zerocount % first in list, so fast match + +\let\currentfeature\empty + +% ! ! ! very experimental, some test code for idris advanced features ! ! ! +% +% \startbuffer +% \definefontfeature[smallcaps][smallcaps][script=latn] +% \definefontfeature[oldstyle] [oldstyle] [script=latn] +% +% \definedfont[name:cambria at 15pt] +% +% Hello there {\setff{smallcaps}capped 123 \setff{oldstyle}123!} \blank +% Hello there {\addff{smallcaps}capped 123 \addff{oldstyle}123!} \blank +% Hello there {\addff{smallcaps}capped \subff{smallcaps}normal} \blank +% \stopbuffer +% +% \typebuffer \getbuffer + +\def\featureattribute#1{\ctxlua{tex.sprint(fonts.define.specify.context_number("#1"))}} +\def\setfontfeature #1{\edef\currentfeature{#1}\attribute\zerocount\featureattribute{#1}\relax} +\def\resetfontfeature#1{\let\currentfeature\empty\attribute\zerocount\zerocount} % initial value + +\def\addfontfeaturetoset #1{\ctxlua{fonts.withset("#1", 1)}} % merge +\def\subtractfontfeaturefromset #1{\ctxlua{fonts.withset("#1",-1)}} % merge +\def\addfontfeaturetofont #1{\ctxlua{fonts.withfnt("#1", 2)}} % overload +\def\subtractfontfeaturefromfont#1{\ctxlua{fonts.withfnt("#1",-2)}} % overload + +\let\setff\setfontfeature +\let\addfs\addfontfeaturetoset +\let\subfs\subtractfontfeaturefromset +\let\addff\addfontfeaturetofont +\let\subff\subtractfontfeaturefromfont + +%D The next auxilliary macro is an alternative to \type +%D {\fontname}. + +\def\purefontname#1{\ctxlua{file.basename("\fontname#1"}} % will be function using id + +%D \macros +%D {switchstyleonly} +%D +%D For switching a style but keeping the alternative, there +%D is: +%D +%D \starttyping +%D {\bf text \switchstyleonly\ss text} +%D {\bf text \switchstyleonly[ss]text} +%D {\sl text \switchstyleonly[sansserif]text} +%D \stoptyping + +\definecomplexorsimple\switchstyleonly + +\def\simpleswitchstyleonly#1% stupid version + {\complexswitchstyleonly[\checkedstrippedcsname#1]} + +\def\complexswitchstyleonly[#1]% todo : check + {\setcurrentfontstyle{\csname\@shortstyle@#1\endcsname}% + \the\everybodyfont} % needed ? + +%D \macros +%D {os} +%D +%D In good old \TEX, the old style numerals were often taken +%D from the math fonts. No longer. + +\definefontfeature + [just-os] + [mode=node,onum=yes] + +% \def\sc{\setfontfeature{smallcaps}} +\unexpanded\def\os{\setfontfeature{just-os}} + +%D Code for switching to fraktur and script has also been +%D changed. We now have an alphabet switcher. + +\ifx\mathtext\undefined \let\mathtext\hbox \fi + +%D \macros +%D {definebodyfontswitch} +%D +%D \PLAIN\ \TEX\ defines some macro's like \type{\tenpoint} +%D to switch to a specific bodyfontsize. Just for the sake of +%D compatibility we can define them like: +%D +%D \starttyping +%D \definebodyfontswitch [twelvepoint] [12pt] +%D \stoptyping +%D +%D We don't support language specific synonyms here, mainly +%D because \PLAIN\ \TEX\ is english anyway. + +\def\dodefinebodyfontswitch[#1][#2]% + {\def\docommand##1{\setvalue{##1}{\switchtobodyfont[#2]}}% + \processcommalist[#1]\docommand} + +\unexpanded\def\definebodyfontswitch + {\dodoubleargument\dodefinebodyfontswitch} + +%D \macros +%D {setsmallbodyfont,setmainbodyfont,setbigbodyfont} +%D +%D When we're typesetting at for instance 10pt, we can call for +%D the \type{small} as well as the \type{big} alternative, +%D related to this main size, using \type{\switchtobodyfont[small]}. +%D The three alternatives can be activated by the next three +%D system calls and are defined by the bodyfontenvironment. + +\let\fontstep\empty % we can use \fontstep for tracing purposes + +\def\setbodyfontstep#1% + {\edef\fontstep{\csname\bodyfontvariable\normalizedbodyfontsize\interfaced{#1}\endcsname}% + \doswitchpoints[\fontstep]% + \doswitchstyle[\fontstyle]} + +\unexpanded\def\setsmallbodyfont{\setbodyfontstep\v!small\the\everybodyfont} +\unexpanded\def\setbigbodyfont {\setbodyfontstep\v!big \the\everybodyfont} + +\unexpanded\def\setmainbodyfont + {\doswitchpoints[\normalizedbodyfontsize]% + \doswitchstyle[\fontstyle]% + \the\everybodyfont + \the\everyglobalbodyfont + \saveinterlinespace} + +%D \macros +%D {restoreglobalbodyfont} +%D +%D Users can set whatever font available while typesetting text. +%D Pagenumbers, footers, headers etc. however must be typeset +%D in the main bodyfont and style of the document. Returning to +%D the global state can be done with the next macro: + +\let\globalfontstyle\c!rm + +\def\fullrestoreglobalbodyfont + {\let\fontsize\defaultfontsize + \let\fontbody\defaultfontbody + \chardef\currentxfontsize\zerocount + \let\fontclass\globalfontclass + \doswitchpoints[\normalizedglobalbodyfontsize]% + \doswitchstyle[\globalfontstyle]% + \redoconvertfont % just in case a pagebreak occurs + \tf + \the\everybodyfont + \the\everyglobalbodyfont + \saveinterlinespace} + +\def\partialrestoreglobalbodyfont + {\let\fontsize\defaultfontsize + \let\fontbody\defaultfontbody + \chardef\currentxfontsize\zerocount + \redoconvertfont + \tf + \the\everybodyfont % indeed needed + \the\everyglobalbodyfont % indeed needed + \saveinterlinespace} + +\def\restoreglobalbodyfont % ook style etc + {\ifx\fontclass\globalfontclass + \ifx\fontstyle\globalfontstyle + \ifx\normalizedbodyfontsize\normalizedglobalbodyfontsize + \partialrestoreglobalbodyfont + \else + \fullrestoreglobalbodyfont + \fi + \else + \fullrestoreglobalbodyfont + \fi + \else + \fullrestoreglobalbodyfont + \fi} + +% in case of troubles: \let\restorebodyfont\fullrestoreglobalbodyfont + +%D This macro has to be called when entering the pagebody +%D handling routine as well as the footnote insert routine. +%D Users can access this feature |<|for instance when one wants +%D to typeset tables and alike in the main bodyfont and style +%D while the running text is temporary set to a smaller one|>| +%D by saying \type{\switchtobodyfont[global]}. + +%D \macros +%D {rasterfont} +%D +%D There are (at the moment) two situations in which we want to +%D have fast access to a particular font. When we are using +%D \TEX\ to typeset rasters, we use small {\rasterfont.}'s +%D (a rather small period indeed), the same as \PICTEX\ uses +%D for drawing purposes. + +\definefont [rasterfont] [Serif at 5pt] + +%D \macros +%D {infofont} +%D +%D The second situation occurs when we enable the info mode, +%D and put all kind of status information in the margin. We +%D don't want huge switches to the main bodyfont and style, so +%D here too we use a direct method. + +\let\infofont\relax % satisfy dep checker + +\definefont [infofont] [Mono at 6pt] % todo \the\everybodyfont + +%D \macros +%D {definealternativestyle} +%D +%D In the main modules we are going to implement lots of +%D parameterized commands and one of these parameters will +%D concern the font to use. To suit consistent use of fonts we +%D here implement a mechanism for defining the keywords that +%D present a particular style or alternative. +%D +%D \starttyping +%D \definealternativestyle [keywords] [\style] [\nostyle] +%D \stoptyping +%D +%D The first command is used in the normal textflow, while the +%D second command takes care of headings and alike. Consider +%D the next two definitions: +%D +%D \starttyping +%D \definealternativestyle [bold] [\bf] [] +%D \definealternativestyle [cap] [\cap] [\cap] +%D \stoptyping +%D +%D A change \type{\bf} in a heading which is to be set in +%D \type{\tfd} does not look that well, so therefore we leave +%D the second argument of \type{\definealternativestyle} empty. +%D When we capatalize characters using the pseudo small cap +%D command \type{\cap}, we want this to take effect in both +%D text and headings, which is accomplished by assigning both +%D arguments. + +\def\dodefinealternativestyle[#1][#2][#3]% + {\def\docommand##1% + {\ifcsname##1\endcsname\else\setuvalue{##1}{\groupedcommand{#2}{}}\fi + \setvalue{\@letter@ ##1}{#2}% + \setvalue{\@noletter@##1}{#3}}% + \processcommalist[#1]\docommand} + +\unexpanded\def\definealternativestyle + {\dotripleempty\dodefinealternativestyle} + +\unexpanded\def\definestyle{\definealternativestyle} + +%D Maybe too geneneric, but probably ok is the following. (Maybe one +%D day we will use a dedicated grouped command for styles.) + +\appendtoks + \let\groupedcommand\thirdofthreearguments +\to \simplifiedcommands + +%D This command also defines the keyword as command. This means +%D that the example definition of \type{bold} we gave before, +%D results in a command \type{\bold} which can be used as: +%D +%D \startbuffer +%D He's a \bold{bold} man with a {\bold head}. +%D \stopbuffer +%D +%D \typebuffer +%D +%D or +%D +%D \startvoorbeeld +%D \definealternativestyle[bold][\bf][]\getbuffer +%D \stopvoorbeeld +%D +%D Such definitions are of course unwanted for \type{\cap} +%D because this would result in an endless recursive call. +%D Therefore we check on the existance of both the command and +%D the substitution. The latter is needed because for instance +%D \type{\type} is an entirely diferent command. That command +%D handles verbatim, while the style command would just switch +%D to teletype font. This is just an example of a tricky +%D naming coincidence. + +%D \macros +%D {doconvertfont,noconvertfont, +%D dontconvertfont,redoconvertfont} +%D +%D After having defined such keywords, we can call for them by +%D using +%D +%D \starttyping +%D \doconvertfont{keyword}{text} +%D \stoptyping +%D +%D We deliberately pass an argument. This enables us to +%D assign converters that handle one argument, like +%D \type{\cap}. +%D +%D By default the first specification is used to set the style, +%D exept when we say \type{\dontconvertfont}, after which the +%D second specification is used. We can also directly call for +%D \type{\noconvertfont}. In nested calls, we can restore the +%D conversion by saying \type{\redoconvertfont}. + +% subtle ... \expandafter is needed else problems with lookahead caps + +\def\@@dodoconvertfont{\csname\@letter@ \p!defined\expandafter\endcsname\gobbleoneargument} +\def\@@donoconvertfont{\csname\@noletter@\p!defined\endcsname} +\def\@@redoconvertfont{\csname \p!defined\expandafter\endcsname\gobbleoneargument} + +% beware: p!defined can contain crap like \edef crap {...} and such +% so we need to pass #1 as well + +\unexpanded\def\dodoconvertfont#1% #2% we need the protection + {\edef\p!defined{#1}% + \ifx\p!defined\empty\else + \@EA\dododoconvertfont + \fi{#1}} + +\def\dododoconvertfont % #1 + {\ifcsname\@letter@\detokenize\@EA{\p!defined}\endcsname + \@EA\@@dodoconvertfont + \else\ifcsname\detokenize\@EA{\p!defined}\endcsname + \@EAEAEA\@@redoconvertfont + \else + \@EAEAEA\firstofoneargument + \fi\fi} % {#1} + +\let\doconvertfont\dodoconvertfont + +\unexpanded\def\noconvertfont#1% #2% + {\edef\p!defined{#1}% + \ifx\p!defined\empty + \else + \@EA\nononoconvertfont + \fi} + +\def\nononoconvertfont + {\ifcsname\@noletter@\detokenize\@EA{\p!defined}\endcsname + \@EA\@@donoconvertfont + \fi} + +%D Extras: + +\unexpanded\def\dontconvertfont{\let\doconvertfont\noconvertfont} +\unexpanded\def\redoconvertfont{\let\doconvertfont\dodoconvertfont} + +%D These commands are not grouped! Grouping is most probably +%D done by the calling macro's and would lead to unnecessary +%D overhead. + +%D \macros +%D {em,emphasistypeface,emphasisboldface} +%D +%D The next macro started as a copy of Donald Arseneau's +%D \type{\em} (\TUGNEWS\ Vol.~3, no.~1, 1994). His +%D implementation was a bit more sophisticated version of the +%D standard \LATEX\ one. We furter enhanced the macro, so now +%D it also adapts itself to boldface mode. Because we favor +%D {\sl slanted} type over {\it italic}, we made the emphasis +%D adaptable, for instance: +%D +%D \starttyping +%D \def\emphasistypeface {\it} +%D \def\emphasisboldface {\bi} +%D \stoptyping +%D +%D But we prefer: + +\def\emphasistypeface {\sl} +\def\emphasisboldface {\bs} + +%D or even better: + +\def\doemphasistypeface#1#2% + {\edef\emphasizedtypeface{\csname\??ft\fontclass\normalizedbodyfontsize\c!em\endcsname}% + \ifx\emphasizedtypeface\v!slanted + #1% + \else\ifx\emphasizedtypeface\v!italic + #2% + \else\ifx\emphasizedtypeface\v!empty + \edef\emphasizedtypeface{\csname\??ft\normalizedbodyfontsize\c!em\endcsname}% + \ifx\emphasizedtypeface\v!slanted + #1% + \else\ifx\emphasizedtypeface\v!italic + #2% + \else + \getvalue\emphasizedtypeface + \fi\fi + \else + \getvalue\emphasizedtypeface + \fi\fi\fi} + +\def\emphasistypeface{\doemphasistypeface\sl\it} +\def\emphasisboldface{\doemphasistypeface\bs\bi} + +%D To be set with the default body font environment: \type +%D {em} being \type {slanted} or \type {italic}. + +\newconditional\emneeded + +\newtoks\everyemphasized + +\unexpanded\def\em + {\relax + \ifdim\slantperpoint>\zeropoint + \settrue\emneeded + \else + \setfalse\emneeded + \fi + \setemphasisboldface % new + \ifx\fontalternative\c!it + \def\emphasistypeface{\it}\tf + \else\ifx\fontalternative\c!sl + \def\emphasistypeface{\sl}\tf + \else\ifx\fontalternative\c!bf + \emphasisboldface + \else\ifx\fontalternative\c!bs + \def\emphasisboldface{\bs}\bf + \else\ifx\fontalternative\c!bi + \def\emphasisboldface{\bi}\bf + \else + \emphasistypeface + \fi\fi\fi\fi\fi + \the\everyemphasized + \ifconditional\emneeded\relax + \else + \expandafter\aftergroup + \fi + \emphasiscorrection} + +% compare ... +% +% \appendtoks \red \to \everyemphasized +% \setupbodyfontenvironment [default] [em={\italic\color[red]}] + +%D The next feature was not present in previous versions. It +%D takes care of \type {\em \bf ...} sitiations. + +\def\setemphasisboldface + {\let\savedemphasisboldface\bf + \let\setemphasisboldface\relax + \unexpanded\def\bf + {%\relax + \let\bf\relax % new + \ifx\fontalternative\c!it + \bi + \else\ifx\fontalternative\c!sl + \bs + \else + \savedemphasisboldface + \fi\fi + \let\bf\savedemphasisboldface}} + +%D Donald's (adapted) macros take the next character into +%D account when placing italic correction. As a bonus we also +%D look for something that looks like a dash, in which case we +%D don't correct. + +\let\italiccorrection=\/ % tex primitive + +\def\emphasiscorrection + {\ifhmode + \expandafter\emphasislook + \fi} + +\def\emphasislook + {\begingroup + \futurelet\next\emphasistest} + +\def\emphasistest + {\ifcat\noexpand\next,% still ok? + \expandafter\doemphasiscorrection + \else + \expandafter\dododoemphasiscorrection + \fi} + +\def\doemphasiscorrection + {\futurelet\next\dodoemphasiscorrection} + +\def\dodoemphasiscorrection + {\setbox\scratchbox\hbox{\next}% + \ifdim\ht\scratchbox=\zeropoint % probably a space + \expandafter\dododoemphasiscorrection + \else\ifdim\ht\scratchbox<.3ex + \expandafter\expandafter\expandafter\endgroup + \else + \expandafter\expandafter\expandafter\dododoemphasiscorrection + \fi\fi} + +\def\dododoemphasiscorrection + {\scratchskip\lastskip + \ifdim\scratchskip=\zeropoint\relax % == \ifzeropt\scratchskip + \italiccorrection\relax + \else + \unskip\italiccorrection\hskip\scratchskip + \fi + \endgroup} + +%D We end with some examples which show the behavior when +%D some punctuation is met. We also show how the mechanism +%D adapts itself to bold, italic and slanted typing. +%D +%D \startbuffer +%D test {test}test \par +%D test {\sl test}test \par +%D test {\em test}test \par +%D test {\em test}--test \par +%D +%D test {test}, test \par +%D test {\em test}, test \par +%D +%D test {\em test {\em test {\em test} test} test} test \par +%D test {\bf test {\em test {\em test} test} test} test \par +%D test {\sl test {\em test {\em test} test} test} test \par +%D test {\it test {\em test {\em test} test} test} test \par +%D \stopbuffer +%D +%D \typebuffer +%D +%D We get: +%D +%D \startvoorbeeld +%D \startpacked +%D \getbuffer +%D \stoppacked +%D \stopvoorbeeld + +%D \macros +%D {emphbf,emphit,emphsl,emphtf} +%D +%D The next emphasis alternatives are for \THANH. They adapt +%D their style as good as possible. + +\def\emphbf{\groupedcommand{\bf\def\emphit{\bi}\def\emphsl{\bs}}{}} +\def\emphit{\groupedcommand{\it\def\emphbf{\bi}\def\emphsl{\sl}}{}} +\def\emphsl{\groupedcommand{\sl\def\emphbf{\bs}\def\emphit{\it}}{}} +\def\emphtf{\groupedcommand{\tf\def\emphbf{\bf}\def\emphit{\it}\def\emphsl{\sl}}{}} + +%D \startbuffer +%D TEXT {\emphbf text \emphit text \emphtf text \emphsl text} TEXT +%D TEXT \emphbf{text \emphit{text} \emphtf{text} \emphsl{text}} TEXT +%D \stopbuffer +%D +%D \typebuffer +%D \startlines +%D \getbuffer +%D \stoplines + +%D \macros +%D {setfont} +%D +%D Every now and then we want to define a font directly, for +%D instance when we typeset title pages. The next macro saves +%D some typing: + +\def\setfont% geen \dosetfont mogelijk + {\def\next{\nextfont\setupinterlinespace}% hm, we need to use \setuplocalinterlinespace + \afterassignment\next\font\nextfont=} + +%D One can call this macro as: +%D +%D \starttyping +%D \setfont cmr10 at 60pt +%D \stoptyping +%D +%D After which the font is active and the baselines and +%D struts are set. + +%D \macros +%D {showbodyfont} +%D +%D One can call for a rather simple overview of a bodyfont and the +%D relations between its alternative fonts. +%D +%D \showsetup{showbodyfont} +%D +%D The current bodyfont (here we omitted the argument) looks like: +%D +%D \showbodyfont +%D +%D The implementation is rather straightforward in using +%D \type{\halign}. + +\fetchruntimecommand \showbodyfont {\f!fontprefix\s!run.mkiv} + +%D \macros +%D {showfontstrip, testminimalbaseline, showminimalbaseline} +%D +%D The next command can come in handy when combining +%D different fonts into a collection (typeface) and +%D determining optimal baseline distances. +%D +%D \showfontstrip \blank \showminimalbaseline + +\fetchruntimecommand \showfontstrip {\f!fontprefix\s!run.mkiv} +\fetchruntimecommand \testminimalbaseline {\f!fontprefix\s!run.mkiv} +\fetchruntimecommand \showminimalbaseline {\f!fontprefix\s!run.mkiv} + +%D \macros +%D {showkerning} +%D +%D A goody is: +%D +%D \showkerning{Can you guess what kerning is?} + +\fetchruntimecommand \showkerning {\f!fontprefix\s!run.mkiv} + +%D \macros +%D {showbodyfontenvironment} +%D +%D The current bodyfontenvironment is: +%D +%D \showbodyfontenvironment +%D +%D This overview is generated using: +%D +%D \showsetup{showbodyfontenvironment} + +\fetchruntimecommand \showbodyfontenvironment {\f!fontprefix\s!run.mkiv} + +%D \macros +%D {showfont,showfontstyle,showligatures} +%D +%D The following command generates a fontmap: +%D +%D \startbuffer +%D \showfont[SansBold at 12pt] +%D \stopbuffer +%D +%D \typebuffer +%D \getbuffer + +% to be internationalized + +\fetchruntimecommand \showfont {\f!fontprefix\s!run.mkiv} +\fetchruntimecommand \showfontstyle {\f!fontprefix\s!run.mkiv} +\fetchruntimecommand \showligature {\f!fontprefix\s!run.mkiv} +\fetchruntimecommand \showligatures {\f!fontprefix\s!run.mkiv} +\fetchruntimecommand \showcharratio {\f!fontprefix\s!run.mkiv} + +%D \macros +%D {getglyph, symbolicfont} +%D +%D Individual glyphs can be accessed by using +%D +%D \starttyping +%D \getglyph{fontname}{character} +%D \stoptyping +%D +%D This macro is used in for instance the symbol modules and +%D as one can see, it does obey the small and even smaller +%D sizes. The \type {\symbolicfont} macro can be used to +%D switch to a font named \type {fontname} (see \type +%D {cont-log} and \type {symb-eur} for examples of symbolic +%D definitions. + +\def\fontstringA + {\ifx\fontstyle\c!rm \s!Serif \else + \ifx\fontstyle\c!ss \s!Sans \else + \ifx\fontstyle\c!tt \s!Mono \else + \s!Serif \fi\fi\fi} + +\def\fontstringB + {\ifx\fontstyle\c!rm \s!Regular \else + \ifx\fontstyle\c!ss \s!Support \else + \ifx\fontstyle\c!tt \s!Type \else + \s!Serif \fi\fi\fi} + +\def\fontstringC + {\ifx\fontalternative\c!bf \s!Bold \else + \ifx\fontalternative\c!sl \s!Slanted \else + \ifx\fontalternative\c!it \s!Italic \else + \ifx\fontalternative\c!bs \s!BoldSlanted \else + \ifx\fontalternative\c!bi \s!BoldItalic \fi\fi\fi\fi\fi} + +\def\fontstringD % default fontstyle + {\expandafter\ifx\csname\??tf\fontclass\s!default\endcsname\c!rm \s!Serif \else + \expandafter\ifx\csname\??tf\fontclass\s!default\endcsname\c!ss \s!Sans \else + \expandafter\ifx\csname\??tf\fontclass\s!default\endcsname\c!tt \s!Mono \else + \s!Serif \fi\fi\fi} + +% potential generalization: + +% \letvalue{\??ff:t:\c!rm}\s!Serif +% \letvalue{\??ff:t:\c!ss}\s!Sans +% \letvalue{\??ff:t:\c!tt}\s!Mono +% +% \letvalue{\??ff:s:\c!bf}\s!Bold +% \letvalue{\??ff:s:\c!sl}\s!Slanted +% \letvalue{\??ff:s:\c!it}\s!Italic +% \letvalue{\??ff:s:\c!bs}\s!BoldSlanted +% \letvalue{\??ff:s:\c!bi}\s!BoldItalic +% +% \letvalue{\??ff:a:\c!rm}\s!Regular +% \letvalue{\??ff:a:\c!ss}\s!Support +% \letvalue{\??ff:a:\c!tt}\s!Type +% +% \def\fontstringA{\executeifdefined{\??ff:t:\fontstyle}\s!Serif} +% \def\fontstringB{\executeifdefined{\??ff:a:\fontstyle}\s!Serif} +% \def\fontstringC{\executeifdefined{\??ff:s:\fontstyle}\empty} +% \def\fontstringD{\executeifdefined{\??ff:t:\csname\??tf\fontclass\s!default\endcsname}\s!Serif} + +\def\glyphfontfile#1% + {#1% + \ifcsname\??ff#1\fontstringA\fontstringC\endcsname + \fontstringA\fontstringC + \else\ifcsname\??ff#1\fontstringB\fontstringC\endcsname + \fontstringB\fontstringC + \else\ifcsname\??ff#1\fontstringA\endcsname + \fontstringA + \else\ifcsname\??ff#1\fontstringB\endcsname + \fontstringB + \else\ifcsname\??ff#1\fontstringC\endcsname + \fontstringC + \fi\fi\fi\fi\fi} + +%D The next macro can be used to make decisions based on the shape: + +\def\doifitalicelse#1#2% + {\ifx\fontalternative\c!sl#1\else + \ifx\fontalternative\c!it#1\else + \ifx\fontalternative\c!bs#1\else + \ifx\fontalternative\c!bi#1\else#2\fi\fi\fi\fi} + +%D For an example of usage of the following command, +%D see \type {cont-log.tex}. +%D +%D \starttyping +%D \def\symbolicfont#1{\definedfont[\glyphfontfile{#1} sa *]} +%D \stoptyping +%D +%D Since we know what scaling it to be applied, we can +%D implement a much faster alternative: + +\let\thedefinedfont\relax + +\def\symbolicsizedfont#1#2#3% + {\scaledfontsize#1% + \scaledfontsize#2\scaledfontsize + \font\thedefinedfont=\truefontname{\glyphfontfile{#3}} at \currentfontbodyscale\scaledfontsize\relax + \thedefinedfont} + +\def\symbolicscaledfont + {\symbolicsizedfont\fontbody} + +\unexpanded\def\symbolicfont + {\symbolicsizedfont\fontbody\plusone} + +\unexpanded\def\getglyph#1#2% slow, faster, much faster + %{{\definefont[\s!dummy][\glyphfontfile{#1} sa \currentfontscale]\dummy#2}} + %{{\definefont[\s!dummy][\glyphfontfile{#1} sa *]\dummy#2}} + %{{\symbolicfont{#1}#2}} + {{\symbolicfont{#1}\doifnumberelse{#2}\char\donothing#2}} + +\unexpanded\def\getscaledglyph#1#2#3% + {{\symbolicscaledfont{#1}{#2}\doifnumberelse{#3}\char\donothing#3}} + +\unexpanded\def\getrawglyph#1#2% for simple symbols + {{\scaledfontsize\fontbody + \font\thedefinedfont=#1 at \currentfontbodyscale\scaledfontsize\relax + \thedefinedfont\doifnumberelse{#2}\char\donothing#2}} + +%D The last implementation of \type {\getglyph} permits +%D definitions like: +%D +%D \starttyping +%D \definefontsynonym [EuroSans] [eurose] +%D \definefontsynonym [EuroSansBold] [euroseb] +%D \definefontsynonym [EuroSansItalic] [eurosei] +%D \definefontsynonym [EuroSansSlanted] [eurosei] +%D \definefontsynonym [EuroSansBoldItalic] [eurosebi] +%D \definefontsynonym [EuroSansBoldSlanted] [eurosebi] +%D +%D \definesymbol [euro] [\getglyph{Euro}{\char160}] +%D +%D \def\euro{\symbol[euro]} +%D \stoptyping +%D +%D These definitions guarantee that the next calls work okay: +%D +%D \starttyping +%D \ss \tf\euro \bf\euro \sla\euro \itd\euro \bs\euro \bic\euro +%D \stoptyping +%D +%D The shape as well as the size is adapted to the current +%D environment. + +%D \macros +%D {ss, SS, sz} +%D +%D We are going to redefine \type{\ss} but for those wo still +%D want to have access to the german \SS, we save it's value in +%D \type{\SS}. Ok, I should have used \type{\sf} instead of +%D \type{\ss} in the first place. + +\ifx\undefined\SS \let\SS=\ss \fi +\ifx\undefined\sz \let\sz=\ss \fi + +%D Personally I think that using \TEX\ macro packages is +%D complicated by the way fonts are handled. Apart from the +%D many encodings, we also deal with different naming schemes. +%D Confronted with this problem, I decided to change the +%D definitions into: +%D +%D \starttyping +%D \definebodyfont [12pt] [rm] [tf=Times-Roman at 12pt] +%D \stoptyping +%D +%D combined with for instance: +%D +%D \starttyping +%D \definefontsynonym [Times-Roman] [tir] +%D \stoptyping + +%D Now we're up to some definitions. + +\definebodyfontenvironment + [\s!default] + [ \s!text=1.0, + \s!script=0.7, + \s!scriptscript=0.5, + \c!a=1.200, + \c!b=1.440, + \c!c=1.728, + \c!d=2.074, + *=\currentfontscale, % wildcard + \c!x=0.8, + \c!xx=0.6, + \c!big=1.2, + \c!small=0.8, + \c!interlinespace=, + \c!em=\v!slanted] + +\definebodyfontenvironment + [20.7pt] + [ \s!text=20.7pt, + \s!script=\!!fourteenpointfour, + \s!scriptscript=\!!twelvepoint, + \c!x=17.3pt, + \c!xx=\!!fourteenpointfour, + \c!big=20.7pt, % !!!! + \c!small=17.3pt] + +\definebodyfontenvironment + [17.3pt] + [ \s!text=17.3pt, + \s!script=\!!twelvepoint, + \s!scriptscript=\!!tenpoint, + \c!x=\!!fourteenpointfour, + \c!xx=\!!twelvepoint, + \c!big=20.7pt, + \c!small=\!!fourteenpointfour] + +\definebodyfontenvironment + [\!!fourteenpointfour] + [ \s!text=\!!fourteenpointfour, + \s!script=\!!elevenpoint, + \s!scriptscript=\!!ninepoint, + \c!x=\!!twelvepoint, + \c!xx=\!!tenpoint, + \c!big=17.3pt, + \c!small=\!!twelvepoint] + +\definebodyfontenvironment + [\!!twelvepoint] + [ \s!text=\!!twelvepoint, + \s!script=\!!ninepoint, + \s!scriptscript=\!!sevenpoint, + \c!x=\!!tenpoint, + \c!xx=\!!eightpoint, + \c!big=\!!fourteenpointfour, + \c!small=\!!tenpoint] + +\definebodyfontenvironment + [\!!elevenpoint] + [ \s!text=\!!elevenpoint, + \s!script=\!!eightpoint, + \s!scriptscript=\!!sixpoint, + \c!x=\!!ninepoint, + \c!xx=\!!sevenpoint, + \c!big=\!!twelvepoint, + \c!small=\!!ninepoint] + +\definebodyfontenvironment + [\!!tenpoint] + [ \s!text=\!!tenpoint, + \s!script=\!!sevenpoint, + \s!scriptscript=\!!fivepoint, + \c!x=\!!eightpoint, + \c!xx=\!!sixpoint, + \c!big=\!!twelvepoint, + \c!small=\!!eightpoint] + +\definebodyfontenvironment + [\!!ninepoint] + [ \s!text=\!!ninepoint, + \s!script=\!!sevenpoint, + \s!scriptscript=\!!fivepoint, + \c!x=\!!sevenpoint, + \c!xx=\!!fivepoint, + \c!big=\!!elevenpoint, + \c!small=\!!sevenpoint] + +\definebodyfontenvironment + [\!!eightpoint] + [ \s!text=\!!eightpoint, + \s!script=\!!sixpoint, + \s!scriptscript=\!!fivepoint, + \c!x=\!!sixpoint, + \c!xx=\!!fivepoint, + \c!big=\!!tenpoint, + \c!small=\!!sixpoint] + +\definebodyfontenvironment + [\!!sevenpoint] + [ \s!text=\!!sevenpoint, + \s!script=\!!sixpoint, + \s!scriptscript=\!!fivepoint, + \c!x=\!!sixpoint, + \c!xx=\!!fivepoint, + \c!big=\!!ninepoint, + \c!small=\!!fivepoint] + +\definebodyfontenvironment + [\!!sixpoint] + [ \s!text=\!!sixpoint, + \s!script=\!!fivepoint, + \s!scriptscript=\!!fivepoint, + \c!x=\!!fivepoint, + \c!xx=\!!fivepoint, + \c!big=\!!eightpoint, + \c!small=\!!fivepoint] + +\definebodyfontenvironment + [\!!fivepoint] + [ \s!text=\!!fivepoint, + \s!script=\!!fivepoint, + \s!scriptscript=\!!fivepoint, + \c!x=\!!fivepoint, + \c!xx=\!!fivepoint, + \c!big=\!!sevenpoint, + \c!small=\!!fivepoint] + +\definebodyfontenvironment + [\!!fourpoint] + [ \s!text=\!!fourpoint, + \s!script=\!!fourpoint, + \s!scriptscript=\!!fourpoint, + \c!x=\!!fourpoint, + \c!xx=\!!fourpoint, + \c!big=\!!sixpoint, + \c!small=\!!fourpoint] + +\definebodyfontswitch [fourteenpointfour] [\!!fourteenpointfour] +\definebodyfontswitch [twelvepoint] [\!!twelvepoint] +\definebodyfontswitch [elevenpoint] [\!!elevenpoint] +\definebodyfontswitch [tenpoint] [\!!tenpoint] +\definebodyfontswitch [ninepoint] [\!!ninepoint] +\definebodyfontswitch [eightpoint] [\!!eightpoint] +\definebodyfontswitch [sevenpoint] [\!!sevenpoint] +\definebodyfontswitch [sixpoint] [\!!sixpoint] +\definebodyfontswitch [fivepoint] [\!!fivepoint] +\definebodyfontswitch [fourpoint] [\!!fourpoint] + +% \definebodyfontswitch [xii] [\!!twelvepoint] +% \definebodyfontswitch [xi] [\!!elevenpoint] +% \definebodyfontswitch [x] [\!!tenpoint] +% \definebodyfontswitch [ix] [\!!ninepoint] +% \definebodyfontswitch [viii] [\!!eightpoint] +% \definebodyfontswitch [vii] [\!!sevenpoint] +% \definebodyfontswitch [vi] [\!!sixpoint] + +%D So far. + +\definefontstyle [\c!mm] [\c!mm] +\definefontstyle [\c!rm,\v!roman,\v!serif,\v!regular] [\c!rm] +\definefontstyle [\c!ss,\v!sansserif,\v!sans,\v!support] [\c!ss] +\definefontstyle [\c!tt,\v!teletype,\v!type,\v!mono] [\c!tt] +\definefontstyle [\c!hw,\v!handwritten] [\c!hw] +\definefontstyle [\c!cg,\v!calligraphic] [\c!cg] + +\definefontalternative[\c!tf] +\definefontalternative[\c!bf] +\definefontalternative[\c!it] +\definefontalternative[\c!sl] +\definefontalternative[\c!bs] +\definefontalternative[\c!bi] +\definefontalternative[\c!sc] + +\definefontsize[\c!a] \definefontsize[\c!b] +\definefontsize[\c!c] \definefontsize[\c!d] + +\definealternativestyle [\v!mediaeval] [\os] [] +\definealternativestyle [\v!normal] [\tf] [] +\definealternativestyle [\v!bold] [\bf] [] +\definealternativestyle [\v!type] [\tt] [] +\definealternativestyle [\v!mono] [\tt] [] +\definealternativestyle [\v!slanted] [\sl] [] +\definealternativestyle [\v!italic] [\it] [] +\definealternativestyle [\v!boldslanted,\v!slantedbold] [\bs] [] +\definealternativestyle [\v!bolditalic,\v!italicbold] [\bi] [] +\definealternativestyle [\v!small,\v!smallnormal] [\tfx] [] +\definealternativestyle [\v!smallbold] [\bfx] [] +\definealternativestyle [\v!smalltype] [\ttx] [] +\definealternativestyle [\v!smallslanted] [\slx] [] +\definealternativestyle [\v!smallboldslanted,\v!smallslantedbold] [\bsx] [] +\definealternativestyle [\v!smallbolditalic,\v!smallitalicbold] [\bix] [] + +\definealternativestyle [\v!sans,\v!sansserif] [\ss] [] +\definealternativestyle [\v!sansbold] [\ss\bf] [] + +%D Slow but handy: + +\definealternativestyle [\v!smallbodyfont] [\setsmallbodyfont] [] +\definealternativestyle [\v!bigbodyfont] [\setbigbodyfont] [] + +%D We treat {\sc Small Caps} and \cap {Pseudo Caps} a bit +%D different. We also provide an \WORD {uppercase} style. + +\definealternativestyle [\v!cap,\v!capital] [\smallcapped] [\smallcapped] +\definealternativestyle [\v!smallcaps] [\sc] [\sc] +\definealternativestyle [\v!WORD] [\WORD] [\WORD] + +%D \macros +%D {fontstylesuffix} +%D +%D The next macro is used to map non latin fontnames on +%D fonts. See \type {font-uni} for an example of its use. + +\def\fontstylesuffix% why the \s!Regular ? see \getglyph + {\ifx\fontalternative\c!tf \s!Regular \else + \ifx\fontalternative\c!bf \s!Bold \else + \ifx\fontalternative\c!sl \s!Slanted \else + \ifx\fontalternative\c!it \s!Italic \else + \ifx\fontalternative\c!bs \s!BoldSlanted \else + \ifx\fontalternative\c!bi \s!BoldItalic \else + \ifx\fontalternative\c!sc \s!Caps \else + \s!Regular \fi\fi\fi\fi\fi\fi\fi}% + +%D \macros +%D {definefontvariant,fontvariant,variant} +%D +%D This command is obsolete in \MKIV\ as we have features. It might +%D come back using the local features handlers. + +\unexpanded\def\definefontvariant{\dotripleargument\dodefinefontvariant} + +\def\dodefinefontvariant[#1][#2][#3]{} +\def\variant [#1]{} + +\ifdefined\Var\else \let\Var\variant \fi + +%D By default we load the Computer Modern Roman fonts (but +%D not yet at this moment) and activate the 12pt roman +%D bodyfont. Sans serif and teletype are also available and +%D can be called for by \type{\ss} and \type{\tt}. Loading +%D takes place elsewhere. +%D +%D For tracing purposes we define: + +\definefont[tinyfont][Mono at 1ex] + +% \tracinglostchars=1 + +% this needs some interfacing +% +% \setupfonts[check=...] + +\def\checkcharactersinfont {\ctxlua{fonts.checkers.enable()}} +\def\removemissingcharacters{\ctxlua{fonts.checkers.enable(true)}} + +%D New commands (not yet interfaced): + +\def\style[#1]% for inline usage, like \color + {\groupedcommand{\ifcsname#1\endcsname\csname#1\endcsname\else\definedfont[#1]\fi}{}} + +\unexpanded\def\startstyle[#1]% + {\begingroup + \ifcsname#1\endcsname\csname#1\endcsname\else\definedfont[#1]\fi} + +\unexpanded\def\stopstyle + {\endgroup} + +%D Still experimental (might even go away). + +% \definestylecollection[mine] + +% \definestyleinstance[mine][default][sorry] +% \definestyleinstance[mine][tt][bs][ttbs:\rm\sl] +% \definestyleinstance[mine][tt][bf][ttbf:\rm\sl] +% \definestyleinstance[mine][bf][\sl] +% \definestyleinstance[mine][sl][\tt] + +% {\bf test \mine test \sl test \mine test \bs oeps \mine oeps {\tt test \mine \bf test}} + +\unexpanded\def\definestylecollection + {\dosingleargument\dodefinestylecollection} + +\def\dodefinestylecollection[#1]% + {\iffirstargument + \setuvalue{#1}{\styleinstance[#1]}% + \def\docommand##1% + {\def\dodocommand####1{\letbeundefined{\??sx##1:####1:\commalistelement}}% + \processcommacommand[\fontalternativelist,\s!default]\dodocommand}% + \processcommacommand[\fontstylelist,\s!default]\docommand + \fi} + +\unexpanded\def\definestyleinstance + {\doquadrupleargument\dodefinestyleinstance} + +\def\dodefinestyleinstance[#1][#2][#3][#4]% [name] [rm|ss|tt|..] [sl|bf|...] [whatever] + {\iffirstargument + \ifcsname#1\endcsname\else\definestylecollection[#1]\fi + \fi + \iffourthargument + \setvalue{\??sx#1:#2:#3}{#4}% + \else\ifthirdargument + \setvalue{\??sx#1::#2}{#3}% + \else\ifsecondargument + \letvalue{\??sx#1::#2}\empty + \fi\fi\fi} + +\unexpanded\def\styleinstance[#1]% will be made faster + {%\begingroup\normalexpanded{\noexpand\infofont[#1:\fontstyle:\fontalternative]}\endgroup + \executeifdefined{\??sx#1:\fontstyle:\fontalternative}% + {\executeifdefined{\??sx#1:\fontstyle:\s!default}% + {\executeifdefined{\??sx#1::\fontalternative} + {\getvalue {\??sx#1::\s!default}}}}} + +% \unexpanded\def\styleinstance[#1]% +% {\csname\??sx#1% +% \ifcsname:\fontstyle:\fontalternative\endcsname +% :\fontstyle:\fontalternative +% \else\ifcsname:\fontstyle:\s!default\endcsname +% :\fontstyle:\s!default +% \else\ifcsname::\fontalternative\endcsname +% ::\fontalternative +% \else\ifcsname::\s!default\endcsname +% ::\s!default +% \else +% % nothing, \relax +% \fi\fi\fi\fi +% \endcsname} + +%D goodies: + +\def\showchardata#1{\ctxlua{fonts.show_char_data("#1")}} +\def\showfontdata {\ctxlua{fonts.show_font_parameters()}} + +%D some low level helpers +%D +%D \starttyping +%D \def\TestLookup#1% +%D {\dolookupfontbyspec{#1} +%D pattern: #1, found: \dolookupnoffound +%D \blank +%D \dorecurse {\dolookupnoffound} {% +%D \recurselevel:~\dolookupgetkeyofindex{fontname}{\recurselevel}\quad +%D }% +%D \blank} +%D +%D \TestLookup{familyname=helveticaneue} +%D \TestLookup{familyname=helveticaneue,weight=bold} +%D \TestLookup{familyname=helveticaneue,weight=bold,style=italic} +%D \stoptyping + +% we can also move the lookups to the fonts.namespace (of commands) + +\def\dolookupfontbyspec #1{\ctxlua{fonts.names.lookup("#1")}} +\def\dolookupnoffound {\ctxlua{tex.write(fonts.names.noflookups())}} +\def\dolookupgetkeyofindex#1#2{\ctxlua{tex.write(fonts.names.getlookupkey("#1",#2))}} +\def\dolookupgetkey #1{\ctxlua{tex.write(fonts.names.getlookupkey("#1"))}} +\def\cleanfontname #1{\ctxlua{fonts.cleanname("#1")}} + +\protect \endinput + +% \startluacode +% function commands.doifelsecurrentfonthasfeature(name) +% local f = fonts.ids[font.current()] +% f = f and f.shared +% f = f and f.otfdata +% f = f and f.luatex +% f = f and f.features +% commands.doifelse(f and (f.gpos[name] or f.gsub[name])) +% end +% \stopluacode + +% \def\doifelsecurrentfonthasfeature#1% +% {\ctxlua{commands.doifelsecurrentfonthasfeature("#1")}} + +% \doifelsecurrentfonthasfeature{smcp}{YES}{NO} +% \doifelsecurrentfonthasfeature{crap}{YES}{NO} +% \doifelsecurrentfonthasfeature{kern}{YES}{NO} diff --git a/tex/context/base/font-jap.mkii b/tex/context/base/font-jap.mkii new file mode 100644 index 000000000..42480df43 --- /dev/null +++ b/tex/context/base/font-jap.mkii @@ -0,0 +1,83 @@ +%D \module +%D [ file=font-jap, +%D version=2006.01.13, +%D title=\CONTEXT\ Font Macros, +%D subtitle=Japanese, +%D author=Hans Hagen, +%D date=\currentdate, +%D suggestions=Wang Lei, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\ifx\handlejapaneseunicodeglyph\undefined \else \endinput \fi +\ifx\handlechineseunicodeglyph \undefined \input font-chi.tex \fi + +\writestatus{loading}{ConTeXt Font Macros / Japanese} + +\unprotect + +\newif\iftracejapanese + +\let\japaneseencoding\empty + +\def\setjapaneseencoding + {\getfontfileparameters\unicodestyle + \ifx\currentfontfileencoding\undefined \else + \let\japaneseencoding\currentfontfileencoding + \fi} + +\def\japaneseunicodescale {\chineseunicodescale } +\def\japaneseunicodeheight {\chineseunicodeheight } +\def\japaneseunicodedepth {\chineseunicodedepth } +\def\japaneseinterglyphskip{\chineseinterglyphskip} +\def\japanesesurroundskip {\chinesesurroundskip } + +\setupunicodefont + [japanese] + [ \c!scale=\japaneseunicodescale, + \c!height=\japaneseunicodeheight, + \c!depth=\japaneseunicodedepth, + \c!strut=\v!yes, + \c!interlinespace=\v!yes, + \c!conversion=\japanesenumber, + \c!commands=\setjapaneseencoding, % needed for digits + \c!command=\handlejapaneseunicodeglyph] + +\def\japanesenumber{\numbers} + +\def\handlejapaneseunicodeglyph + {\begingroup +% \let\chineseunicodescale \japaneseunicodescale +% \let\chineseunicodeheight \japaneseunicodeheight +% \let\chineseunicodedepth \japaneseunicodedepth +% \let\chineseinterglyphskip\japaneseinterglyphskip +% \let\chinesesurroundskip \japanesesurroundskip + \iftracejapanese\tracechinesetrue\else\tracechinesefalse\fi + \handlechineseunicodeglyph + \endgroup} + +\doifelse \currentregime {utf} { + + % todo: typescripts + + \definefontsynonym [JapaneseRegular] [uni-cybercjk-][encoding=cjk-uni] + \definefontsynonym [JapaneseSlanted] [uni-cybercjk-][encoding=cjk-uni] + \definefontsynonym [JapaneseItalic] [uni-cybercjk-][encoding=cjk-uni] + \definefontsynonym [JapaneseBold] [uni-cybercjk-][encoding=cjk-uni] + \definefontsynonym [JapaneseBoldSlanted][uni-cybercjk-][encoding=cjk-uni] + \definefontsynonym [JapaneseBoldItalic] [uni-cybercjk-][encoding=cjk-uni] + + \loadmapfile[uni-cybercjk.map] + + \defineunicodefont [Japanese] [Japanese] [japanese] + +} { + \writestatus{Japanese}{No fonts defined} +} + +\Japanese + +\protect \endinput diff --git a/tex/context/base/font-log.lua b/tex/context/base/font-log.lua new file mode 100644 index 000000000..97cb4ff7c --- /dev/null +++ b/tex/context/base/font-log.lua @@ -0,0 +1,58 @@ +if not modules then modules = { } end modules ['font-log'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local next, format, lower, concat = next, string.format, string.lower, table.concat + +local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) + +fonts.logger = fonts.logger or { } + +--[[ldx-- +The following functions are used for reporting about the fonts +used. The message itself is not that useful in regular runs but since +we now have several readers it may be handy to know what reader is +used for which font.
+--ldx]]-- + +function fonts.logger.save(tfmtable,source,specification) -- save file name in spec here ! ! ! ! ! ! + if tfmtable and specification and specification.specification then + local name = lower(specification.name) + if trace_defining and not fonts.used[name] then + logs.report("define font","registering %s as %s (used: %s)",file.basename(specification.name),source,file.basename(specification.filename)) + end + specification.source = source + fonts.loaded[lower(specification.specification)] = specification + -- fonts.used[name] = source + fonts.used[lower(specification.filename or specification.name)] = source + end +end + +function fonts.logger.report(complete) + local t = { } + for name, used in table.sortedhash(fonts.used) do + if complete then + t[#t+1] = used .. "->" .. file.basename(name) + else + t[#t+1] = file.basename(name) + end + end + return t +end + +function fonts.logger.format(name) + return fonts.used[name] or "unknown" +end + +statistics.register("loaded fonts", function() + if next(fonts.used) then + local t = fonts.logger.report() + return (#t > 0 and format("%s files: %s",#t,concat(t,separator or " "))) or "none" + else + return nil + end +end) diff --git a/tex/context/base/font-map.lua b/tex/context/base/font-map.lua new file mode 100644 index 000000000..299508764 --- /dev/null +++ b/tex/context/base/font-map.lua @@ -0,0 +1,370 @@ +if not modules then modules = { } end modules ['font-map'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local utf = unicode.utf8 +local match, format, find, concat, gsub, lower = string.match, string.format, string.find, table.concat, string.gsub, string.lower +local lpegmatch = lpeg.match +local utfbyte = utf.byte + +local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) +local trace_unimapping = false trackers.register("otf.unimapping", function(v) trace_unimapping = v end) + +local ctxcatcodes = tex and tex.ctxcatcodes + +--[[ldx-- +Eventually this code will disappear because map files are kind +of obsolete. Some code may move to runtime or auxiliary modules.
+The name to unciode related code will stay of course.
+--ldx]]-- + +fonts = fonts or { } +fonts.map = fonts.map or { } + +local function load_lum_table(filename) -- will move to font goodies + local lumname = file.replacesuffix(file.basename(filename),"lum") + local lumfile = resolvers.find_file(lumname,"map") or "" + if lumfile ~= "" and lfs.isfile(lumfile) then + if trace_loading or trace_unimapping then + logs.report("load otf","enhance: loading %s ",lumfile) + end + lumunic = dofile(lumfile) + return lumunic, lumfile + end +end + +local hex = lpeg.R("AF","09") +local hexfour = (hex*hex*hex*hex) / function(s) return tonumber(s,16) end +local hexsix = (hex^1) / function(s) return tonumber(s,16) end +local dec = (lpeg.R("09")^1) / tonumber +local period = lpeg.P(".") + +local unicode = lpeg.P("uni") * (hexfour * (period + lpeg.P(-1)) * lpeg.Cc(false) + lpeg.Ct(hexfour^1) * lpeg.Cc(true)) +local ucode = lpeg.P("u") * (hexsix * (period + lpeg.P(-1)) * lpeg.Cc(false) + lpeg.Ct(hexsix ^1) * lpeg.Cc(true)) +local index = lpeg.P("index") * dec * lpeg.Cc(false) + +local parser = unicode + ucode + index + +local parsers = { } + +local function make_name_parser(str) + if not str or str == "" then + return parser + else + local p = parsers[str] + if not p then + p = lpeg.P(str) * period * dec * lpeg.Cc(false) + parsers[str] = p + end + return p + end +end + +--~ local parser = fonts.map.make_name_parser("Japan1") +--~ local parser = fonts.map.make_name_parser() +--~ local function test(str) +--~ local b, a = lpegmatch(parser,str) +--~ print((a and table.serialize(b)) or b) +--~ end +--~ test("a.sc") +--~ test("a") +--~ test("uni1234") +--~ test("uni1234.xx") +--~ test("uni12349876") +--~ test("index1234") +--~ test("Japan1.123") + +local function tounicode16(unicode) + if unicode < 0x10000 then + return format("%04X",unicode) + else + return format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00) + end +end + +local function tounicode16sequence(unicodes) + local t = { } + for l=1,#unicodes do + local unicode = unicodes[l] + if unicode < 0x10000 then + t[l] = format("%04X",unicode) + else + t[l] = format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00) + end + end + return concat(t) +end + +--~ This is quite a bit faster but at the cost of some memory but if we +--~ do this we will also use it elsewhere so let's not follow this route +--~ now. I might use this method in the plain variant (no caching there) +--~ but then I need a flag that distinguishes between code branches. +--~ +--~ local cache = { } +--~ +--~ function fonts.map.tounicode16(unicode) +--~ local s = cache[unicode] +--~ if not s then +--~ if unicode < 0x10000 then +--~ s = format("%04X",unicode) +--~ else +--~ s = format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00) +--~ end +--~ cache[unicode] = s +--~ end +--~ return s +--~ end + +fonts.map.load_lum_table = load_lum_table +fonts.map.make_name_parser = make_name_parser +fonts.map.tounicode16 = tounicode16 +fonts.map.tounicode16sequence = tounicode16sequence + +local separator = lpeg.S("_.") +local other = lpeg.C((1 - separator)^1) +local ligsplitter = lpeg.Ct(other * (separator * other)^0) + +--~ print(table.serialize(lpegmatch(ligsplitter,"this"))) +--~ print(table.serialize(lpegmatch(ligsplitter,"this.that"))) +--~ print(table.serialize(lpegmatch(ligsplitter,"japan1.123"))) +--~ print(table.serialize(lpegmatch(ligsplitter,"such_so_more"))) +--~ print(table.serialize(lpegmatch(ligsplitter,"such_so_more.that"))) + +fonts.map.add_to_unicode = function(data,filename) + local unicodes = data.luatex and data.luatex.unicodes + if not unicodes then + return + end + -- we need to move this code + unicodes['space'] = unicodes['space'] or 32 + unicodes['hyphen'] = unicodes['hyphen'] or 45 + unicodes['zwj'] = unicodes['zwj'] or 0x200D + unicodes['zwnj'] = unicodes['zwnj'] or 0x200C + -- the tounicode mapping is sparse and only needed for alternatives + local tounicode, originals, ns, nl, private, unknown = { }, { }, 0, 0, fonts.private, format("%04X",utfbyte("?")) + data.luatex.tounicode, data.luatex.originals = tounicode, originals + local lumunic, uparser, oparser + if false then -- will become an option + lumunic = load_lum_table(filename) + lumunic = lumunic and lumunic.tounicode + end + local cidinfo, cidnames, cidcodes = data.cidinfo + local usedmap = cidinfo and cidinfo.usedname + usedmap = usedmap and lower(usedmap) + usedmap = usedmap and fonts.cid.map[usedmap] + if usedmap then + oparser = usedmap and make_name_parser(cidinfo.ordering) + cidnames = usedmap.names + cidcodes = usedmap.unicodes + end + uparser = make_name_parser() + local aglmap = fonts.map and fonts.map.agl_to_unicode + for index, glyph in next, data.glyphs do + local name, unic = glyph.name, glyph.unicode or -1 -- play safe + if unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then + local unicode = (lumunic and lumunic[name]) or (aglmap and aglmap[name]) + if unicode then + originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 + end + -- cidmap heuristics, beware, there is no guarantee for a match unless + -- the chain resolves + if (not unicode) and usedmap then + local foundindex = lpegmatch(oparser,name) + if foundindex then + unicode = cidcodes[foundindex] -- name to number + if unicode then + originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 + else + local reference = cidnames[foundindex] -- number to name + if reference then + local foundindex = lpegmatch(oparser,reference) + if foundindex then + unicode = cidcodes[foundindex] + if unicode then + originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 + end + end + if not unicode then + local foundcodes, multiple = lpegmatch(uparser,reference) + if foundcodes then + if multiple then + originals[index], tounicode[index], nl, unicode = foundcodes, tounicode16sequence(foundcodes), nl + 1, true + else + originals[index], tounicode[index], ns, unicode = foundcodes, tounicode16(foundcodes), ns + 1, foundcodes + end + end + end + end + end + end + end + -- a.whatever or a_b_c.whatever or a_b_c (no numbers) + if not unicode then + local split = lpegmatch(ligsplitter,name) + local nplit = (split and #split) or 0 + if nplit == 0 then + -- skip + elseif nplit == 1 then + local base = split[1] + unicode = unicodes[base] or (aglmap and aglmap[base]) + if unicode then + if type(unicode) == "table" then + unicode = unicode[1] + end + originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 + end + else + local t = { } + for l=1,nplit do + local base = split[l] + local u = unicodes[base] or (aglmap and aglmap[base]) + if not u then + break + elseif type(u) == "table" then + t[#t+1] = u[1] + else + t[#t+1] = u + end + end + if #t > 0 then -- done then + originals[index], tounicode[index], nl, unicode = t, tounicode16sequence(t), nl + 1, true + end + end + end + -- last resort + if not unicode then + local foundcodes, multiple = lpegmatch(uparser,name) + if foundcodes then + if multiple then + originals[index], tounicode[index], nl, unicode = foundcodes, tounicode16sequence(foundcodes), nl + 1, true + else + originals[index], tounicode[index], ns, unicode = foundcodes, tounicode16(foundcodes), ns + 1, foundcodes + end + end + end + if not unicode then + originals[index], tounicode[index] = 0xFFFD, "FFFD" + end + end + end + if trace_unimapping then + for index, glyph in table.sortedhash(data.glyphs) do + local toun, name, unic = tounicode[index], glyph.name, glyph.unicode or -1 -- play safe + if toun then + logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X, tounicode: %s",index,name,unic,toun) + else + logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X",index,name,unic) + end + end + end + if trace_loading and (ns > 0 or nl > 0) then + logs.report("load otf","enhance: %s tounicode entries added (%s ligatures)",nl+ns, ns) + end +end + +-- the following is sort of obsolete +-- +-- fonts.map.data = fonts.map.data or { } +-- fonts.map.encodings = fonts.map.encodings or { } +-- fonts.map.loaded = fonts.map.loaded or { } +-- fonts.map.line = fonts.map.line or { } +-- +-- function fonts.map.line.pdftex(e) +-- if e.name and e.fontfile then +-- local fullname = e.fullname or "" +-- if e.slant and e.slant ~= 0 then +-- if e.encoding then +-- pdf.mapline(format('= %s %s "%g SlantFont" <%s <%s',e.name,fullname,e.slant,e.encoding,e.fontfile))) +-- else +-- pdf.mapline(format('= %s %s "%g SlantFont" <%s',e.name,fullname,e.slant,e.fontfile))) +-- end +-- elseif e.extend and e.extend ~= 1 and e.extend ~= 0 then +-- if e.encoding then +-- pdf.mapline(format('= %s %s "%g ExtendFont" <%s <%s',e.name,fullname,e.extend,e.encoding,e.fontfile))) +-- else +-- pdf.mapline(format('= %s %s "%g ExtendFont" <%s',e.name,fullname,e.extend,e.fontfile))) +-- end +-- else +-- if e.encoding then +-- pdf.mapline(format('= %s %s <%s <%s',e.name,fullname,e.encoding,e.fontfile))) +-- else +-- pdf.mapline(format('= %s %s <%s',e.name,fullname,e.fontfile))) +-- end +-- end +-- else +-- return nil +-- end +-- end +-- +-- function fonts.map.flush(backend) -- will also erase the accumulated data +-- local flushline = fonts.map.line[backend or "pdftex"] or fonts.map.line.pdftex +-- for _, e in next, fonts.map.data do +-- flushline(e) +-- end +-- fonts.map.data = { } +-- end +-- +-- fonts.map.line.dvips = fonts.map.line.pdftex +-- fonts.map.line.dvipdfmx = function() end +-- +-- function fonts.map.convert_entries(filename) +-- if not fonts.map.loaded[filename] then +-- fonts.map.data, fonts.map.encodings = fonts.map.load_file(filename,fonts.map.data, fonts.map.encodings) +-- fonts.map.loaded[filename] = true +-- end +-- end +-- +-- function fonts.map.load_file(filename, entries, encodings) +-- entries = entries or { } +-- encodings = encodings or { } +-- local f = io.open(filename) +-- if f then +-- local data = f:read("*a") +-- if data then +-- for line in gmatch(data,"(.-)[\n\t]") do +-- if find(line,"^[%#%%%s]") then +-- -- print(line) +-- else +-- local extend, slant, name, fullname, fontfile, encoding +-- line = gsub(line,'"(.+)"', function(s) +-- extend = find(s,'"([^"]+) ExtendFont"') +-- slant = find(s,'"([^"]+) SlantFont"') +-- return "" +-- end) +-- if not name then +-- -- name fullname encoding fontfile +-- name, fullname, encoding, fontfile = match(line,"^(%S+)%s+(%S*)[%s<]+(%S*)[%s<]+(%S*)%s*$") +-- end +-- if not name then +-- -- name fullname (flag) fontfile encoding +-- name, fullname, fontfile, encoding = match(line,"^(%S+)%s+(%S*)[%d%s<]+(%S*)[%s<]+(%S*)%s*$") +-- end +-- if not name then +-- -- name fontfile +-- name, fontfile = match(line,"^(%S+)%s+[%d%s<]+(%S*)%s*$") +-- end +-- if name then +-- if encoding == "" then encoding = nil end +-- entries[name] = { +-- name = name, -- handy +-- fullname = fullname, +-- encoding = encoding, +-- fontfile = fontfile, +-- slant = tonumber(slant), +-- extend = tonumber(extend) +-- } +-- encodings[name] = encoding +-- elseif line ~= "" then +-- -- print(line) +-- end +-- end +-- end +-- end +-- f:close() +-- end +-- return entries, encodings +-- end diff --git a/tex/context/base/font-mis.lua b/tex/context/base/font-mis.lua new file mode 100644 index 000000000..80a56332a --- /dev/null +++ b/tex/context/base/font-mis.lua @@ -0,0 +1,101 @@ +if not modules then modules = { } end modules ['font-mis'] = { + version = 1.001, + comment = "companion to luatex-fonts.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local next = next +local lower, strip = string.lower, string.strip + +fonts.otf = fonts.otf or { } + +fonts.otf.version = fonts.otf.version or 2.650 +fonts.otf.pack = true +fonts.otf.cache = containers.define("fonts", "otf", fonts.otf.version, true) + +function fonts.otf.loadcached(filename,format,sub) + -- no recache when version mismatch + local name = file.basename(file.removesuffix(filename)) + if sub == "" then sub = false end + local hash = name + if sub then + hash = hash .. "-" .. sub + end + hash = containers.cleanname(hash) + local data = containers.read(fonts.otf.cache(), hash) + if data and not data.verbose then + fonts.otf.enhancers.unpack(data) + return data + else + return nil + end +end + +local featuregroups = { "gsub", "gpos" } + +function fonts.get_features(name,t,script,language) + local t = lower(t or (name and file.extname(name)) or "") + if t == "otf" or t == "ttf" or t == "ttc" or t == "dfont" then + local filename = resolvers.find_file(name,t) or "" + if filename ~= "" then + local data = fonts.otf.loadcached(filename) + if data and data.luatex and data.luatex.features then + return data.luatex.features + else + local ff = fontloader.open(filename) + if ff then + local data = fontloader.to_table(ff) + fontloader.close(ff) + local features = { } + for k=1,#featuregroups do + local what = featuregroups[k] + local dw = data[what] + if dw then + local f = { } + features[what] = f + for i=1,#dw do + local d = dw[i] + local dfeatures = d.features + if dfeatures then + for i=1,#dfeatures do + local df = dfeatures[i] + local tag = strip(lower(df.tag)) + local ft = f[tag] if not ft then ft = {} f[tag] = ft end + local dfscripts = df.scripts + for i=1,#dfscripts do + local ds = dfscripts[i] + local scri = strip(lower(ds.script)) + local fts = ft[scri] if not fts then fts = {} ft[scri] = fts end + local dslangs = ds.langs + for i=1,#dslangs do + local lang = dslangs[i] + lang = strip(lower(lang)) + if scri == script then + if lang == language then + fts[lang] = 'sl' + else + fts[lang] = 's' + end + else + if lang == language then + fts[lang] = 'l' + else + fts[lang] = true + end + end + end + end + end + end + end + end + end + return features + end + end + end + end + return nil, nil +end diff --git a/tex/context/base/font-ota.lua b/tex/context/base/font-ota.lua new file mode 100644 index 000000000..0b61e17d1 --- /dev/null +++ b/tex/context/base/font-ota.lua @@ -0,0 +1,286 @@ +if not modules then modules = { } end modules ['font-ota'] = { + version = 1.001, + comment = "companion to font-otf.lua (analysing)", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- this might become scrp-*.lua + +local type, tostring, match, format, concat = type, tostring, string.match, string.format, table.concat + +if not trackers then trackers = { register = function() end } end + +local trace_analyzing = false trackers.register("otf.analyzing", function(v) trace_analyzing = v end) +local trace_cjk = false trackers.register("cjk.injections", function(v) trace_cjk = v end) + +trackers.register("cjk.analyzing","otf.analyzing") + +fonts = fonts or { } +fonts.analyzers = fonts.analyzers or { } +fonts.analyzers.initializers = fonts.analyzers.initializers or { node = { otf = { } } } +fonts.analyzers.methods = fonts.analyzers.methods or { node = { otf = { } } } + +local otf = fonts.otf +local tfm = fonts.tfm + +local initializers = fonts.analyzers.initializers +local methods = fonts.analyzers.methods + +local glyph = node.id('glyph') +local glue = node.id('glue') +local penalty = node.id('penalty') + +local set_attribute = node.set_attribute +local has_attribute = node.has_attribute +local traverse_id = node.traverse_id +local traverse_node_list = node.traverse + +local fontdata = fonts.ids +local state = attributes.private('state') + +local fcs = (fonts.color and fonts.color.set) or function() end +local fcr = (fonts.color and fonts.color.reset) or function() end + +local a_to_script = otf.a_to_script +local a_to_language = otf.a_to_language + +-- in the future we will use language/script attributes instead of the +-- font related value, but then we also need dynamic features which is +-- somewhat slower; and .. we need a chain of them + +function fonts.initializers.node.otf.analyze(tfmdata,value,attr) + if attr and attr > 0 then + script, language = a_to_script[attr], a_to_language[attr] + else + script, language = tfmdata.script, tfmdata.language + end + local action = initializers[script] + if action then + if type(action) == "function" then + return action(tfmdata,value) + else + local action = action[language] + if action then + return action(tfmdata,value) + end + end + end + return nil +end + +function fonts.methods.node.otf.analyze(head,font,attr) + local tfmdata = fontdata[font] + local script, language + if attr and attr > 0 then + script, language = a_to_script[attr], a_to_language[attr] + else + script, language = tfmdata.script, tfmdata.language + end + local action = methods[script] + if action then + if type(action) == "function" then + return action(head,font,attr) + else + action = action[language] + if action then + return action(head,font,attr) + end + end + end + return head, false +end + +otf.features.register("analyze",true) -- we always analyze +table.insert(fonts.triggers,"analyze") -- we need a proper function for doing this + +-- latin + +fonts.analyzers.methods.latn = fonts.analyzers.aux.setstate + +-- this info eventually will go into char-def + +local zwnj = 0x200C +local zwj = 0x200D + +local isol = { + [0x0600] = true, [0x0601] = true, [0x0602] = true, [0x0603] = true, + [0x0608] = true, [0x060B] = true, [0x0621] = true, [0x0674] = true, + [0x06DD] = true, [zwnj] = true, +} + +local isol_fina = { + [0x0622] = true, [0x0623] = true, [0x0624] = true, [0x0625] = true, + [0x0627] = true, [0x0629] = true, [0x062F] = true, [0x0630] = true, + [0x0631] = true, [0x0632] = true, [0x0648] = true, [0x0671] = true, + [0x0672] = true, [0x0673] = true, [0x0675] = true, [0x0676] = true, + [0x0677] = true, [0x0688] = true, [0x0689] = true, [0x068A] = true, + [0x068B] = true, [0x068C] = true, [0x068D] = true, [0x068E] = true, + [0x068F] = true, [0x0690] = true, [0x0691] = true, [0x0692] = true, + [0x0693] = true, [0x0694] = true, [0x0695] = true, [0x0696] = true, + [0x0697] = true, [0x0698] = true, [0x0699] = true, [0x06C0] = true, + [0x06C3] = true, [0x06C4] = true, [0x06C5] = true, [0x06C6] = true, + [0x06C7] = true, [0x06C8] = true, [0x06C9] = true, [0x06CA] = true, + [0x06CB] = true, [0x06CD] = true, [0x06CF] = true, [0x06D2] = true, + [0x06D3] = true, [0x06D5] = true, [0x06EE] = true, [0x06EF] = true, + [0x0759] = true, [0x075A] = true, [0x075B] = true, [0x076B] = true, + [0x076C] = true, [0x0771] = true, [0x0773] = true, [0x0774] = true, + [0x0778] = true, [0x0779] = true, [0xFEF5] = true, [0xFEF7] = true, + [0xFEF9] = true, [0xFEFB] = true, +} + +local isol_fina_medi_init = { + [0x0626] = true, [0x0628] = true, [0x062A] = true, [0x062B] = true, + [0x062C] = true, [0x062D] = true, [0x062E] = true, [0x0633] = true, + [0x0634] = true, [0x0635] = true, [0x0636] = true, [0x0637] = true, + [0x0638] = true, [0x0639] = true, [0x063A] = true, [0x063B] = true, + [0x063C] = true, [0x063D] = true, [0x063E] = true, [0x063F] = true, + [0x0640] = true, [0x0641] = true, [0x0642] = true, [0x0643] = true, + [0x0644] = true, [0x0645] = true, [0x0646] = true, [0x0647] = true, + [0x0649] = true, [0x064A] = true, [0x066E] = true, [0x066F] = true, + [0x0678] = true, [0x0679] = true, [0x067A] = true, [0x067B] = true, + [0x067C] = true, [0x067D] = true, [0x067E] = true, [0x067F] = true, + [0x0680] = true, [0x0681] = true, [0x0682] = true, [0x0683] = true, + [0x0684] = true, [0x0685] = true, [0x0686] = true, [0x0687] = true, + [0x069A] = true, [0x069B] = true, [0x069C] = true, [0x069D] = true, + [0x069E] = true, [0x069F] = true, [0x06A0] = true, [0x06A1] = true, + [0x06A2] = true, [0x06A3] = true, [0x06A4] = true, [0x06A5] = true, + [0x06A6] = true, [0x06A7] = true, [0x06A8] = true, [0x06A9] = true, + [0x06AA] = true, [0x06AB] = true, [0x06AC] = true, [0x06AD] = true, + [0x06AE] = true, [0x06AF] = true, [0x06B0] = true, [0x06B1] = true, + [0x06B2] = true, [0x06B3] = true, [0x06B4] = true, [0x06B5] = true, + [0x06B6] = true, [0x06B7] = true, [0x06B8] = true, [0x06B9] = true, + [0x06BA] = true, [0x06BB] = true, [0x06BC] = true, [0x06BD] = true, + [0x06BE] = true, [0x06BF] = true, [0x06C1] = true, [0x06C2] = true, + [0x06CC] = true, [0x06CE] = true, [0x06D0] = true, [0x06D1] = true, + [0x06FA] = true, [0x06FB] = true, [0x06FC] = true, [0x06FF] = true, + [0x0750] = true, [0x0751] = true, [0x0752] = true, [0x0753] = true, + [0x0754] = true, [0x0755] = true, [0x0756] = true, [0x0757] = true, + [0x0758] = true, [0x075C] = true, [0x075D] = true, [0x075E] = true, + [0x075F] = true, [0x0760] = true, [0x0761] = true, [0x0762] = true, + [0x0763] = true, [0x0764] = true, [0x0765] = true, [0x0766] = true, + [0x0767] = true, [0x0768] = true, [0x0769] = true, [0x076A] = true, + [0x076D] = true, [0x076E] = true, [0x076F] = true, [0x0770] = true, + [0x0772] = true, [0x0775] = true, [0x0776] = true, [0x0777] = true, + [0x077A] = true, [0x077B] = true, [0x077C] = true, [0x077D] = true, + [0x077E] = true, [0x077F] = true, [zwj] = true, +} + +local arab_warned = { } + +-- todo: gref + +local function warning(current,what) + local char = current.char + if not arab_warned[char] then + log.report("analyze","arab: character %s (U+%04X) has no %s class", char, char, what) + arab_warned[char] = true + end +end + +function fonts.analyzers.methods.nocolor(head,font,attr) + for n in traverse_node_list(head,glyph) do + if not font or n.font == font then + fcr(n) + end + end + return head, true +end + +local function finish(first,last) + if last then + if first == last then + local fc = first.char + if isol_fina_medi_init[fc] or isol_fina[fc] then + set_attribute(first,state,4) -- isol + if trace_analyzing then fcs(first,"font:isol") end + else + warning(first,"isol") + set_attribute(first,state,0) -- error + if trace_analyzing then fcr(first) end + end + else + local lc = last.char + if isol_fina_medi_init[lc] or isol_fina[lc] then -- why isol here ? + -- if laststate == 1 or laststate == 2 or laststate == 4 then + set_attribute(last,state,3) -- fina + if trace_analyzing then fcs(last,"font:fina") end + else + warning(last,"fina") + set_attribute(last,state,0) -- error + if trace_analyzing then fcr(last) end + end + end + first, last = nil, nil + elseif first then + -- first and last are either both set so we never com here + local fc = first.char + if isol_fina_medi_init[fc] or isol_fina[fc] then + set_attribute(first,state,4) -- isol + if trace_analyzing then fcs(first,"font:isol") end + else + warning(first,"isol") + set_attribute(first,state,0) -- error + if trace_analyzing then fcr(first) end + end + first = nil + end + return first, last +end + +function fonts.analyzers.methods.arab(head,font,attr) -- maybe make a special version with no trace + local tfmdata = fontdata[font] + local marks = tfmdata.marks + local first, last, current, done = nil, nil, head, false + while current do + if current.id == glyph and current.subtype<256 and current.font == font and not has_attribute(current,state) then + done = true + local char = current.char + if marks[char] then + set_attribute(current,state,5) -- mark + if trace_analyzing then fcs(current,"font:mark") end + elseif isol[char] then -- can be zwj or zwnj too + first, last = finish(first,last) + set_attribute(current,state,4) -- isol + if trace_analyzing then fcs(current,"font:isol") end + first, last = nil, nil + elseif not first then + if isol_fina_medi_init[char] then + set_attribute(current,state,1) -- init + if trace_analyzing then fcs(current,"font:init") end + first, last = first or current, current + elseif isol_fina[char] then + set_attribute(current,state,4) -- isol + if trace_analyzing then fcs(current,"font:isol") end + first, last = nil, nil + else -- no arab + first, last = finish(first,last) + end + elseif isol_fina_medi_init[char] then + first, last = first or current, current + set_attribute(current,state,2) -- medi + if trace_analyzing then fcs(current,"font:medi") end + elseif isol_fina[char] then + if not has_attribute(last,state,1) then + -- tricky, we need to check what last may be ! + set_attribute(last,state,2) -- medi + if trace_analyzing then fcs(last,"font:medi") end + end + set_attribute(current,state,3) -- fina + if trace_analyzing then fcs(current,"font:fina") end + first, last = nil, nil + elseif char >= 0x0600 and char <= 0x06FF then + if trace_analyzing then fcs(current,"font:rest") end + first, last = finish(first,last) + else --no + first, last = finish(first,last) + end + else + first, last = finish(first,last) + end + current = current.next + end + first, last = finish(first,last) + return head, done +end diff --git a/tex/context/base/font-otb.lua b/tex/context/base/font-otb.lua new file mode 100644 index 000000000..a3d347737 --- /dev/null +++ b/tex/context/base/font-otb.lua @@ -0,0 +1,373 @@ +if not modules then modules = { } end modules ['font-otb'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local concat = table.concat +local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip +local type, next, tonumber, tostring = type, next, tonumber, tostring +local lpegmatch = lpeg.match + +local otf = fonts.otf +local tfm = fonts.tfm + +local trace_baseinit = false trackers.register("otf.baseinit", function(v) trace_baseinit = v end) +local trace_singles = false trackers.register("otf.singles", function(v) trace_singles = v end) +local trace_multiples = false trackers.register("otf.multiples", function(v) trace_multiples = v end) +local trace_alternatives = false trackers.register("otf.alternatives", function(v) trace_alternatives = v end) +local trace_ligatures = false trackers.register("otf.ligatures", function(v) trace_ligatures = v end) +local trace_kerns = false trackers.register("otf.kerns", function(v) trace_kerns = v end) +local trace_preparing = false trackers.register("otf.preparing", function(v) trace_preparing = v end) + +local wildcard = "*" +local default = "dflt" + +local split_at_space = lpeg.Ct(lpeg.splitat(" ")) -- no trailing or multiple spaces anyway + +local pcache, fcache = { }, { } -- could be weak + +local function gref(descriptions,n) + if type(n) == "number" then + local name = descriptions[n].name + if name then + return format("U+%04X (%s)",n,name) + else + return format("U+%04X") + end + elseif n then + local num, nam = { }, { } + for i=1,#n do + local ni = n[i] + num[i] = format("U+%04X",ni) + nam[i] = descriptions[ni].name or "?" + end + return format("%s (%s)",concat(num," "), concat(nam," ")) + else + return "?" + end +end + +local function cref(kind,lookupname) + if lookupname then + return format("feature %s, lookup %s",kind,lookupname) + else + return format("feature %s",kind) + end +end + +local function resolve_ligatures(tfmdata,ligatures,kind) + kind = kind or "unknown" + local unicodes = tfmdata.unicodes + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + local changed = tfmdata.changed + local done = { } + while true do + local ok = false + for k,v in next, ligatures do + local lig = v[1] + if not done[lig] then + local ligs = lpegmatch(split_at_space,lig) + if #ligs == 2 then + local uc = v[2] + local c, f, s = characters[uc], ligs[1], ligs[2] + local uft, ust = unicodes[f] or 0, unicodes[s] or 0 + if not uft or not ust then + logs.report("define otf","%s: unicode problem with base ligature %s = %s + %s",cref(kind),gref(descriptions,uc),gref(descriptions,uft),gref(descriptions,ust)) + -- some kind of error + else + if type(uft) == "number" then uft = { uft } end + if type(ust) == "number" then ust = { ust } end + for ufi=1,#uft do + local uf = uft[ufi] + for usi=1,#ust do + local us = ust[usi] + if changed[uf] or changed[us] then + if trace_baseinit and trace_ligatures then + logs.report("define otf","%s: base ligature %s + %s ignored",cref(kind),gref(descriptions,uf),gref(descriptions,us)) + end + else + local first, second = characters[uf], us + if first and second then + local t = first.ligatures + if not t then + t = { } + first.ligatures = t + end + if type(uc) == "number" then + t[second] = { type = 0, char = uc } + else + t[second] = { type = 0, char = uc[1] } -- can this still happen? + end + if trace_baseinit and trace_ligatures then + logs.report("define otf","%s: base ligature %s + %s => %s",cref(kind),gref(descriptions,uf),gref(descriptions,us),gref(descriptions,uc)) + end + end + end + end + end + end + ok, done[lig] = true, descriptions[uc].name + end + end + end + if ok then + -- done has "a b c" = "a_b_c" and ligatures the already set ligatures: "a b" = 123 + -- and here we add extras (f i i = fi + i and alike) + -- + -- we could use a hash for fnc and pattern + -- + -- this might be interfering ! + for d,n in next, done do + local pattern = pcache[d] if not pattern then pattern = "^(" .. d .. ") " pcache[d] = pattern end + local fnc = fcache[n] if not fnc then fnc = function() return n .. " " end fcache[n] = fnc end + for k,v in next, ligatures do + v[1] = gsub(v[1],pattern,fnc) + end + end + else + break + end + end +end + +local splitter = lpeg.splitat(" ") + +function prepare_base_substitutions(tfmdata,kind,value) -- we can share some code with the node features + if value then + local otfdata = tfmdata.shared.otfdata + local validlookups, lookuplist = otf.collect_lookups(otfdata,kind,tfmdata.script,tfmdata.language) + if validlookups then + local ligatures = { } + local unicodes = tfmdata.unicodes -- names to unicodes + local indices = tfmdata.indices + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + local changed = tfmdata.changed + -- + local actions = { + substitution = function(p,lookup,k,glyph,unicode) + local pv = p[2] -- p.variant + if pv then + local upv = unicodes[pv] + if upv then + if type(upv) == "table" then + upv = upv[1] + end + if characters[upv] then + if trace_baseinit and trace_singles then + logs.report("define otf","%s: base substitution %s => %s",cref(kind,lookup),gref(descriptions,k),gref(descriptions,upv)) + end + changed[k] = upv + end + end + end + end, + alternate = function(p,lookup,k,glyph,unicode) + local pc = p[2] -- p.components + if pc then + -- a bit optimized ugliness + if value == 1 then + pc = lpegmatch(splitter,pc) + elseif value == 2 then + local a, b = lpegmatch(splitter,pc) + pc = b or a + else + pc = { lpegmatch(splitter,pc) } + pc = pc[value] or pc[#pc] + end + if pc then + local upc = unicodes[pc] + if upc then + if type(upc) == "table" then + upc = upc[1] + end + if characters[upc] then + if trace_baseinit and trace_alternatives then + logs.report("define otf","%s: base alternate %s %s => %s",cref(kind,lookup),tostring(value),gref(descriptions,k),gref(descriptions,upc)) + end + changed[k] = upc + end + end + end + end + end, + ligature = function(p,lookup,k,glyph,unicode) + local pc = p[2] + if pc then + if trace_baseinit and trace_ligatures then + local upc = { lpegmatch(splitter,pc) } + for i=1,#upc do upc[i] = unicodes[upc[i]] end + -- we assume that it's no table + logs.report("define otf","%s: base ligature %s => %s",cref(kind,lookup),gref(descriptions,upc),gref(descriptions,k)) + end + ligatures[#ligatures+1] = { pc, k } + end + end, + } + -- + for k,c in next, characters do + local glyph = descriptions[k] + local lookups = glyph.slookups + if lookups then + for l=1,#lookuplist do + local lookup = lookuplist[l] + local p = lookups[lookup] + if p then + local a = actions[p[1]] + if a then + a(p,lookup,k,glyph,unicode) + end + end + end + end + local lookups = glyph.mlookups + if lookups then + for l=1,#lookuplist do + local lookup = lookuplist[l] + local ps = lookups[lookup] + if ps then + for i=1,#ps do + local p = ps[i] + local a = actions[p[1]] + if a then + a(p,lookup,k,glyph,unicode) + end + end + end + end + end + end + resolve_ligatures(tfmdata,ligatures,kind) + end + else + tfmdata.ligatures = tfmdata.ligatures or { } -- left over from what ? + end +end + +local function prepare_base_kerns(tfmdata,kind,value) -- todo what kind of kerns, currently all + if value then + local otfdata = tfmdata.shared.otfdata + local validlookups, lookuplist = otf.collect_lookups(otfdata,kind,tfmdata.script,tfmdata.language) + if validlookups then + local unicodes = tfmdata.unicodes -- names to unicodes + local indices = tfmdata.indices + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + local sharedkerns = { } + for u, chr in next, characters do + local d = descriptions[u] + if d then + local dk = d.mykerns -- shared + if dk then + local s = sharedkerns[dk] + if s == false then + -- skip + elseif s then + chr.kerns = s + else + local t, done = chr.kerns or { }, false + for l=1,#lookuplist do + local lookup = lookuplist[l] + local kerns = dk[lookup] + if kerns then + for k, v in next, kerns do + if v ~= 0 and not t[k] then -- maybe no 0 test here + t[k], done = v, true + if trace_baseinit and trace_kerns then + logs.report("define otf","%s: base kern %s + %s => %s",cref(kind,lookup),gref(descriptions,u),gref(descriptions,k),v) + end + end + end + end + end + if done then + sharedkerns[dk] = t + chr.kerns = t -- no empty assignments + else + sharedkerns[dk] = false + end + end + end + end + end + end + end +end + +-- In principle we could register each feature individually which was +-- what we did in earlier versions. However, after the rewrite it +-- made more sense to collect them in an overall features initializer +-- just as with the node variant. There it was needed because we need +-- to do complete mixed runs and not run featurewise (as we did before). + +local supported_gsub = { + 'liga', 'dlig', 'rlig', 'hlig', + 'pnum', 'onum', 'tnum', 'lnum', + 'zero', + 'smcp', 'cpsp', 'c2sc', 'ornm', 'aalt', + 'hwid', 'fwid', + 'ssty', 'rtlm', -- math +-- 'tlig', 'trep', +} + +local supported_gpos = { + 'kern' +} + +function otf.features.register_base_substitution(tag) + supported_gsub[#supported_gsub+1] = tag +end +function otf.features.register_base_kern(tag) + supported_gsub[#supported_gpos+1] = tag +end + +local basehash, basehashes = { }, 1 + +function fonts.initializers.base.otf.features(tfmdata,value) + if true then -- value then + -- not shared + local t = trace_preparing and os.clock() + local features = tfmdata.shared.features + if features then + local h = { } + for f=1,#supported_gsub do + local feature = supported_gsub[f] + local value = features[feature] + prepare_base_substitutions(tfmdata,feature,value) + if value then + h[#h+1] = feature .. "=" .. tostring(value) + end + end + for f=1,#supported_gpos do + local feature = supported_gpos[f] + local value = features[feature] + prepare_base_kerns(tfmdata,feature,features[feature]) + if value then + h[#h+1] = feature .. "=" .. tostring(value) + end + end + local hash = concat(h," ") + local base = basehash[hash] + if not base then + basehashes = basehashes + 1 + base = basehashes + basehash[hash] = base + end + -- We need to make sure that luatex sees the difference between + -- base fonts that have different glyphs in the same slots in fonts + -- that have the same fullname (or filename). LuaTeX will merge fonts + -- eventually (and subset later on). If needed we can use a more + -- verbose name as long as we don't use <()<>[]{}/%> and the length + -- is < 128. + tfmdata.fullname = tfmdata.fullname .. "-" .. base -- tfmdata.psname is the original + --~ logs.report("otf define","fullname base hash: '%s', featureset '%s'",tfmdata.fullname,hash) + end + if trace_preparing then + logs.report("otf define","preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?") + end + end +end diff --git a/tex/context/base/font-otc.lua b/tex/context/base/font-otc.lua new file mode 100644 index 000000000..357d347b1 --- /dev/null +++ b/tex/context/base/font-otc.lua @@ -0,0 +1,213 @@ +if not modules then modules = { } end modules ['font-otc'] = { + version = 1.001, + comment = "companion to font-otf.lua (context)", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local format, insert = string.format, table.insert +local type, next = type, next + +-- we assume that the other otf stuff is loaded already + +local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) + +local otf = fonts.otf +local tfm = fonts.tfm + +-- instead of "script = "DFLT", langs = { 'dflt' }" we now use wildcards (we used to +-- have always); some day we can write a "force always when true" trick for other +-- features as well +-- +-- we could have a tnum variant as well + +local extra_lists = { + tlig = { + { + endash = "hyphen hyphen", + emdash = "hyphen hyphen hyphen", + -- quotedblleft = "quoteleft quoteleft", + -- quotedblright = "quoteright quoteright", + -- quotedblleft = "grave grave", + -- quotedblright = "quotesingle quotesingle", + -- quotedblbase = "comma comma", + }, + }, + trep = { + { + -- [0x0022] = 0x201D, + [0x0027] = 0x2019, + -- [0x0060] = 0x2018, + }, + }, + anum = { + { -- arabic + [0x0030] = 0x0660, + [0x0031] = 0x0661, + [0x0032] = 0x0662, + [0x0033] = 0x0663, + [0x0034] = 0x0664, + [0x0035] = 0x0665, + [0x0036] = 0x0666, + [0x0037] = 0x0667, + [0x0038] = 0x0668, + [0x0039] = 0x0669, + }, + { -- persian + [0x0030] = 0x06F0, + [0x0031] = 0x06F1, + [0x0032] = 0x06F2, + [0x0033] = 0x06F3, + [0x0034] = 0x06F4, + [0x0035] = 0x06F5, + [0x0036] = 0x06F6, + [0x0037] = 0x06F7, + [0x0038] = 0x06F8, + [0x0039] = 0x06F9, + }, + }, +} + +local extra_features = { -- maybe just 1..n so that we prescribe order + tlig = { + { + features = { { scripts = { { script = "*", langs = { "*" }, } }, tag = "tlig", comment = "added bij mkiv" }, }, + name = "ctx_tlig_1", + subtables = { { name = "ctx_tlig_1_s" } }, + type = "gsub_ligature", + flags = { }, + }, + }, + trep = { + { + features = { { scripts = { { script = "*", langs = { "*" }, } }, tag = "trep", comment = "added bij mkiv" }, }, + name = "ctx_trep_1", + subtables = { { name = "ctx_trep_1_s" } }, + type = "gsub_single", + flags = { }, + }, + }, + anum = { + { + features = { { scripts = { { script = "arab", langs = { "dflt", "FAR" }, } }, tag = "anum", comment = "added bij mkiv" }, }, + name = "ctx_anum_1", + subtables = { { name = "ctx_anum_1_s" } }, + type = "gsub_single", + flags = { }, + }, + { + features = { { scripts = { { script = "arab", langs = { "URD" }, } }, tag = "anum", comment = "added bij mkiv" }, }, + name = "ctx_anum_2", + subtables = { { name = "ctx_anum_2_s" } }, + type = "gsub_single", + flags = { }, + }, + }, +} + +fonts.otf.enhancers["add some missing characters"] = function(data,filename) + -- todo +end + +fonts.otf.enhancers["enrich with features"] = function(data,filename) + -- could be done elsewhere (true can be #) + local used = { } + for i=1,#otf.glists do + local g = data[otf.glists[i]] + if g then + for i=1,#g do + local f = g[i].features + if f then + for i=1,#f do + local t = f[i].tag + if t then used[t] = true end + end + end + end + end + end + -- + local glyphs = data.glyphs + local indices = data.map.map + data.gsub = data.gsub or { } + for kind, specifications in next, extra_features do + if not used[kind] then + local done = 0 + for s=1,#specifications do + local added = false + local specification = specifications[s] + local list = extra_lists[kind][s] + local name = specification.name .. "_s" + if specification.type == "gsub_ligature" then + for unicode, index in next, indices do + local glyph = glyphs[index] + local ligature = list[glyph.name] + if ligature then + local o = glyph.lookups or { } + -- o[name] = { "ligature", ligature, glyph.name } + o[name] = { + { + ["type"] = "ligature", + ["specification"] = { + char = glyph.name, + components = ligature, + } + } + } + glyph.lookups, done, added = o, done+1, true + end + end + elseif specification.type == "gsub_single" then + for unicode, index in next, indices do + local glyph = glyphs[index] + local r = list[unicode] + if r then + local replacement = indices[r] + if replacement and glyphs[replacement] then + local o = glyph.lookups or { } + -- o[name] = { { "substitution", glyphs[replacement].name } } + o[name] = { + { + ["type"] = "substitution", + ["specification"] = { + variant = glyphs[replacement].name, + } + } + } + glyph.lookups, done, added = o, done+1, true + end + end + end + end + if added then + insert(data.gsub,s,table.fastcopy(specification)) -- right order + end + end + if done > 0 then + if trace_loading then + logs.report("load otf","enhance: registering %s feature (%s glyphs affected)",kind,done) + end + end + end + end +end + +otf.tables.features['tlig'] = 'TeX Ligatures' +otf.tables.features['trep'] = 'TeX Replacements' +otf.tables.features['anum'] = 'Arabic Digits' + +otf.features.register_base_substitution('tlig') +otf.features.register_base_substitution('trep') +otf.features.register_base_substitution('anum') + +-- the functionality is defined elsewhere + +fonts.initializers.base.otf.equaldigits = fonts.initializers.common.equaldigits +fonts.initializers.node.otf.equaldigits = fonts.initializers.common.equaldigits + +fonts.initializers.base.otf.lineheight = fonts.initializers.common.lineheight +fonts.initializers.node.otf.lineheight = fonts.initializers.common.lineheight + +fonts.initializers.base.otf.compose = fonts.initializers.common.compose +fonts.initializers.node.otf.compose = fonts.initializers.common.compose diff --git a/tex/context/base/font-otd.lua b/tex/context/base/font-otd.lua new file mode 100644 index 000000000..41e885331 --- /dev/null +++ b/tex/context/base/font-otd.lua @@ -0,0 +1,78 @@ +if not modules then modules = { } end modules ['font-otd'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) + +fonts = fonts or { } +fonts.otf = fonts.otf or { } + +local otf = fonts.otf +local fontdata = fonts.ids + +otf.features = otf.features or { } +otf.features.default = otf.features.default or { } + +local context_setups = fonts.define.specify.context_setups +local context_numbers = fonts.define.specify.context_numbers + +local a_to_script = { } otf.a_to_script = a_to_script +local a_to_language = { } otf.a_to_language = a_to_language + +function otf.set_dynamics(font,dynamics,attribute) + features = context_setups[context_numbers[attribute]] -- can be moved to caller + if features then + local script = features.script or 'dflt' + local language = features.language or 'dflt' + local ds = dynamics[script] + if not ds then + ds = { } + dynamics[script] = ds + end + local dsl = ds[language] + if not dsl then + dsl = { } + ds[language] = dsl + end + local dsla = dsl[attribute] + if dsla then + -- if trace_dynamics then + -- logs.report("otf define","using dynamics %s: attribute %s, script %s, language %s",context_numbers[attribute],attribute,script,language) + -- end + return dsla + else + local tfmdata = fontdata[font] + a_to_script [attribute] = script + a_to_language[attribute] = language + -- we need to save some values + local saved = { + script = tfmdata.script, + language = tfmdata.language, + mode = tfmdata.mode, + features = tfmdata.shared.features + } + tfmdata.mode = "node" + tfmdata.language = language + tfmdata.script = script + tfmdata.shared.features = { } + -- end of save + dsla = otf.set_features(tfmdata,fonts.define.check(features,otf.features.default)) + if trace_dynamics then + logs.report("otf define","setting dynamics %s: attribute %s, script %s, language %s",context_numbers[attribute],attribute,script,language) + end + -- we need to restore some values + tfmdata.script = saved.script + tfmdata.language = saved.language + tfmdata.mode = saved.mode + tfmdata.shared.features = saved.features + -- end of restore + dynamics[script][language][attribute] = dsla -- cache + return dsla + end + end + return nil -- { } +end diff --git a/tex/context/base/font-otf.lua b/tex/context/base/font-otf.lua new file mode 100644 index 000000000..9cecf21f0 --- /dev/null +++ b/tex/context/base/font-otf.lua @@ -0,0 +1,1752 @@ +if not modules then modules = { } end modules ['font-otf'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local utf = unicode.utf8 + +local concat, getn, utfbyte = table.concat, table.getn, utf.byte +local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip +local type, next, tonumber, tostring = type, next, tonumber, tostring +local abs = math.abs +local lpegmatch = lpeg.match + +local trace_private = false trackers.register("otf.private", function(v) trace_private = v end) +local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) +local trace_features = false trackers.register("otf.features", function(v) trace_features = v end) +local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) +local trace_sequences = false trackers.register("otf.sequences", function(v) trace_sequences = v end) +local trace_math = false trackers.register("otf.math", function(v) trace_math = v end) +local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) + +--~ trackers.enable("otf.loading") + +--[[ldx-- +The fontforge table has organized lookups in a certain way. A first implementation +of this code was organized featurewise: information related to features was +collected and processing boiled down to a run over the features. The current +implementation honors the order in the main feature table. Since we can reorder this +table as we want, we can eventually support several models of processing. We kept +the static as well as dynamic feature processing, because it had proved to be +rather useful. The formerly three loop variants have beem discarded but will +reapear at some time.
+ +In
We process the whole list and then consult the glyph nodes. An alternative approach +is to collect strings of characters using the same font including spaces (because some +lookups involve spaces). However, we then need to reconstruct the list which is no fun. +Also, we need to carry quite some information, like attributes, so eventually we don't +gain much (if we gain something at all).
+ +Another consideration has been to operate on sublists (subhead, subtail) but again +this would complicate matters as we then neext to keep track of a changing subhead +and subtail. On the other hand, this might save some runtime. The number of changes +involved is not that large. This only makes sense when we have many fonts in a list +and don't change to frequently.
+--ldx]]-- + +fonts = fonts or { } +fonts.otf = fonts.otf or { } +fonts.tfm = fonts.tfm or { } + +local otf = fonts.otf +local tfm = fonts.tfm + +local fontdata = fonts.ids + +otf.tables = otf.tables or { } -- defined in font-ott.lua +otf.meanings = otf.meanings or { } -- defined in font-ott.lua +otf.tables.features = otf.tables.features or { } -- defined in font-ott.lua +otf.tables.languages = otf.tables.languages or { } -- defined in font-ott.lua +otf.tables.scripts = otf.tables.scripts or { } -- defined in font-ott.lua + +otf.features = otf.features or { } +otf.features.list = otf.features.list or { } +otf.features.default = otf.features.default or { } + +otf.enhancers = otf.enhancers or { } +otf.glists = { "gsub", "gpos" } + +otf.version = 2.650 -- beware: also sync font-mis.lua +otf.pack = true -- beware: also sync font-mis.lua +otf.syncspace = true +otf.notdef = false +otf.cache = containers.define("fonts", "otf", otf.version, true) +otf.cleanup_aat = false -- only context + +local wildcard = "*" +local default = "dflt" + +--[[ldx-- +We start with a lot of tables and related functions.
+--ldx]]-- + +otf.tables.global_fields = table.tohash { + "lookups", + "glyphs", + "subfonts", + "luatex", + "pfminfo", + "cidinfo", + "tables", + "names", + "unicodes", + "names", +--~ "math", + "anchor_classes", + "kern_classes", + "gpos", + "gsub" +} + +otf.tables.valid_fields = { + "anchor_classes", + "ascent", + "cache_version", + "cidinfo", + "copyright", + "creationtime", + "descent", + "design_range_bottom", + "design_range_top", + "design_size", + "encodingchanged", + "extrema_bound", + "familyname", + "fontname", + "fontstyle_id", + "fontstyle_name", + "fullname", + "glyphs", + "hasvmetrics", + "head_optimized_for_cleartype", + "horiz_base", + "issans", + "isserif", + "italicangle", + "kerns", + "lookups", + -- "luatex", + "macstyle", + "modificationtime", + "onlybitmaps", + "origname", + "os2_version", + "pfminfo", + "private", + "serifcheck", + "sfd_version", + -- "size", + "strokedfont", + "strokewidth", + "subfonts", + "table_version", + -- "tables", + -- "ttf_tab_saved", + "ttf_tables", + "uni_interp", + "uniqueid", + "units_per_em", + "upos", + "use_typo_metrics", + "uwidth", + "validation_state", + "verbose", + "version", + "vert_base", + "weight", + "weight_width_slope_only", + "xuid", +} + +--[[ldx-- +Here we go.
+--ldx]]-- + +local function load_featurefile(ff,featurefile) + if featurefile then + featurefile = resolvers.find_file(file.addsuffix(featurefile,'fea'),'fea') + if featurefile and featurefile ~= "" then + if trace_loading then + logs.report("load otf", "featurefile: %s", featurefile) + end + fontloader.apply_featurefile(ff, featurefile) + end + end +end + +function otf.enhance(name,data,filename,verbose) + local enhancer = otf.enhancers[name] + if enhancer then + if (verbose ~= nil and verbose) or trace_loading then + logs.report("load otf","enhance: %s (%s)",name,filename) + end + enhancer(data,filename) + end +end + +local enhancers = { + -- pack and unpack are handled separately; they might even be moved + -- away from the enhancers namespace + "patch bugs", + "merge cid fonts", "prepare unicode", "cleanup ttf tables", "compact glyphs", "reverse coverage", + "cleanup aat", "enrich with features", "add some missing characters", + "reorganize mark classes", + "reorganize kerns", -- moved here + "flatten glyph lookups", "flatten anchor tables", "flatten feature tables", + "simplify glyph lookups", -- some saving + "prepare luatex tables", + "analyse features", "rehash features", + "analyse anchors", "analyse marks", "analyse unicodes", "analyse subtables", + "check italic correction","check math", + "share widths", + "strip not needed data", + "migrate metadata", + "check math parameters", +} + +function otf.load(filename,format,sub,featurefile) + local name = file.basename(file.removesuffix(filename)) + if featurefile then + name = name .. "@" .. file.removesuffix(file.basename(featurefile)) + end + if sub == "" then sub = false end + local hash = name + if sub then + hash = hash .. "-" .. sub + end + hash = containers.cleanname(hash) + local data = containers.read(otf.cache(), hash) + local size = lfs.attributes(filename,"size") or 0 + if not data or data.verbose ~= fonts.verbose or data.size ~= size then + logs.report("load otf","loading: %s (hash: %s)",filename,hash) + local ff, messages + if sub then + ff, messages = fontloader.open(filename,sub) + else + ff, messages = fontloader.open(filename) + end + if trace_loading and messages and #messages > 0 then + if type(messages) == "string" then + logs.report("load otf","warning: %s",messages) + else + for m=1,#messages do + logs.report("load otf","warning: %s",tostring(messages[m])) + end + end + else + logs.report("load otf","font loaded okay") + end + if ff then + load_featurefile(ff,featurefile) + data = fontloader.to_table(ff) + fontloader.close(ff) + if data then + logs.report("load otf","file size: %s", size) + logs.report("load otf","enhancing ...") + for e=1,#enhancers do + otf.enhance(enhancers[e],data,filename) + io.flush() -- we want instant messages + end + if otf.pack and not fonts.verbose then + otf.enhance("pack",data,filename) + end + data.size = size + data.verbose = fonts.verbose + logs.report("load otf","saving in cache: %s",filename) + data = containers.write(otf.cache(), hash, data) + collectgarbage("collect") + data = containers.read(otf.cache(), hash) -- this frees the old table and load the sparse one + collectgarbage("collect") + else + logs.report("load otf","loading failed (table conversion error)") + end + else + logs.report("load otf","loading failed (file read error)") + end + end + if data then + if trace_defining then + logs.report("define font","loading from cache: %s",hash) + end + otf.enhance("unpack",data,filename,false) -- no message here + otf.add_dimensions(data) + if trace_sequences then + otf.show_feature_order(data,filename) + end + end + return data +end + +function otf.add_dimensions(data) + -- todo: forget about the width if it's the defaultwidth (saves mem) + -- we could also build the marks hash here (instead of storing it) + if data then + local force = otf.notdef + local luatex = data.luatex + local defaultwidth = luatex.defaultwidth or 0 + local defaultheight = luatex.defaultheight or 0 + local defaultdepth = luatex.defaultdepth or 0 + for _, d in next, data.glyphs do + local bb, wd = d.boundingbox, d.width + if not wd then + d.width = defaultwidth + elseif wd ~= 0 and d.class == "mark" then + d.width = -wd + end + if force and not d.name then + d.name = ".notdef" + end + if bb then + local ht, dp = bb[4], -bb[2] + if ht == 0 or ht < 0 then + -- no need to set it and no negative heights, nil == 0 + else + d.height = ht + end + if dp == 0 or dp < 0 then + -- no negative depths and no negative depths, nil == 0 + else + d.depth = dp + end + end + end + end +end + +function otf.show_feature_order(otfdata,filename) + local sequences = otfdata.luatex.sequences + if sequences and #sequences > 0 then + if trace_loading then + logs.report("otf check","font %s has %s sequences",filename,#sequences) + logs.report("otf check"," ") + end + for nos=1,#sequences do + local sequence = sequences[nos] + local typ = sequence.type or "no-type" + local name = sequence.name or "no-name" + local subtables = sequence.subtables or { "no-subtables" } + local features = sequence.features + if trace_loading then + logs.report("otf check","%3i %-15s %-20s [%s]",nos,name,typ,concat(subtables,",")) + end + if features then + for feature, scripts in next, features do + local tt = { } + for script, languages in next, scripts do + local ttt = { } + for language, _ in next, languages do + ttt[#ttt+1] = language + end + tt[#tt+1] = format("[%s: %s]",script,concat(ttt," ")) + end + if trace_loading then + logs.report("otf check"," %s: %s",feature,concat(tt," ")) + end + end + end + end + if trace_loading then + logs.report("otf check","\n") + end + elseif trace_loading then + logs.report("otf check","font %s has no sequences",filename) + end +end + +-- todo: normalize, design_size => designsize + +otf.enhancers["reorganize mark classes"] = function(data,filename) + if data.mark_classes then + local unicodes = data.luatex.unicodes + local reverse = { } + for name, class in next, data.mark_classes do + local t = { } + for s in gmatch(class,"[^ ]+") do + local us = unicodes[s] + if type(us) == "table" then + for u=1,#us do + t[us[u]] = true + end + else + t[us] = true + end + end + reverse[name] = t + end + data.luatex.markclasses = reverse + data.mark_classes = nil + end +end + +otf.enhancers["prepare luatex tables"] = function(data,filename) + data.luatex = data.luatex or { } + local luatex = data.luatex + luatex.filename = filename + luatex.version = otf.version + luatex.creator = "context mkiv" +end + +otf.enhancers["cleanup aat"] = function(data,filename) + if otf.cleanup_aat then + end +end + +local function analyze_features(g, features) + if g then + local t, done = { }, { } + for k=1,#g do + local f = features or g[k].features + if f then + for k=1,#f do + -- scripts and tag + local tag = f[k].tag + if not done[tag] then + t[#t+1] = tag + done[tag] = true + end + end + end + end + if #t > 0 then + return t + end + end + return nil +end + +otf.enhancers["analyse features"] = function(data,filename) + -- local luatex = data.luatex + -- luatex.gposfeatures = analyze_features(data.gpos) + -- luatex.gsubfeatures = analyze_features(data.gsub) +end + +otf.enhancers["rehash features"] = function(data,filename) + local features = { } + data.luatex.features = features + for k, what in next, otf.glists do + local dw = data[what] + if dw then + local f = { } + features[what] = f + for i=1,#dw do + local d= dw[i] + local dfeatures = d.features + if dfeatures then + for i=1,#dfeatures do + local df = dfeatures[i] + local tag = strip(lower(df.tag)) + local ft = f[tag] if not ft then ft = {} f[tag] = ft end + local dscripts = df.scripts + for script, languages in next, dscripts do + script = strip(lower(script)) + local fts = ft[script] if not fts then fts = {} ft[script] = fts end + for i=1,#languages do + fts[strip(lower(languages[i]))] = true + end + end + end + end + end + end + end +end + +otf.enhancers["analyse anchors"] = function(data,filename) + local classes = data.anchor_classes + local luatex = data.luatex + local anchor_to_lookup, lookup_to_anchor = { }, { } + luatex.anchor_to_lookup, luatex.lookup_to_anchor = anchor_to_lookup, lookup_to_anchor + if classes then + for c=1,#classes do + local class = classes[c] + local anchor = class.name + local lookups = class.lookup + if type(lookups) ~= "table" then + lookups = { lookups } + end + local a = anchor_to_lookup[anchor] + if not a then a = { } anchor_to_lookup[anchor] = a end + for l=1,#lookups do + local lookup = lookups[l] + local l = lookup_to_anchor[lookup] + if not l then l = { } lookup_to_anchor[lookup] = l end + l[anchor] = true + a[lookup] = true + end + end + end +end + +otf.enhancers["analyse marks"] = function(data,filename) + local glyphs = data.glyphs + local marks = { } + data.luatex.marks = marks + for unicode, index in next, data.luatex.indices do + local glyph = glyphs[index] + if glyph.class == "mark" then + marks[unicode] = true + end + end +end + +otf.enhancers["analyse unicodes"] = fonts.map.add_to_unicode + +otf.enhancers["analyse subtables"] = function(data,filename) + data.luatex = data.luatex or { } + local luatex = data.luatex + local sequences = { } + local lookups = { } + luatex.sequences = sequences + luatex.lookups = lookups + for _, g in next, { data.gsub, data.gpos } do + for k=1,#g do + local gk = g[k] + local typ = gk.type + if typ == "gsub_contextchain" or typ == "gpos_contextchain" then + gk.chain = 1 + elseif typ == "gsub_reversecontextchain" or typ == "gpos_reversecontextchain" then + gk.chain = -1 + else + gk.chain = 0 + end + local features = gk.features + if features then + sequences[#sequences+1] = gk + -- scripts, tag, ismac + local t = { } + for f=1,#features do + local feature = features[f] + local hash = { } + -- only script and langs matter + for s, languages in next, feature.scripts do + s = lower(s) + local h = hash[s] + if not h then h = { } hash[s] = h end + for l=1,#languages do + h[strip(lower(languages[l]))] = true + end + end + t[feature.tag] = hash + end + gk.features = t + else + lookups[gk.name] = gk + gk.name = nil + end + local subtables = gk.subtables + if subtables then + local t = { } + for s=1,#subtables do + local subtable = subtables[s] + local name = subtable.name + t[#t+1] = name + end + gk.subtables = t + end + local flags = gk.flags + if flags then + gk.flags = { -- forcing false packs nicer + (flags.ignorecombiningmarks and "mark") or false, + (flags.ignoreligatures and "ligature") or false, + (flags.ignorebaseglyphs and "base") or false, + flags.r2l or false, + } + if flags.mark_class then + gk.markclass = luatex.markclasses[flags.mark_class] + end + end + end + end +end + +otf.enhancers["merge cid fonts"] = function(data,filename) + -- we can also move the names to data.luatex.names which might + -- save us some more memory (at the cost of harder tracing) + if data.subfonts then + if data.glyphs and next(data.glyphs) then + logs.report("load otf","replacing existing glyph table due to subfonts") + end + local cidinfo = data.cidinfo + local verbose = fonts.verbose + if cidinfo.registry then + local cidmap, cidname = fonts.cid.getmap(cidinfo.registry,cidinfo.ordering,cidinfo.supplement) + if cidmap then + cidinfo.usedname = cidmap.usedname + local glyphs, uni_to_int, int_to_uni, nofnames, nofunicodes = { }, { }, { }, 0, 0 + local unicodes, names = cidmap.unicodes, cidmap.names + for n, subfont in next, data.subfonts do + for index, g in next, subfont.glyphs do + if not next(g) then + -- dummy entry + else + local unicode, name = unicodes[index], names[index] + g.cidindex = n + g.boundingbox = g.boundingbox -- or zerobox + g.name = g.name or name or "unknown" + if unicode then + uni_to_int[unicode] = index + int_to_uni[index] = unicode + nofunicodes = nofunicodes + 1 + g.unicode = unicode + elseif name then + nofnames = nofnames + 1 + g.unicode = -1 + end + glyphs[index] = g + end + end + subfont.glyphs = nil + end + if trace_loading then + logs.report("load otf","cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes, nofnames, nofunicodes+nofnames) + end + data.glyphs = glyphs + data.map = data.map or { } + data.map.map = uni_to_int + data.map.backmap = int_to_uni + elseif trace_loading then + logs.report("load otf","unable to remap cid font, missing cid file for %s",filename) + end + elseif trace_loading then + logs.report("load otf","font %s has no glyphs",filename) + end + end +end + +otf.enhancers["prepare unicode"] = function(data,filename) + local luatex = data.luatex + if not luatex then luatex = { } data.luatex = luatex end + local indices, unicodes, multiples, internals = { }, { }, { }, { } + local glyphs = data.glyphs + local mapmap = data.map + if not mapmap then + logs.report("load otf","no map in %s",filename) + mapmap = { } + data.map = { map = mapmap } + elseif not mapmap.map then + logs.report("load otf","no unicode map in %s",filename) + mapmap = { } + data.map.map = mapmap + else + mapmap = mapmap.map + end + local criterium = fonts.private + local private = fonts.private + for index, glyph in next, glyphs do + if index > 0 then + local name = glyph.name + if name then + local unicode = glyph.unicode + if unicode == -1 or unicode >= criterium then + glyph.unicode = private + indices[private] = index + unicodes[name] = private + internals[index] = true + if trace_private then + logs.report("load otf","enhance: glyph %s at index U+%04X is moved to private unicode slot U+%04X",name,index,private) + end + private = private + 1 + else + indices[unicode] = index + unicodes[name] = unicode + end + end + end + end + -- beware: the indices table is used to initialize the tfm table + for unicode, index in next, mapmap do + if not internals[index] then + local name = glyphs[index].name + if name then + local un = unicodes[name] + if not un then + unicodes[name] = unicode -- or 0 + elseif type(un) == "number" then + if un ~= unicode then + multiples[#multiples+1] = name + unicodes[name] = { un, unicode } + indices[unicode] = index + end + else + local ok = false + for u=1,#un do + if un[u] == unicode then + ok = true + break + end + end + if not ok then + multiples[#multiples+1] = name + un[#un+1] = unicode + indices[unicode] = index + end + end + end + end + end + if trace_loading then + if #multiples > 0 then + logs.report("load otf","%s glyph are reused: %s",#multiples, concat(multiples," ")) + else + logs.report("load otf","no glyph are reused") + end + end + luatex.indices = indices + luatex.unicodes = unicodes + luatex.private = private +end + +otf.enhancers["cleanup ttf tables"] = function(data,filename) + local ttf_tables = data.ttf_tables + if ttf_tables then + for k=1,#ttf_tables do + if ttf_tables[k].data then ttf_tables[k].data = "deleted" end + end + end + data.ttf_tab_saved = nil +end + +otf.enhancers["compact glyphs"] = function(data,filename) + table.compact(data.glyphs) -- needed? + if data.subfonts then + for _, subfont in next, data.subfonts do + table.compact(subfont.glyphs) -- needed? + end + end +end + +otf.enhancers["reverse coverage"] = function(data,filename) + -- we prefer the before lookups in a normal order + if data.lookups then + for _, v in next, data.lookups do + if v.rules then + for _, vv in next, v.rules do + local c = vv.coverage + if c and c.before then + c.before = table.reverse(c.before) + end + end + end + end + end +end + +otf.enhancers["check italic correction"] = function(data,filename) + local glyphs = data.glyphs + local ok = false + for index, glyph in next, glyphs do + local ic = glyph.italic_correction + if ic then + if ic ~= 0 then + glyph.italic = ic + end + glyph.italic_correction = nil + ok = true + end + end + -- we can use this to avoid calculations + otf.tables.valid_fields[#otf.tables.valid_fields+1] = "has_italic" + data.has_italic = true +end + +otf.enhancers["check math"] = function(data,filename) + if data.math then + -- we move the math stuff into a math subtable because we then can + -- test faster in the tfm copy + local glyphs = data.glyphs + local unicodes = data.luatex.unicodes + for index, glyph in next, glyphs do + local mk = glyph.mathkern + local hv = glyph.horiz_variants + local vv = glyph.vert_variants + if mk or hv or vv then + local math = { } + glyph.math = math + if mk then + for k, v in next, mk do + if not next(v) then + mk[k] = nil + end + end + math.kerns = mk + glyph.mathkern = nil + end + if hv then + math.horiz_variants = hv.variants + local p = hv.parts + if p then + if #p>0 then + for i=1,#p do + local pi = p[i] + pi.glyph = unicodes[pi.component] or 0 + end + math.horiz_parts = p + end + end + local ic = hv.italic_correction + if ic and ic ~= 0 then + math.horiz_italic_correction = ic + end + glyph.horiz_variants = nil + end + if vv then + local uc = unicodes[index] + math.vert_variants = vv.variants + local p = vv.parts + if p then + if #p>0 then + for i=1,#p do + local pi = p[i] + pi.glyph = unicodes[pi.component] or 0 + end + math.vert_parts = p + end + end + local ic = vv.italic_correction + if ic and ic ~= 0 then + math.vert_italic_correction = ic + end + glyph.vert_variants = nil + end + local ic = glyph.italic_correction + if ic then + if ic ~= 0 then + math.italic_correction = ic + end + glyph.italic_correction = nil + end + end + end + end +end + +otf.enhancers["share widths"] = function(data,filename) + local glyphs = data.glyphs + local widths = { } + for index, glyph in next, glyphs do + local width = glyph.width + widths[width] = (widths[width] or 0) + 1 + end + -- share width for cjk fonts + local wd, most = 0, 1 + for k,v in next, widths do + if v > most then + wd, most = k, v + end + end + if most > 1000 then + if trace_loading then + logs.report("load otf", "most common width: %s (%s times), sharing (cjk font)",wd,most) + end + for k, v in next, glyphs do + if v.width == wd then + v.width = nil + end + end + data.luatex.defaultwidth = wd + end +end + +-- kern: ttf has a table with kerns + +--~ otf.enhancers["reorganize kerns"] = function(data,filename) +--~ local glyphs, mapmap, unicodes = data.glyphs, data.luatex.indices, data.luatex.unicodes +--~ local mkdone = false +--~ for index, glyph in next, data.glyphs do +--~ if glyph.kerns then +--~ local mykerns = { } +--~ for k,v in next, glyph.kerns do +--~ local vc, vo, vl = v.char, v.off, v.lookup +--~ if vc and vo and vl then -- brrr, wrong! we miss the non unicode ones +--~ local uvc = unicodes[vc] +--~ if not uvc then +--~ if trace_loading then +--~ logs.report("load otf","problems with unicode %s of kern %s at glyph %s",vc,k,index) +--~ end +--~ else +--~ if type(vl) ~= "table" then +--~ vl = { vl } +--~ end +--~ for l=1,#vl do +--~ local vll = vl[l] +--~ local mkl = mykerns[vll] +--~ if not mkl then +--~ mkl = { } +--~ mykerns[vll] = mkl +--~ end +--~ if type(uvc) == "table" then +--~ for u=1,#uvc do +--~ mkl[uvc[u]] = vo +--~ end +--~ else +--~ mkl[uvc] = vo +--~ end +--~ end +--~ end +--~ end +--~ end +--~ glyph.mykerns = mykerns +--~ glyph.kerns = nil -- saves space and time +--~ mkdone = true +--~ end +--~ end +--~ if trace_loading and mkdone then +--~ logs.report("load otf", "replacing 'kerns' tables by 'mykerns' tables") +--~ end +--~ if data.kerns then +--~ if trace_loading then +--~ logs.report("load otf", "removing global 'kern' table") +--~ end +--~ data.kerns = nil +--~ end +--~ local dgpos = data.gpos +--~ if dgpos then +--~ for gp=1,#dgpos do +--~ local gpos = dgpos[gp] +--~ local subtables = gpos.subtables +--~ if subtables then +--~ for s=1,#subtables do +--~ local subtable = subtables[s] +--~ local kernclass = subtable.kernclass -- name is inconsistent with anchor_classes +--~ if kernclass then -- the next one is quite slow +--~ for k=1,#kernclass do +--~ local kcl = kernclass[k] +--~ local firsts, seconds, offsets, lookups = kcl.firsts, kcl.seconds, kcl.offsets, kcl.lookup -- singular +--~ if type(lookups) ~= "table" then +--~ lookups = { lookups } +--~ end +--~ for l=1,#lookups do +--~ local lookup = lookups[l] +--~ -- weird, as maxfirst and maxseconds can have holes +--~ local maxfirsts, maxseconds = getn(firsts), getn(seconds) +--~ if trace_loading then +--~ logs.report("load otf", "adding kernclass %s with %s times %s pairs",lookup, maxfirsts, maxseconds) +--~ end +--~ for fk, fv in next, firsts do +--~ for first in gmatch(fv,"[^ ]+") do +--~ local first_unicode = unicodes[first] +--~ if type(first_unicode) == "number" then +--~ first_unicode = { first_unicode } +--~ end +--~ for f=1,#first_unicode do +--~ local glyph = glyphs[mapmap[first_unicode[f]]] +--~ if glyph then +--~ local mykerns = glyph.mykerns +--~ if not mykerns then +--~ mykerns = { } -- unicode indexed ! +--~ glyph.mykerns = mykerns +--~ end +--~ local lookupkerns = mykerns[lookup] +--~ if not lookupkerns then +--~ lookupkerns = { } +--~ mykerns[lookup] = lookupkerns +--~ end +--~ for sk, sv in next, seconds do +--~ local offset = offsets[(fk-1) * maxseconds + sk] +--~ --~ local offset = offsets[sk] -- (fk-1) * maxseconds + sk] +--~ for second in gmatch(sv,"[^ ]+") do +--~ local second_unicode = unicodes[second] +--~ if type(second_unicode) == "number" then +--~ lookupkerns[second_unicode] = offset +--~ else +--~ for s=1,#second_unicode do +--~ lookupkerns[second_unicode[s]] = offset +--~ end +--~ end +--~ end +--~ end +--~ elseif trace_loading then +--~ logs.report("load otf", "no glyph data for U+%04X", first_unicode[f]) +--~ end +--~ end +--~ end +--~ end +--~ end +--~ end +--~ subtable.comment = "The kernclass table is merged into mykerns in the indexed glyph tables." +--~ subtable.kernclass = { } +--~ end +--~ end +--~ end +--~ end +--~ end +--~ end + +otf.enhancers["reorganize kerns"] = function(data,filename) + local glyphs, mapmap, unicodes = data.glyphs, data.luatex.indices, data.luatex.unicodes + local mkdone = false + for index, glyph in next, data.glyphs do + if glyph.kerns then + local mykerns = { } + for k,v in next, glyph.kerns do + local vc, vo, vl = v.char, v.off, v.lookup + if vc and vo and vl then -- brrr, wrong! we miss the non unicode ones + local uvc = unicodes[vc] + if not uvc then + if trace_loading then + logs.report("load otf","problems with unicode %s of kern %s at glyph %s",vc,k,index) + end + else + if type(vl) ~= "table" then + vl = { vl } + end + for l=1,#vl do + local vll = vl[l] + local mkl = mykerns[vll] + if not mkl then + mkl = { } + mykerns[vll] = mkl + end + if type(uvc) == "table" then + for u=1,#uvc do + mkl[uvc[u]] = vo + end + else + mkl[uvc] = vo + end + end + end + end + end + glyph.mykerns = mykerns + glyph.kerns = nil -- saves space and time + mkdone = true + end + end + if trace_loading and mkdone then + logs.report("load otf", "replacing 'kerns' tables by 'mykerns' tables") + end + if data.kerns then + if trace_loading then + logs.report("load otf", "removing global 'kern' table") + end + data.kerns = nil + end + local dgpos = data.gpos + if dgpos then + local separator = lpeg.P(" ") + local other = ((1 - separator)^0) / unicodes + local splitter = lpeg.Ct(other * (separator * other)^0) + for gp=1,#dgpos do + local gpos = dgpos[gp] + local subtables = gpos.subtables + if subtables then + for s=1,#subtables do + local subtable = subtables[s] + local kernclass = subtable.kernclass -- name is inconsistent with anchor_classes + if kernclass then -- the next one is quite slow + for k=1,#kernclass do + local kcl = kernclass[k] + local firsts, seconds, offsets, lookups = kcl.firsts, kcl.seconds, kcl.offsets, kcl.lookup -- singular + if type(lookups) ~= "table" then + lookups = { lookups } + end + local split = { } + for l=1,#lookups do + local lookup = lookups[l] + -- weird, as maxfirst and maxseconds can have holes, first seems to be indexed, seconds starts at 2 + local maxfirsts, maxseconds = getn(firsts), getn(seconds) + for _, s in next, firsts do + split[s] = split[s] or lpegmatch(splitter,s) + end + for _, s in next, seconds do + split[s] = split[s] or lpegmatch(splitter,s) + end + if trace_loading then + logs.report("load otf", "adding kernclass %s with %s times %s pairs",lookup, maxfirsts, maxseconds) + end + local function do_it(fk,first_unicode) + local glyph = glyphs[mapmap[first_unicode]] + if glyph then + local mykerns = glyph.mykerns + if not mykerns then + mykerns = { } -- unicode indexed ! + glyph.mykerns = mykerns + end + local lookupkerns = mykerns[lookup] + if not lookupkerns then + lookupkerns = { } + mykerns[lookup] = lookupkerns + end + local baseoffset = (fk-1) * maxseconds + for sk=2,maxseconds do + local sv = seconds[sk] + local offset = offsets[baseoffset + sk] + --~ local offset = offsets[sk] -- (fk-1) * maxseconds + sk] + local splt = split[sv] + if splt then + for i=1,#splt do + local second_unicode = splt[i] + if tonumber(second_unicode) then + lookupkerns[second_unicode] = offset + else + for s=1,#second_unicode do + lookupkerns[second_unicode[s]] = offset + end + end + end + end + end + elseif trace_loading then + logs.report("load otf", "no glyph data for U+%04X", first_unicode) + end + end + for fk=1,#firsts do + local fv = firsts[fk] + local splt = split[fv] + if splt then + for i=1,#splt do + local first_unicode = splt[i] + if tonumber(first_unicode) then + do_it(fk,first_unicode) + else + for f=1,#first_unicode do + do_it(fk,first_unicode[f]) + end + end + end + end + end + end + end + subtable.comment = "The kernclass table is merged into mykerns in the indexed glyph tables." + subtable.kernclass = { } + end + end + end + end + end +end + +otf.enhancers["strip not needed data"] = function(data,filename) + local verbose = fonts.verbose + local int_to_uni = data.luatex.unicodes + for k, v in next, data.glyphs do + local d = v.dependents + if d then v.dependents = nil end + local a = v.altuni + if a then v.altuni = nil end + if verbose then + local code = int_to_uni[k] + -- looks like this is done twice ... bug? + if code then + local vu = v.unicode + if not vu then + v.unicode = code + elseif type(vu) == "table" then + if vu[#vu] == code then + -- weird + else + vu[#vu+1] = code + end + elseif vu ~= code then + v.unicode = { vu, code } + end + end + else + v.unicode = nil + v.index = nil + end + end + data.luatex.comment = "Glyph tables have their original index. When present, mykern tables are indexed by unicode." + data.map = nil + data.names = nil -- funny names for editors + data.glyphcnt = nil + data.glyphmax = nil + if true then + data.gpos = nil + data.gsub = nil + data.anchor_classes = nil + end +end + +otf.enhancers["migrate metadata"] = function(data,filename) + local global_fields = otf.tables.global_fields + local metadata = { } + for k,v in next, data do + if not global_fields[k] then + metadata[k] = v + data[k] = nil + end + end + data.metadata = metadata + -- goodies + local pfminfo = data.pfminfo + metadata.isfixedpitch = metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose["proportion"] == "Monospaced") + metadata.charwidth = pfminfo and pfminfo.avgwidth +end + +local private_math_parameters = { + "FractionDelimiterSize", + "FractionDelimiterDisplayStyleSize", +} + +otf.enhancers["check math parameters"] = function(data,filename) + local mathdata = data.metadata.math + if mathdata then + for m=1,#private_math_parameters do + local pmp = private_math_parameters[m] + if not mathdata[pmp] then + if trace_loading then + logs.report("load otf", "setting math parameter '%s' to 0", pmp) + end + mathdata[pmp] = 0 + end + end + end +end + +otf.enhancers["flatten glyph lookups"] = function(data,filename) + for k, v in next, data.glyphs do + local lookups = v.lookups + if lookups then + for kk, vv in next, lookups do + for kkk=1,#vv do + local vvv = vv[kkk] + local s = vvv.specification + if s then + local t = vvv.type + if t == "ligature" then + vv[kkk] = { "ligature", s.components, s.char } + elseif t == "alternate" then + vv[kkk] = { "alternate", s.components } + elseif t == "substitution" then + vv[kkk] = { "substitution", s.variant } + elseif t == "multiple" then + vv[kkk] = { "multiple", s.components } + elseif t == "position" then + vv[kkk] = { "position", { s.x or 0, s.y or 0, s.h or 0, s.v or 0 } } + elseif t == "pair" then + local one, two, paired = s.offsets[1], s.offsets[2], s.paired or "" + if one then + if two then + vv[kkk] = { "pair", paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0 } } + else + vv[kkk] = { "pair", paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 } } + end + else + if two then + vv[kkk] = { "pair", paired, { }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0} } -- maybe nil instead of { } + else + vv[kkk] = { "pair", paired } + end + end + else + if trace_loading then + logs.report("load otf", "flattening needed, report to context list") + end + for a, b in next, s do + if trace_loading and vvv[a] then + logs.report("load otf", "flattening conflict, report to context list") + end + vvv[a] = b + end + vvv.specification = nil + end + end + end + end + end + end +end + +otf.enhancers["simplify glyph lookups"] = function(data,filename) + for k, v in next, data.glyphs do + local lookups = v.lookups + if lookups then + local slookups, mlookups + for kk, vv in next, lookups do + if #vv == 1 then + if not slookups then + slookups = { } + v.slookups = slookups + end + slookups[kk] = vv[1] + else + if not mlookups then + mlookups = { } + v.mlookups = mlookups + end + mlookups[kk] = vv + end + end + v.lookups = nil + end + end +end + +otf.enhancers["flatten anchor tables"] = function(data,filename) + for k, v in next, data.glyphs do + if v.anchors then + for kk, vv in next, v.anchors do + for kkk, vvv in next, vv do + if vvv.x or vvv.y then + vv[kkk] = { vvv.x or 0, vvv.y or 0 } + else + for kkkk=1,#vvv do + local vvvv = vvv[kkkk] + vvv[kkkk] = { vvvv.x or 0, vvvv.y or 0 } + end + end + end + end + end + end +end + +otf.enhancers["flatten feature tables"] = function(data,filename) + -- is this needed? do we still use them at all? + for _, tag in next, otf.glists do + if data[tag] then + if trace_loading then + logs.report("load otf", "flattening %s table", tag) + end + for k, v in next, data[tag] do + local features = v.features + if features then + for kk=1,#features do + local vv = features[kk] + local t = { } + local scripts = vv.scripts + for kkk=1,#scripts do + local vvv = scripts[kkk] + t[vvv.script] = vvv.langs + end + vv.scripts = t + end + end + end + end + end +end + +otf.enhancers.patches = otf.enhancers.patches or { } + +otf.enhancers["patch bugs"] = function(data,filename) + local basename = file.basename(lower(filename)) + for pattern, action in next, otf.enhancers.patches do + if find(basename,pattern) then + action(data,filename) + end + end +end + +-- tex features + +fonts.otf.enhancers["enrich with features"] = function(data,filename) + -- later, ctx only +end + +function otf.features.register(name,default) + otf.features.list[#otf.features.list+1] = name + otf.features.default[name] = default +end + +-- for context this will become a task handler + +function otf.set_features(tfmdata,features) + local processes = { } + if features and next(features) then + local lists = { -- why local + fonts.triggers, + fonts.processors, + fonts.manipulators, + } + local mode = tfmdata.mode or fonts.mode -- or features.mode + local initializers = fonts.initializers + local fi = initializers[mode] + if fi then + local fiotf = fi.otf + if fiotf then + local done = { } + for l=1,4 do + local list = lists[l] + if list then + for i=1,#list do + local f = list[i] + local value = features[f] + if value and fiotf[f] then -- brr + if not done[f] then -- so, we can move some to triggers + if trace_features then + logs.report("define otf","initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown', tfmdata.fullname or 'unknown') + end + fiotf[f](tfmdata,value) -- can set mode (no need to pass otf) + mode = tfmdata.mode or fonts.mode -- keep this, mode can be set local ! + local im = initializers[mode] + if im then + fiotf = initializers[mode].otf + end + done[f] = true + end + end + end + end + end + end + end + local fm = fonts.methods[mode] -- todo: zonder node/mode otf/... + if fm then + local fmotf = fm.otf + if fmotf then + for l=1,4 do + local list = lists[l] + if list then + for i=1,#list do + local f = list[i] + if fmotf[f] then -- brr + if trace_features then + logs.report("define otf","installing feature handler %s for mode %s for font %s",f,mode or 'unknown', tfmdata.fullname or 'unknown') + end + processes[#processes+1] = fmotf[f] + end + end + end + end + end + else + -- message + end + end + return processes, features +end + +function otf.otf_to_tfm(specification) + local name = specification.name + local sub = specification.sub + local filename = specification.filename + local format = specification.format + local features = specification.features.normal + local cache_id = specification.hash + local tfmdata = containers.read(tfm.cache(),cache_id) +--~ print(cache_id) + if not tfmdata then + local otfdata = otf.load(filename,format,sub,features and features.featurefile) + if otfdata and next(otfdata) then + otfdata.shared = otfdata.shared or { + featuredata = { }, + anchorhash = { }, + initialized = false, + } + tfmdata = otf.copy_to_tfm(otfdata,cache_id) + if tfmdata and next(tfmdata) then + tfmdata.unique = tfmdata.unique or { } + tfmdata.shared = tfmdata.shared or { } -- combine + local shared = tfmdata.shared + shared.otfdata = otfdata + shared.features = features -- default + shared.dynamics = { } + shared.processes = { } + shared.set_dynamics = otf.set_dynamics -- fast access and makes other modules independent + -- this will be done later anyway, but it's convenient to have + -- them already for fast access + tfmdata.luatex = otfdata.luatex + tfmdata.indices = otfdata.luatex.indices + tfmdata.unicodes = otfdata.luatex.unicodes + tfmdata.marks = otfdata.luatex.marks + tfmdata.originals = otfdata.luatex.originals + tfmdata.changed = { } + tfmdata.has_italic = otfdata.metadata.has_italic + if not tfmdata.language then tfmdata.language = 'dflt' end + if not tfmdata.script then tfmdata.script = 'dflt' end + shared.processes, shared.features = otf.set_features(tfmdata,fonts.define.check(features,otf.features.default)) + end + end + containers.write(tfm.cache(),cache_id,tfmdata) + end + return tfmdata +end + +--~ { +--~ ['boundingbox']={ 95, -458, 733, 1449 }, +--~ ['class']="base", +--~ ['name']="braceleft", +--~ ['unicode']=123, +--~ ['vert_variants']={ +--~ ['italic_correction']=0, +--~ ['parts']={ +--~ { ['component']="uni23A9", ['endConnectorLength']=1000, ['fullAdvance']=2546, ['is_extender']=0, ['startConnectorLength']=0, }, -- bot +--~ { ['component']="uni23AA", ['endConnectorLength']=2500, ['fullAdvance']=2501, ['is_extender']=1, ['startConnectorLength']=2500, }, -- rep +--~ { ['component']="uni23A8", ['endConnectorLength']=1000, ['fullAdvance']=4688, ['is_extender']=0, ['startConnectorLength']=1000, }, -- mid +--~ { ['component']="uni23AA", ['endConnectorLength']=2500, ['fullAdvance']=2501, ['is_extender']=1, ['startConnectorLength']=2500, }, -- rep +--~ { ['component']="uni23A7", ['endConnectorLength']=0, ['fullAdvance']=2546, ['is_extender']=0, ['startConnectorLength']=1000, }, -- top +--~ }, +--~ ['variants']="braceleft braceleft.vsize1 braceleft.vsize2 braceleft.vsize3 braceleft.vsize4 braceleft.vsize5 braceleft.vsize6 braceleft.vsize7", +--~ }, +--~ ['width']=793, +--~ }, + +-- the first version made a top/mid/not extensible table, now we just pass on the variants data +-- and deal with it in the tfm scaler (there is no longer an extensible table anyway) + +-- we cannot share descriptions as virtual fonts might extend them (ok, we could +-- use a cache with a hash + +fonts.formats.dfont = "truetype" +fonts.formats.ttc = "truetype" +fonts.formats.ttf = "truetype" +fonts.formats.otf = "opentype" + +function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder the tma to unicode (nasty due to one->many) + if data then + local glyphs, pfminfo, metadata = data.glyphs or { }, data.pfminfo or { }, data.metadata or { } + local luatex = data.luatex + local unicodes = luatex.unicodes -- names to unicodes + local indices = luatex.indices + local characters, parameters, math_parameters, descriptions = { }, { }, { }, { } + local designsize = metadata.designsize or metadata.design_size or 100 + if designsize == 0 then + designsize = 100 + end + local spaceunits = 500 + -- indices maps from unicodes to indices + for u, i in next, indices do + characters[u] = { } -- we need this because for instance we add protruding info and loop over characters + descriptions[u] = glyphs[i] + end + -- math + if metadata.math then + -- parameters + for name, value in next, metadata.math do + math_parameters[name] = value + end + -- we could use a subset + for u, char in next, characters do + local d = descriptions[u] + local m = d.math + -- we have them shared because that packs nicer + -- we could prepare the variants and keep 'm in descriptions + if m then + local variants = m.horiz_variants + if variants then + local c = char + for n in gmatch(variants,"[^ ]+") do + local un = unicodes[n] + if un and u ~= un then + c.next = un + c = characters[un] + end + end + c.horiz_variants = m.horiz_parts + else + local variants = m.vert_variants + if variants then + local c = char + for n in gmatch(variants,"[^ ]+") do + local un = unicodes[n] + if un and u ~= un then + c.next = un + c = characters[un] + end + end + c.vert_variants = m.vert_parts + c.vert_italic_correction = m.vert_italic_correction + end + end + local kerns = m.kerns + if kerns then + char.mathkerns = kerns + end + end + end + end + -- end math + local endash, emdash, space = 0x20, 0x2014, "space" -- unicodes['space'], unicodes['emdash'] + if metadata.isfixedpitch then + if descriptions[endash] then + spaceunits, spacer = descriptions[endash].width, "space" + end + if not spaceunits and descriptions[emdash] then + spaceunits, spacer = descriptions[emdash].width, "emdash" + end + if not spaceunits and metadata.charwidth then + spaceunits, spacer = metadata.charwidth, "charwidth" + end + else + if descriptions[endash] then + spaceunits, spacer = descriptions[endash].width, "space" + end + if not spaceunits and descriptions[emdash] then + spaceunits, spacer = descriptions[emdash].width/2, "emdash/2" + end + if not spaceunits and metadata.charwidth then + spaceunits, spacer = metadata.charwidth, "charwidth" + end + end + spaceunits = tonumber(spaceunits) or tfm.units/2 -- 500 -- brrr + -- we need a runtime lookup because of running from cdrom or zip, brrr (shouldn't we use the basename then?) + local filename = fonts.tfm.checked_filename(luatex) + local fontname = metadata.fontname + local fullname = metadata.fullname or fontname + local cidinfo = data.cidinfo + local units = metadata.units_per_em or 1000 + -- + cidinfo.registry = cidinfo and cidinfo.registry or "" -- weird here, fix upstream + -- + parameters.slant = 0 + parameters.space = spaceunits -- 3.333 (cmr10) + parameters.space_stretch = units/2 -- 500 -- 1.666 (cmr10) + parameters.space_shrink = 1*units/3 -- 333 -- 1.111 (cmr10) + parameters.x_height = 2*units/5 -- 400 + parameters.quad = units -- 1000 + if spaceunits < 2*units/5 then + -- todo: warning + end + local italicangle = metadata.italicangle + if italicangle then -- maybe also in afm _ + parameters.slant = parameters.slant - math.round(math.tan(italicangle*math.pi/180)) + end + if metadata.isfixedpitch then + parameters.space_stretch = 0 + parameters.space_shrink = 0 + elseif otf.syncspace then -- + parameters.space_stretch = spaceunits/2 + parameters.space_shrink = spaceunits/3 + end + parameters.extra_space = parameters.space_shrink -- 1.111 (cmr10) + if pfminfo.os2_xheight and pfminfo.os2_xheight > 0 then + parameters.x_height = pfminfo.os2_xheight + else + local x = 0x78 -- unicodes['x'] + if x then + local x = descriptions[x] + if x then + parameters.x_height = x.height + end + end + end + -- + return { + characters = characters, + parameters = parameters, + math_parameters = math_parameters, + descriptions = descriptions, + indices = indices, + unicodes = unicodes, + type = "real", + direction = 0, + boundarychar_label = 0, + boundarychar = 65536, + designsize = (designsize/10)*65536, + spacer = "500 units", + encodingbytes = 2, + filename = filename, + fontname = fontname, + fullname = fullname, + psname = fontname or fullname, + name = filename or fullname, + units = units, + format = fonts.fontformat(filename,"opentype"), + cidinfo = cidinfo, + ascender = abs(metadata.ascent or 0), + descender = abs(metadata.descent or 0), + spacer = spacer, + italicangle = italicangle, + } + else + return nil + end +end + +otf.features.register('mathsize') + +function tfm.read_from_open_type(specification) + local tfmtable = otf.otf_to_tfm(specification) + if tfmtable then + local otfdata = tfmtable.shared.otfdata + tfmtable.name = specification.name + tfmtable.sub = specification.sub + local s = specification.size + local m = otfdata.metadata.math + if m then + -- this will move to a function + local f = specification.features + if f then + local f = f.normal + if f and f.mathsize then + local mathsize = specification.mathsize or 0 + if mathsize == 2 then + local p = m.ScriptPercentScaleDown + if p then + local ps = p * specification.textsize / 100 + if trace_math then + logs.report("define font","asked script size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100) + end + s = ps + end + elseif mathsize == 3 then + local p = m.ScriptScriptPercentScaleDown + if p then + local ps = p * specification.textsize / 100 + if trace_math then + logs.report("define font","asked scriptscript size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100) + end + s = ps + end + end + end + end + end + tfmtable = tfm.scale(tfmtable,s,specification.relativeid) + if tfm.fontname_mode == "specification" then + -- not to be used in context ! + local specname = specification.specification + if specname then + tfmtable.name = specname + if trace_defining then + logs.report("define font","overloaded fontname: '%s'",specname) + end + end + end + fonts.logger.save(tfmtable,file.extname(specification.filename),specification) + end +--~ print(tfmtable.fullname) + return tfmtable +end + +-- helpers + +function otf.collect_lookups(otfdata,kind,script,language) + -- maybe store this in the font + local sequences = otfdata.luatex.sequences + if sequences then + local featuremap, featurelist = { }, { } + for s=1,#sequences do + local sequence = sequences[s] + local features = sequence.features + features = features and features[kind] + features = features and (features[script] or features[default] or features[wildcard]) + features = features and (features[language] or features[default] or features[wildcard]) + if features then + local subtables = sequence.subtables + if subtables then + for s=1,#subtables do + local ss = subtables[s] + if not featuremap[s] then + featuremap[ss] = true + featurelist[#featurelist+1] = ss + end + end + end + end + end + if #featurelist > 0 then + return featuremap, featurelist + end + end + return nil, nil +end diff --git a/tex/context/base/font-oti.lua b/tex/context/base/font-oti.lua new file mode 100644 index 000000000..4cb270626 --- /dev/null +++ b/tex/context/base/font-oti.lua @@ -0,0 +1,57 @@ +if not modules then modules = { } end modules ['font-oti'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- i need to check features=yes|no also in relation to hashing + +local lower = string.lower + +local otf = fonts.otf + +otf.default_language = 'latn' +otf.default_script = 'dflt' + +local languages = otf.tables.languages +local scripts = otf.tables.scripts + +function otf.features.language(tfmdata,value) + if value then + value = lower(value) + if languages[value] then + tfmdata.language = value + end + end +end + +function otf.features.script(tfmdata,value) + if value then + value = lower(value) + if scripts[value] then + tfmdata.script = value + end + end +end + +function otf.features.mode(tfmdata,value) + if value then + tfmdata.mode = lower(value) + end +end + +fonts.initializers.base.otf.language = otf.features.language +fonts.initializers.base.otf.script = otf.features.script +fonts.initializers.base.otf.mode = otf.features.mode +fonts.initializers.base.otf.method = otf.features.mode + +fonts.initializers.node.otf.language = otf.features.language +fonts.initializers.node.otf.script = otf.features.script +fonts.initializers.node.otf.mode = otf.features.mode +fonts.initializers.node.otf.method = otf.features.mode + +otf.features.register("features",true) -- we always do features +table.insert(fonts.processors,"features") -- we need a proper function for doing this + diff --git a/tex/context/base/font-otn.lua b/tex/context/base/font-otn.lua new file mode 100644 index 000000000..d4f89adc6 --- /dev/null +++ b/tex/context/base/font-otn.lua @@ -0,0 +1,2660 @@ +if not modules then modules = { } end modules ['font-otn'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- this is still somewhat preliminary and it will get better in due time; +-- much functionality could only be implemented thanks to the husayni font +-- of Idris Samawi Hamid to who we dedicate this module. + +-- I'm in the process of cleaning up the code (which happens in another +-- file) so don't rely on things staying the same. + +-- some day when we can jit this, we can use more functions + +-- we can use more lpegs when lpeg is extended with function args and so +-- resolving to unicode does not gain much + +-- in retrospect it always looks easy but believe it or not, it took a lot +-- of work to get proper open type support done: buggy fonts, fuzzy specs, +-- special made testfonts, many skype sessions between taco, idris and me, +-- torture tests etc etc ... unfortunately the code does not show how much +-- time it took ... + +-- todo: +-- +-- kerning is probably not yet ok for latin around dics nodes +-- extension infrastructure (for usage out of context) +-- sorting features according to vendors/renderers +-- alternative loop quitters +-- check cursive and r2l +-- find out where ignore-mark-classes went +-- remove unused tables +-- slide tail (always glue at the end so only needed once +-- default features (per language, script) +-- cleanup kern(class) code, remove double info +-- handle positions (we need example fonts) +-- handle gpos_single (we might want an extra width field in glyph nodes because adding kerns might interfere) + +--[[ldx-- +This module is a bit more split up that I'd like but since we also want to test
+with plain
The specification of OpenType is kind of vague. Apart from a lack of a proper +free specifications there's also the problem that Microsoft and Adobe +may have their own interpretation of how and in what order to apply features. +In general the Microsoft website has more detailed specifications and is a +better reference. There is also some information in the FontForge help files.
+ +Because there is so much possible, fonts might contain bugs and/or be made to +work with certain rederers. These may evolve over time which may have the side +effect that suddenly fonts behave differently.
+ +After a lot of experiments (mostly by Taco, me and Idris) we're now at yet another
+implementation. Of course all errors are mine and of course the code can be
+improved. There are quite some optimizations going on here and processing speed
+is currently acceptable. Not all functions are implemented yet, often because I
+lack the fonts for testing. Many scripts are not yet supported either, but I will
+look into them as soon as
Because there are different interpretations possible, I will extend the code +with more (configureable) variants. I can also add hooks for users so that they can +write their own extensions.
+ +Glyphs are indexed not by unicode but in their own way. This is because there is no
+relationship with unicode at all, apart from the fact that a font might cover certain
+ranges of characters. One character can have multiple shapes. However, at the
+
The raw table as it coms from
This module is sparsely documented because it is a moving target. The table format +of the reader changes and we experiment a lot with different methods for supporting +features.
+ +As with the
Incrementing the version number will force a re-cache. We jump the number by one
+when there's a fix in the
We get hits on a mark, but we're not sure if the it has to be applied so +we need to explicitly test for basechar, baselig and basemark entries.
+--ldx]]-- + +function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence) + local markchar = start.char + if marks[markchar] then + local base = start.prev -- [glyph] [start=mark] + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + local basechar = base.char + if marks[basechar] then + while true do + base = base.prev + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + basechar = base.char + if not marks[basechar] then + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) + end + return start, false + end + end + end + local baseanchors = descriptions[basechar] + if baseanchors then + baseanchors = baseanchors.anchors + end + if baseanchors then + local baseanchors = baseanchors['basechar'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + local dx, dy, bound = set_mark(start,base,tfmdata.factor,rlmode,ba,ma) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)", + pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return start, true + end + end + end + if trace_bugs then + logwarning("%s, no matching anchors for mark %s and base %s",pref(kind,lookupname),gref(markchar),gref(basechar)) + end + end + else -- if trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) + fonts.register_message(currentfont,basechar,"no base anchors") + end + elseif trace_bugs then + logwarning("%s: prev node is no char",pref(kind,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) + end + return start, false +end + +function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) + -- check chainpos variant + local markchar = start.char + if marks[markchar] then + local base = start.prev -- [glyph] [optional marks] [start=mark] + local index = 1 + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + local basechar = base.char + if marks[basechar] then + index = index + 1 + while true do + base = base.prev + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + basechar = base.char + if marks[basechar] then + index = index + 1 + else + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) + end + return start, false + end + end + end + local i = has_attribute(start,markdone) + if i then index = i end + local baseanchors = descriptions[basechar] + if baseanchors then + baseanchors = baseanchors.anchors + if baseanchors then + local baseanchors = baseanchors['baselig'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + ba = ba[index] + if ba then + local dx, dy, bound = set_mark(start,base,tfmdata.factor,rlmode,ba,ma,index) + if trace_marks then + logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)", + pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) + end + return start, true + end + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and baselig %s",pref(kind,lookupname),gref(markchar),gref(basechar)) + end + end + end + else -- if trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) + fonts.register_message(currentfont,basechar,"no base anchors") + end + elseif trace_bugs then + logwarning("%s: prev node is no char",pref(kind,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) + end + return start, false +end + +function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) + local markchar = start.char + if marks[markchar] then +--~ local alreadydone = markonce and has_attribute(start,markmark) +--~ if not alreadydone then + local base = start.prev -- [glyph] [basemark] [start=mark] + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then -- subtype test can go + local basechar = base.char + local baseanchors = descriptions[basechar] + if baseanchors then + baseanchors = baseanchors.anchors + if baseanchors then + baseanchors = baseanchors['basemark'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + local dx, dy, bound = set_mark(start,base,tfmdata.factor,rlmode,ba,ma) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", + pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return start,true + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and basemark %s",pref(kind,lookupname),gref(markchar),gref(basechar)) + end + end + end + else -- if trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) + fonts.register_message(currentfont,basechar,"no base anchors") + end + elseif trace_bugs then + logwarning("%s: prev node is no mark",pref(kind,lookupname)) + end +--~ elseif trace_marks and trace_details then +--~ logprocess("%s, mark %s is already bound (n=%s), ignoring mark2mark",pref(kind,lookupname),gref(markchar),alreadydone) +--~ end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) + end + return start,false +end + +function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence) -- to be checked + local alreadydone = cursonce and has_attribute(start,cursbase) + if not alreadydone then + local done = false + local startchar = start.char + if marks[startchar] then + if trace_cursive then + logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) + end + else + local nxt = start.next + while not done and nxt and nxt.id == glyph and nxt.subtype<256 and nxt.font == currentfont do + local nextchar = nxt.char + if marks[nextchar] then + -- should not happen (maybe warning) + nxt = nxt.next + else + local entryanchors = descriptions[nextchar] + if entryanchors then + entryanchors = entryanchors.anchors + if entryanchors then + entryanchors = entryanchors['centry'] + if entryanchors then + local al = anchorlookups[lookupname] + for anchor, entry in next, entryanchors do + if al[anchor] then + local exit = exitanchors[anchor] + if exit then + local dx, dy, bound = set_cursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + if trace_cursive then + logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) + end + done = true + break + end + end + end + end + end + else -- if trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) + fonts.register_message(currentfont,startchar,"no entry anchors") + end + break + end + end + end + return start, done + else + if trace_cursive and trace_details then + logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) + end + return start, false + end +end + +function handlers.gpos_single(start,kind,lookupname,kerns,sequence) + local startchar = start.char + local dx, dy, w, h = set_pair(start,tfmdata.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) + end + return start, false +end + +function handlers.gpos_pair(start,kind,lookupname,kerns,sequence) + -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too + -- todo: kerns in components of ligatures + local snext = start.next + if not snext then + return start, false + else + local prev, done = start, false + local factor = tfmdata.factor + while snext and snext.id == glyph and snext.subtype<256 and snext.font == currentfont do + local nextchar = snext.char +local krn = kerns[nextchar] + if not krn and marks[nextchar] then + prev = snext + snext = snext.next + else + local krn = kerns[nextchar] + if not krn then + -- skip + elseif type(krn) == "table" then + if krn[1] == "pair" then + local a, b = krn[3], krn[4] + if a and #a > 0 then + local startchar = start.char + local x, y, w, h = set_pair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting first of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + if b and #b > 0 then + local startchar = start.char + local x, y, w, h = set_pair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) + if trace_kerns then + logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + else + logs.report("%s: check this out (old kern stuff)",pref(kind,lookupname)) + local a, b = krn[3], krn[7] + if a and a ~= 0 then + local k = set_kern(snext,factor,rlmode,a) + if trace_kerns then + logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) + end + end + if b and b ~= 0 then + logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor) + end + end + done = true + elseif krn ~= 0 then + local k = set_kern(snext,factor,rlmode,krn) + if trace_kerns then + logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) + end + done = true + end + break + end + end + return start, done + end +end + +--[[ldx-- +I will implement multiple chain replacements once I run into a font that uses +it. It's not that complex to handle.
+--ldx]]-- + +local chainmores = { } +local chainprocs = { } + +local function logprocess(...) + if trace_steps then + registermessage(...) + end + logs.report("otf subchain",...) +end +local function logwarning(...) + logs.report("otf subchain",...) +end + +-- ['coverage']={ +-- ['after']={ "r" }, +-- ['before']={ "q" }, +-- ['current']={ "a", "b", "c" }, +-- }, +-- ['lookups']={ "ls_l_1", "ls_l_1", "ls_l_1" }, + +function chainmores.chainsub(start,stop,kind,chainname,currentcontext,cache,lookuplist,chainlookupname,n) + logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) + return start, false +end + +-- handled later: +-- +-- function chainmores.gsub_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) +-- return chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) +-- end + +function chainmores.gsub_multiple(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) + logprocess("%s: gsub_multiple not yet supported",cref(kind,chainname,chainlookupname)) + return start, false +end +function chainmores.gsub_alternate(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) + logprocess("%s: gsub_alternate not yet supported",cref(kind,chainname,chainlookupname)) + return start, false +end + +-- handled later: +-- +-- function chainmores.gsub_ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) +-- return chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) +-- end + +local function logprocess(...) + if trace_steps then + registermessage(...) + end + logs.report("otf chain",...) +end +local function logwarning(...) + logs.report("otf chain",...) +end + +-- We could share functions but that would lead to extra function calls with many +-- arguments, redundant tests and confusing messages. + +function chainprocs.chainsub(start,stop,kind,chainname,currentcontext,cache,lookuplist,chainlookupname) + logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) + return start, false +end + +-- The reversesub is a special case, which is why we need to store the replacements +-- in a bit weird way. There is no lookup and the replacement comes from the lookup +-- itself. It is meant mostly for dealing with Urdu. + +function chainprocs.reversesub(start,stop,kind,chainname,currentcontext,cache,replacements) + local char = start.char + local replacement = replacements[char] + if replacement then + if trace_singles then + logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement)) + end + start.char = replacement + return start, true + else + return start, false + end +end + +--[[ldx-- +This chain stuff is somewhat tricky since we can have a sequence of actions to be +applied: single, alternate, multiple or ligature where ligature can be an invalid +one in the sense that it will replace multiple by one but not neccessary one that +looks like the combination (i.e. it is the counterpart of multiple then). For +example, the following is valid:
+ +Therefore we we don't really do the replacement here already unless we have the +single lookup case. The efficiency of the replacements can be improved by deleting +as less as needed but that would also mke the code even more messy.
+--ldx]]-- + +local function delete_till_stop(start,stop,ignoremarks) + if start ~= stop then + -- todo keep marks + local done = false + while not done do + done = start == stop + delete_node(start,start.next) + end + end +end + +--[[ldx-- +Here we replace start by a single variant, First we delete the rest of the +match.
+--ldx]]-- + +function chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex) + -- todo: marks ? + if not chainindex then + delete_till_stop(start,stop) -- ,currentlookup.flags[1]) + end + local current = start + local subtables = currentlookup.subtables + while current do + if current.id == glyph then + local currentchar = current.char + local lookupname = subtables[1] + local replacement = cache.gsub_single[lookupname] + if not replacement then + if trace_bugs then + logwarning("%s: no single hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) + end + else + replacement = replacement[currentchar] + if not replacement then + if trace_bugs then + logwarning("%s: no single for %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar)) + end + else + if trace_singles then + logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement)) + end + current.char = replacement + end + end + return start, true + elseif current == stop then + break + else + current = current.next + end + end + return start, false +end + +chainmores.gsub_single = chainprocs.gsub_single + +--[[ldx-- +Here we replace start by a sequence of new glyphs. First we delete the rest of +the match.
+--ldx]]-- + +function chainprocs.gsub_multiple(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) + delete_till_stop(start,stop) + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local replacements = cache.gsub_multiple[lookupname] + if not replacements then + if trace_bugs then + logwarning("%s: no multiple hits",cref(kind,chainname,chainlookupname,lookupname)) + end + else + replacements = replacements[startchar] + if not replacements then + if trace_bugs then + logwarning("%s: no multiple for %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar)) + end + else + if trace_multiples then + logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements)) + end + local sn = start.next + for k=1,#replacements do + if k == 1 then + start.char = replacements[k] + else + local n = copy_node(start) -- maybe delete the components and such + n.char = replacements[k] + n.next, n.prev = sn, start + if sn then + sn.prev = n + end + start.next, start = n, n + end + end + return start, true + end + end + return start, false +end + +--[[ldx-- +Here we replace start by new glyph. First we delete the rest of the match.
+--ldx]]-- + +function chainprocs.gsub_alternate(start,stop,kind,lookupname,currentcontext,cache,currentlookup) + -- todo: marks ? + delete_till_stop(start,stop) + local current = start + local subtables = currentlookup.subtables + while current do + if current.id == glyph then + local currentchar = current.char + local lookupname = subtables[1] + local alternatives = cache.gsub_alternate[lookupname] + if not alternatives then + if trace_bugs then + logwarning("%s: no alternative hits",cref(kind,chainname,chainlookupname,lookupname)) + end + else + alternatives = alternatives[currentchar] + if not alternatives then + if trace_bugs then + logwarning("%s: no alternative for %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar)) + end + else + local choice, index = alternative_glyph(current,alternatives,kind,chainname,chainlookupname,lookupname) + current.char = choice + if trace_alternatives then + logprocess("%s: replacing single %s by alternative %s (%s)",cref(kind,chainname,chainlookupname,lookupname),index,gref(currentchar),gref(choice),index) + end + end + end + return start, true + elseif current == stop then + break + else + current = current.next + end + end + return start, false +end + +--[[ldx-- +When we replace ligatures we use a helper that handles the marks. I might change +this function (move code inline and handle the marks by a separate function). We +assume rather stupid ligatures (no complex disc nodes).
+--ldx]]-- + +function chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex) + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local ligatures = cache.gsub_ligature[lookupname] + if not ligatures then + if trace_bugs then + logwarning("%s: no ligature hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) + end + else + ligatures = ligatures[startchar] + if not ligatures then + if trace_bugs then + logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) + end + else + local s, discfound, last, nofreplacements = start.next, false, stop, 0 + while s do + local id = s.id + if id == disc then + s = s.next + discfound = true + else + local schar = s.char + if marks[schar] then -- marks + s = s.next + else + local lg = ligatures[1][schar] + if not lg then + break + else + ligatures, last, nofreplacements = lg, s, nofreplacements + 1 + if s == stop then + break + else + s = s.next + end + end + end + end + end + local l2 = ligatures[2] + if l2 then + if chainindex then + stop = last + end + if trace_ligatures then + if start == stop then + logprocess("%s: replacing character %s by ligature %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2)) + else + logprocess("%s: replacing character %s upto %s by ligature %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2)) + end + end + start = toligature(kind,lookup,start,stop,l2,currentlookup.flags[1],discfound) + return start, true, nofreplacements + elseif trace_bugs then + if start == stop then + logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) + else + logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char)) + end + end + end + end + return start, false, 0 +end + +chainmores.gsub_ligature = chainprocs.gsub_ligature + +function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) + local markchar = start.char + if marks[markchar] then + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local markanchors = cache.gpos_mark2base[lookupname] + if markanchors then + markanchors = markanchors[markchar] + end + if markanchors then + local base = start.prev -- [glyph] [start=mark] + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + local basechar = base.char + if marks[basechar] then + while true do + base = base.prev + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + basechar = base.char + if not marks[basechar] then + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) + end + return start, false + end + end + end + local baseanchors = descriptions[basechar].anchors + if baseanchors then + local baseanchors = baseanchors['basechar'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + local dx, dy, bound = set_mark(start,base,tfmdata.factor,rlmode,ba,ma) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)", + cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return start, true + end + end + end + if trace_bugs then + logwarning("%s, no matching anchors for mark %s and base %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + logwarning("%s: prev node is no char",cref(kind,chainname,chainlookupname,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) + end + return start, false +end + +function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) + local markchar = start.char + if marks[markchar] then + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local markanchors = cache.gpos_mark2ligature[lookupname] + if markanchors then + markanchors = markanchors[markchar] + end + if markanchors then + local base = start.prev -- [glyph] [optional marks] [start=mark] + local index = 1 + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + local basechar = base.char + if marks[basechar] then + index = index + 1 + while true do + base = base.prev + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + basechar = base.char + if marks[basechar] then + index = index + 1 + else + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",cref(kind,chainname,chainlookupname,lookupname),markchar) + end + return start, false + end + end + end + -- todo: like marks a ligatures hash + local i = has_attribute(start,markdone) + if i then index = i end + local baseanchors = descriptions[basechar].anchors + if baseanchors then + local baseanchors = baseanchors['baselig'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + ba = ba[index] + if ba then + local dx, dy, bound = set_mark(start,base,tfmdata.factor,rlmode,ba,ma,index) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)", + cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) + end + return start, true + end + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and baselig %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + logwarning("feature %s, lookup %s: prev node is no char",kind,lookupname) + end + elseif trace_bugs then + logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) + end + return start, false +end + +function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) + local markchar = start.char + if marks[markchar] then +--~ local alreadydone = markonce and has_attribute(start,markmark) +--~ if not alreadydone then + -- local markanchors = descriptions[markchar].anchors markanchors = markanchors and markanchors.mark + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local markanchors = cache.gpos_mark2mark[lookupname] + if markanchors then + markanchors = markanchors[markchar] + end + if markanchors then + local base = start.prev -- [glyph] [basemark] [start=mark] + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then -- subtype test can go + local basechar = base.char + local baseanchors = descriptions[basechar].anchors + if baseanchors then + baseanchors = baseanchors['basemark'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + local dx, dy, bound = set_mark(start,base,tfmdata.factor,rlmode,ba,ma) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", + cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return start, true + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and basemark %s",gref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + logwarning("%s: prev node is no mark",cref(kind,chainname,chainlookupname,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) + end +--~ elseif trace_marks and trace_details then +--~ logprocess("%s, mark %s is already bound (n=%s), ignoring mark2mark",pref(kind,lookupname),gref(markchar),alreadydone) +--~ end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) + end + return start, false +end + +-- ! ! ! untested ! ! ! + +function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) + local alreadydone = cursonce and has_attribute(start,cursbase) + if not alreadydone then + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local exitanchors = cache.gpos_cursive[lookupname] + if exitanchors then + exitanchors = exitanchors[startchar] + end + if exitanchors then + local done = false + if marks[startchar] then + if trace_cursive then + logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) + end + else + local nxt = start.next + while not done and nxt and nxt.id == glyph and nxt.subtype<256 and nxt.font == currentfont do + local nextchar = nxt.char + if marks[nextchar] then + -- should not happen (maybe warning) + nxt = nxt.next + else + local entryanchors = descriptions[nextchar] + if entryanchors then + entryanchors = entryanchors.anchors + if entryanchors then + entryanchors = entryanchors['centry'] + if entryanchors then + local al = anchorlookups[lookupname] + for anchor, entry in next, entryanchors do + if al[anchor] then + local exit = exitanchors[anchor] + if exit then + local dx, dy, bound = set_cursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + if trace_cursive then + logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) + end + done = true + break + end + end + end + end + end + else -- if trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) + fonts.register_message(currentfont,startchar,"no entry anchors") + end + break + end + end + end + return start, done + else + if trace_cursive and trace_details then + logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) + end + return start, false + end + end + return start, false +end + +function chainprocs.gpos_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex,sequence) + -- untested + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local kerns = cache.gpos_single[lookupname] + if kerns then + kerns = kerns[startchar] + if kerns then + local dx, dy, w, h = set_pair(start,tfmdata.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) + end + end + end + return start, false +end + +-- when machines become faster i will make a shared function + +function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex,sequence) +-- logwarning("%s: gpos_pair not yet supported",cref(kind,chainname,chainlookupname)) + local snext = start.next + if snext then + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local kerns = cache.gpos_pair[lookupname] + if kerns then + kerns = kerns[startchar] + if kerns then + local prev, done = start, false + local factor = tfmdata.factor + while snext and snext.id == glyph and snext.subtype<256 and snext.font == currentfont do + local nextchar = snext.char + local krn = kerns[nextchar] + if not krn and marks[nextchar] then + prev = snext + snext = snext.next + else + if not krn then + -- skip + elseif type(krn) == "table" then + if krn[1] == "pair" then + local a, b = krn[3], krn[4] + if a and #a > 0 then + local startchar = start.char + local x, y, w, h = set_pair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting first of pair %s and %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + if b and #b > 0 then + local startchar = start.char + local x, y, w, h = set_pair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) + if trace_kerns then + logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + else + logs.report("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) + local a, b = krn[3], krn[7] + if a and a ~= 0 then + local k = set_kern(snext,factor,rlmode,a) + if trace_kerns then + logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) + end + end + if b and b ~= 0 then + logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor) + end + end + done = true + elseif krn ~= 0 then + local k = set_kern(snext,factor,rlmode,krn) + if trace_kerns then + logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) + end + done = true + end + break + end + end + return start, done + end + end + end + return start, false +end + +-- what pointer to return, spec says stop +-- to be discussed ... is bidi changer a space? +-- elseif char == zwnj and sequence[n][32] then -- brrr + +-- somehow l or f is global +-- we don't need to pass the currentcontext, saves a bit +-- make a slow variant then can be activated but with more tracing + +local function show_skip(kind,chainname,char,ck,class) + if ck[9] then + logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s (%s=>%s)",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) + else + logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s",cref(kind,chainname),gref(char),class,ck[1],ck[2]) + end +end + +local function normal_handle_contextchain(start,kind,chainname,contexts,sequence,cache) + -- local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6] + local flags, done = sequence.flags, false + local skipmark, skipligature, skipbase = flags[1], flags[2], flags[3] + local someskip = skipmark or skipligature or skipbase -- could be stored in flags for a fast test (hm, flags could be false !) + local markclass = sequence.markclass -- todo, first we need a proper test + local skipped = false + for k=1,#contexts do + local match, current, last = true, start, start + local ck = contexts[k] + local seq = ck[3] + local s = #seq + -- f..l = mid string + if s == 1 then + -- never happens + match = current.id == glyph and current.subtype<256 and current.font == currentfont and seq[1][current.char] + else + -- todo: better space check (maybe check for glue) + local f, l = ck[4], ck[5] + if f == l then + -- already a hit + match = true + else + -- no need to test first hit (to be optimized) + local n = f + 1 + last = last.next + -- we cannot optimize for n=2 because there can be disc nodes + -- if not someskip and n == l then + -- -- n=2 and no skips then faster loop + -- match = last and last.id == glyph and last.subtype<256 and last.font == currentfont and seq[n][last.char] + -- else + while n <= l do + if last then + local id = last.id + if id == glyph then + if last.subtype<256 and last.font == currentfont then + local char = last.char + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then + skipped = true + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end + last = last.next + elseif seq[n][char] then + if n < l then + last = last.next + end + n = n + 1 + else + match = false break + end + else + match = false break + end + else + match = false break + end + elseif id == disc then -- what to do with kerns? + last = last.next + else + match = false break + end + else + match = false break + end + end + -- end + end + if match and f > 1 then + -- before + local prev = start.prev + if prev then + local n = f-1 + while n >= 1 do + if prev then + local id = prev.id + if id == glyph then + if prev.subtype<256 and prev.font == currentfont then -- normal char + local char = prev.char + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then + skipped = true + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end + elseif seq[n][char] then + n = n -1 + else + match = false break + end + else + match = false break + end + else + match = false break + end + elseif id == disc then + -- skip 'm + elseif seq[n][32] then + n = n -1 + else + match = false break + end + prev = prev.prev + elseif seq[n][32] then + n = n -1 + else + match = false break + end + end + elseif f == 2 then + match = seq[1][32] + else + for n=f-1,1 do + if not seq[n][32] then + match = false break + end + end + end + end + if match and s > l then + -- after + local current = last.next + if current then + -- removed optimization for s-l == 1, we have to deal with marks anyway + local n = l + 1 + while n <= s do + if current then + local id = current.id + if id == glyph then + if current.subtype<256 and current.font == currentfont then -- normal char + local char = current.char + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then + skipped = true + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end + elseif seq[n][char] then + n = n + 1 + else + match = false break + end + else + match = false break + end + else + match = false break + end + elseif id == disc then + -- skip 'm + elseif seq[n][32] then -- brrr + n = n + 1 + else + match = false break + end + current = current.next + elseif seq[n][32] then + n = n + 1 + else + match = false break + end + end + elseif s-l == 1 then + match = seq[s][32] + else + for n=l+1,s do + if not seq[n][32] then + match = false break + end + end + end + end + end + if match then + -- ck == currentcontext + if trace_contexts then + local rule, lookuptype, f, l = ck[1], ck[2], ck[4], ck[5] + local char = start.char + if ck[9] then + logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %s (%s=>%s)",cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10]) + else + logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %s",cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype) + end + end + local chainlookups = ck[6] + if chainlookups then + local nofchainlookups = #chainlookups + -- we can speed this up if needed + if nofchainlookups == 1 then + local chainlookupname = chainlookups[1] + local chainlookup = lookuptable[chainlookupname] + local cp = chainprocs[chainlookup.type] + if cp then + start, done = cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,nil,sequence) + else + logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) + end + else + -- actually this needs a more complex treatment for which we will use chainmores +--~ local i = 1 +--~ repeat +--~ local chainlookupname = chainlookups[i] +--~ local chainlookup = lookuptable[chainlookupname] +--~ local cp = chainmores[chainlookup.type] +--~ if cp then +--~ local ok, n +--~ start, ok, n = cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,i,sequence) +--~ -- messy since last can be changed ! +--~ if ok then +--~ done = true +--~ start = start.next +--~ if n then +--~ -- skip next one(s) if ligature +--~ i = i + n - 1 +--~ end +--~ end +--~ else +--~ logprocess("%s: multiple subchains for %s are not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) +--~ end +--~ i = i + 1 +--~ until i > nofchainlookups + + local i = 1 + repeat +if skipped then + while true do + local char = start.char + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then + start = start.next + else + break + end + else + break + end + end +end + local chainlookupname = chainlookups[i] + local chainlookup = lookuptable[chainlookupname] + local cp = chainmores[chainlookup.type] + if cp then + local ok, n + start, ok, n = cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,i,sequence) + -- messy since last can be changed ! + if ok then + done = true + -- skip next one(s) if ligature + i = i + (n or 1) + else + i = i + 1 + end + else + logprocess("%s: multiple subchains for %s are not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) + i = i + 1 + end + start = start.next + until i > nofchainlookups + + end + else + local replacements = ck[7] + if replacements then + start, done = chainprocs.reversesub(start,last,kind,chainname,ck,cache,replacements) -- sequence + else + done = true -- can be meant to be skipped + if trace_contexts then + logprocess("%s: skipping match",cref(kind,chainname)) + end + end + end + end + end + return start, done +end + +-- Because we want to keep this elsewhere (an because speed is less an issue) we +-- pass the font id so that the verbose variant can access the relevant helper tables. + +local verbose_handle_contextchain = function(font,...) + logwarning("no verbose handler installed, reverting to 'normal'") + otf.setcontextchain() + return normal_handle_contextchain(...) +end + +otf.chainhandlers = { + normal = normal_handle_contextchain, + verbose = verbose_handle_contextchain, +} + +function otf.setcontextchain(method) + if not method or method == "normal" or not otf.chainhandlers[method] then + if handlers.contextchain then -- no need for a message while making the format + logwarning("installing normal contextchain handler") + end + handlers.contextchain = normal_handle_contextchain + else + logwarning("installing contextchain handler '%s'",method) + local handler = otf.chainhandlers[method] + handlers.contextchain = function(...) + return handler(currentfont,...) -- hm, get rid of ... + end + end + handlers.gsub_context = handlers.contextchain + handlers.gsub_contextchain = handlers.contextchain + handlers.gsub_reversecontextchain = handlers.contextchain + handlers.gpos_contextchain = handlers.contextchain + handlers.gpos_context = handlers.contextchain +end + +otf.setcontextchain() + +local missing = { } -- we only report once + +local function logprocess(...) + if trace_steps then + registermessage(...) + end + logs.report("otf process",...) +end +local function logwarning(...) + logs.report("otf process",...) +end + +local function report_missing_cache(typ,lookup) + local f = missing[currentfont] if not f then f = { } missing[currentfont] = f end + local t = f[typ] if not t then t = { } f[typ] = t end + if not t[lookup] then + t[lookup] = true + logwarning("missing cache for lookup %s of type %s in font %s (%s)",lookup,typ,currentfont,tfmdata.fullname) + end +end + +local resolved = { } -- we only resolve a font,script,language pair once + +-- todo: pass all these 'locals' in a table + +function fonts.methods.node.otf.features(head,font,attr) + if trace_steps then + checkstep(head) + end + tfmdata = fontdata[font] + local shared = tfmdata.shared + otfdata = shared.otfdata + local luatex = otfdata.luatex + descriptions = tfmdata.descriptions + characters = tfmdata.characters + indices = tfmdata.indices + unicodes = tfmdata.unicodes + marks = tfmdata.marks + anchorlookups = luatex.lookup_to_anchor + currentfont = font + rlmode = 0 + local featuredata = otfdata.shared.featuredata -- can be made local to closure + local sequences = luatex.sequences + lookuptable = luatex.lookups + local done = false + local script, language, s_enabled, a_enabled, dyn + local attribute_driven = attr and attr ~= 0 + if attribute_driven then + local features = context_setups[context_numbers[attr]] -- could be a direct list + dyn = context_merged[attr] or 0 + language, script = features.language or "dflt", features.script or "dflt" + a_enabled = features -- shared.features -- can be made local to the resolver + if dyn == 2 or dyn == -2 then + -- font based + s_enabled = shared.features + end + else + language, script = tfmdata.language or "dflt", tfmdata.script or "dflt" + s_enabled = shared.features -- can be made local to the resolver + dyn = 0 + end + -- we can save some runtime by caching feature tests + local res = resolved[font] if not res then res = { } resolved[font] = res end + local rs = res [script] if not rs then rs = { } res [script] = rs end + local rl = rs [language] if not rl then rl = { } rs [language] = rl end + local ra = rl [attr] if ra == nil then ra = { } rl [attr] = ra end -- attr can be false + -- sequences always > 1 so no need for optimization + for s=1,#sequences do + local pardir, txtdir = 0, { } + local success = false + local sequence = sequences[s] + local r = ra[s] -- cache + if r == nil then + -- + -- this bit will move to font-ctx and become a function + --- + local chain = sequence.chain or 0 + local features = sequence.features + if not features then + -- indirect lookup, part of chain (todo: make this a separate table) + r = false -- { false, false, chain } + else + local valid, attribute, kind, what = false, false + for k,v in next, features do + -- we can quit earlier but for the moment we want the tracing + local s_e = s_enabled and s_enabled[k] + local a_e = a_enabled and a_enabled[k] + if s_e or a_e then + local l = v[script] or v[wildcard] + if l then + -- not l[language] or l[default] or l[wildcard] because we want tracing + -- only first attribute match check, so we assume simple fina's + -- default can become a font feature itself + if l[language] then + valid, what = s_e or a_e, language + -- elseif l[default] then + -- valid, what = true, default + elseif l[wildcard] then + valid, what = s_e or a_e, wildcard + end + if valid then + kind, attribute = k, special_attributes[k] or false + if a_e and dyn < 0 then + valid = false + end + if trace_applied then + local typ, action = match(sequence.type,"(.*)_(.*)") + logs.report("otf node mode", + "%s font: %03i, dynamic: %03i, kind: %s, lookup: %3i, script: %-4s, language: %-4s (%-4s), type: %s, action: %s, name: %s", + (valid and "+") or "-",font,attr or 0,kind,s,script,language,what,typ,action,sequence.name) + end + break + end + end + end + end + if valid then + r = { valid, attribute, chain, kind } + else + r = false -- { valid, attribute, chain, "generic" } -- false anyway, could be flag instead of table + end + end + ra[s] = r + end + featurevalue = r and r[1] -- todo: pass to function instead of using a global + if featurevalue then + local attribute, chain, typ, subtables = r[2], r[3], sequence.type, sequence.subtables + if chain < 0 then + -- this is a limited case, no special treatments like 'init' etc + local handler = handlers[typ] + local thecache = featuredata[typ] or { } + -- we need to get rid of this slide ! + local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo + while start do + local id = start.id + if id == glyph then + if start.subtype<256 and start.font == font and (not attr or has_attribute(start,0,attr)) then +--~ if start.subtype<256 and start.font == font and has_attribute(start,0,attr) then + for i=1,#subtables do + local lookupname = subtables[i] + local lookupcache = thecache[lookupname] + if lookupcache then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + start, success = handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,i) + if success then + break + end + end + else + report_missing_cache(typ,lookupname) + end + end + if start then start = start.prev end + else + start = start.prev + end + else + start = start.prev + end + end + else + local handler = handlers[typ] + local ns = #subtables + local thecache = featuredata[typ] or { } + local start = head -- local ? + rlmode = 0 -- to be checked ? + if ns == 1 then + local lookupname = subtables[1] + local lookupcache = thecache[lookupname] + if not lookupcache then + report_missing_cache(typ,lookupname) + else + while start do + local id = start.id + if id == glyph then +--~ if start.font == font and start.subtype<256 and has_attribute(start,0,attr) and (not attribute or has_attribute(start,state,attribute)) then + if start.font == font and start.subtype<256 and (not attr or has_attribute(start,0,attr)) and (not attribute or has_attribute(start,state,attribute)) then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + -- sequence kan weg + local ok + start, ok = handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,1) + if ok then + success = true + end + end + if start then start = start.next end + else + start = start.next + end + -- elseif id == glue then + -- if p[5] then -- chain + -- local pc = pp[32] + -- if pc then + -- start, ok = start, false -- p[1](start,kind,p[2],pc,p[3],p[4]) + -- if ok then + -- done = true + -- end + -- if start then start = start.next end + -- else + -- start = start.next + -- end + -- else + -- start = start.next + -- end + elseif id == whatsit then + local subtype = start.subtype + if subtype == 7 then + local dir = start.dir + if dir == "+TRT" or dir == "+TLT" then + insert(txtdir,dir) + elseif dir == "-TRT" or dir == "-TLT" then + remove(txtdir) + end + local d = txtdir[#txtdir] + if d == "+TRT" then + rlmode = -1 + elseif d == "+TLT" then + rlmode = 1 + else + rlmode = pardir + end + if trace_directions then + logs.report("fonts","directions after textdir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) + end + elseif subtype == 6 then + local dir = start.dir + if dir == "TRT" then + pardir = -1 + elseif dir == "TLT" then + pardir = 1 + else + pardir = 0 + end + rlmode = pardir + --~ txtdir = { } + if trace_directions then + logs.report("fonts","directions after pardir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) + end + end + start = start.next + else + start = start.next + end + end + end + else + while start do + local id = start.id + if id == glyph then + if start.subtype<256 and start.font == font and (not attr or has_attribute(start,0,attr)) and (not attribute or has_attribute(start,state,attribute)) then +--~ if start.subtype<256 and start.font == font and has_attribute(start,0,attr) and (not attribute or has_attribute(start,state,attribute)) then + for i=1,ns do + local lookupname = subtables[i] + local lookupcache = thecache[lookupname] + if lookupcache then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + -- we could move all code inline but that makes things even more unreadable + local ok + start, ok = handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,i) + if ok then + success = true + break + end + end + else + report_missing_cache(typ,lookupname) + end + end + if start then start = start.next end + else + start = start.next + end + -- elseif id == glue then + -- if p[5] then -- chain + -- local pc = pp[32] + -- if pc then + -- start, ok = start, false -- p[1](start,kind,p[2],pc,p[3],p[4]) + -- if ok then + -- done = true + -- end + -- if start then start = start.next end + -- else + -- start = start.next + -- end + -- else + -- start = start.next + -- end + elseif id == whatsit then + local subtype = start.subtype + local subtype = start.subtype + if subtype == 7 then + local dir = start.dir + if dir == "+TRT" or dir == "+TLT" then + insert(txtdir,dir) + elseif dir == "-TRT" or dir == "-TLT" then + remove(txtdir) + end + local d = txtdir[#txtdir] + if d == "+TRT" then + rlmode = -1 + elseif d == "+TLT" then + rlmode = 1 + else + rlmode = pardir + end + if trace_directions then + logs.report("fonts","directions after textdir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) + end + elseif subtype == 6 then + local dir = start.dir + if dir == "TRT" then + pardir = -1 + elseif dir == "TLT" then + pardir = 1 + else + pardir = 0 + end + rlmode = pardir + --~ txtdir = { } + if trace_directions then + logs.report("fonts","directions after pardir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) + end + end + start = start.next + else + start = start.next + end + end + end + end + if success then + done = true + end + if trace_steps then -- ? + registerstep(head) + end + end + end + return head, done +end + +otf.features.prepare = { } + +-- we used to share code in the following functions but that costs a lot of +-- memory due to extensive calls to functions (easily hundreds of thousands per +-- document) + +local function split(replacement,original,cache,unicodes) + -- we can cache this too, but not the same (although unicode is a unique enough hash) + local o, t, n = { }, { }, 0 + for s in gmatch(original,"[^ ]+") do + local us = unicodes[s] + if type(us) == "number" then -- tonumber(us) + o[#o+1] = us + else + o[#o+1] = us[1] + end + end + for s in gmatch(replacement,"[^ ]+") do + n = n + 1 + local us = unicodes[s] + if type(us) == "number" then -- tonumber(us) + t[o[n]] = us + else + t[o[n]] = us[1] + end + end + return t +end + +local function uncover(covers,result,cache,unicodes) + -- lpeg hardly faster (.005 sec on mk) + for n=1,#covers do + local c = covers[n] + local cc = cache[c] + if not cc then + local t = { } + for s in gmatch(c,"[^ ]+") do + local us = unicodes[s] + if type(us) == "number" then + t[us] = true + else + for i=1,#us do + t[us[i]] = true + end + end + end + cache[c] = t + result[#result+1] = t + else + result[#result+1] = cc + end + end +end + +local function prepare_lookups(tfmdata) + local otfdata = tfmdata.shared.otfdata + local featuredata = otfdata.shared.featuredata + local anchor_to_lookup = otfdata.luatex.anchor_to_lookup + local lookup_to_anchor = otfdata.luatex.lookup_to_anchor + -- + local multiple = featuredata.gsub_multiple + local alternate = featuredata.gsub_alternate + local single = featuredata.gsub_single + local ligature = featuredata.gsub_ligature + local pair = featuredata.gpos_pair + local position = featuredata.gpos_single + local kerns = featuredata.gpos_pair + local mark = featuredata.gpos_mark2mark + local cursive = featuredata.gpos_cursive + -- + local unicodes = tfmdata.unicodes -- names to unicodes + local indices = tfmdata.indices + local descriptions = tfmdata.descriptions + -- + -- we can change the otf table after loading but then we need to adapt base mode + -- as well (no big deal) + -- + local action = { + substitution = function(p,lookup,k,glyph,unicode) + local old, new = unicode, unicodes[p[2]] + if type(new) == "table" then + new = new[1] + end + local s = single[lookup] + if not s then s = { } single[lookup] = s end + s[old] = new + --~ if trace_lookups then + --~ logs.report("define otf","lookup %s: substitution %s => %s",lookup,old,new) + --~ end + end, + multiple = function (p,lookup,k,glyph,unicode) + local old, new = unicode, { } + local m = multiple[lookup] + if not m then m = { } multiple[lookup] = m end + m[old] = new + for pc in gmatch(p[2],"[^ ]+") do + local upc = unicodes[pc] + if type(upc) == "number" then + new[#new+1] = upc + else + new[#new+1] = upc[1] + end + end + --~ if trace_lookups then + --~ logs.report("define otf","lookup %s: multiple %s => %s",lookup,old,concat(new," ")) + --~ end + end, + alternate = function(p,lookup,k,glyph,unicode) + local old, new = unicode, { } + local a = alternate[lookup] + if not a then a = { } alternate[lookup] = a end + a[old] = new + for pc in gmatch(p[2],"[^ ]+") do + local upc = unicodes[pc] + if type(upc) == "number" then + new[#new+1] = upc + else + new[#new+1] = upc[1] + end + end + --~ if trace_lookups then + --~ logs.report("define otf","lookup %s: alternate %s => %s",lookup,old,concat(new,"|")) + --~ end + end, + ligature = function (p,lookup,k,glyph,unicode) + --~ if trace_lookups then + --~ logs.report("define otf","lookup %s: ligature %s => %s",lookup,p[2],glyph.name) + --~ end + local first = true + local t = ligature[lookup] + if not t then t = { } ligature[lookup] = t end + for s in gmatch(p[2],"[^ ]+") do + if first then + local u = unicodes[s] + if not u then + logs.report("define otf","lookup %s: ligature %s => %s ignored due to invalid unicode",lookup,p[2],glyph.name) + break + elseif type(u) == "number" then + if not t[u] then + t[u] = { { } } + end + t = t[u] + else + local tt = t + local tu + for i=1,#u do + local u = u[i] + if i==1 then + if not t[u] then + t[u] = { { } } + end + tu = t[u] + t = tu + else + if not t[u] then + tt[u] = tu + end + end + end + end + first = false + else + s = unicodes[s] + local t1 = t[1] + if not t1[s] then + t1[s] = { { } } + end + t = t1[s] + end + end + t[2] = unicode + end, + position = function(p,lookup,k,glyph,unicode) + -- not used + local s = position[lookup] + if not s then s = { } position[lookup] = s end + s[unicode] = p[2] -- direct pointer to kern spec + end, + pair = function(p,lookup,k,glyph,unicode) + local s = pair[lookup] + if not s then s = { } pair[lookup] = s end + local others = s[unicode] + if not others then others = { } s[unicode] = others end + -- todo: fast check for space + local two = p[2] + local upc = unicodes[two] + if not upc then + for pc in gmatch(two,"[^ ]+") do + local upc = unicodes[pc] + if type(upc) == "number" then + others[upc] = p -- direct pointer to main table + else + for i=1,#upc do + others[upc[i]] = p -- direct pointer to main table + end + end + end + elseif type(upc) == "number" then + others[upc] = p -- direct pointer to main table + else + for i=1,#upc do + others[upc[i]] = p -- direct pointer to main table + end + end + --~ if trace_lookups then + --~ logs.report("define otf","lookup %s: pair for U+%04X",lookup,unicode) + --~ end + end, + } + -- + for unicode, glyph in next, descriptions do + local lookups = glyph.slookups + if lookups then + for lookup, p in next, lookups do + action[p[1]](p,lookup,k,glyph,unicode) + end + end + local lookups = glyph.mlookups + if lookups then + for lookup, whatever in next, lookups do + for i=1,#whatever do -- normaly one + local p = whatever[i] + action[p[1]](p,lookup,k,glyph,unicode) + end + end + end + local list = glyph.mykerns + if list then + for lookup, krn in next, list do + local k = kerns[lookup] + if not k then k = { } kerns[lookup] = k end + k[unicode] = krn -- ref to glyph, saves lookup + --~ if trace_lookups then + --~ logs.report("define otf","lookup %s: kern for U+%04X",lookup,unicode) + --~ end + end + end + local oanchor = glyph.anchors + if oanchor then + for typ, anchors in next, oanchor do -- types + if typ == "mark" then + for name, anchor in next, anchors do + local lookups = anchor_to_lookup[name] + if lookups then + for lookup, _ in next, lookups do + local f = mark[lookup] + if not f then f = { } mark[lookup] = f end + f[unicode] = anchors -- ref to glyph, saves lookup + --~ if trace_lookups then + --~ logs.report("define otf","lookup %s: mark anchor %s for U+%04X",lookup,name,unicode) + --~ end + end + end + end + elseif typ == "cexit" then -- or entry? + for name, anchor in next, anchors do + local lookups = anchor_to_lookup[name] + if lookups then + for lookup, _ in next, lookups do + local f = cursive[lookup] + if not f then f = { } cursive[lookup] = f end + f[unicode] = anchors -- ref to glyph, saves lookup + --~ if trace_lookups then + --~ logs.report("define otf","lookup %s: exit anchor %s for U+%04X",lookup,name,unicode) + --~ end + end + end + end + end + end + end + end +end + +-- local cache = { } +luatex = luatex or {} -- this has to change ... we need a better one + +function prepare_contextchains(tfmdata) + local otfdata = tfmdata.shared.otfdata + local lookups = otfdata.lookups + if lookups then + local featuredata = otfdata.shared.featuredata + local contextchain = featuredata.gsub_contextchain -- shared with gpos + local reversecontextchain = featuredata.gsub_reversecontextchain -- shared with gpos + local characters = tfmdata.characters + local unicodes = tfmdata.unicodes + local indices = tfmdata.indices + local cache = luatex.covers + if not cache then + cache = { } + luatex.covers = cache + end + -- + for lookupname, lookupdata in next, otfdata.lookups do + local lookuptype = lookupdata.type + if not lookuptype then + logs.report("otf process","missing lookuptype for %s",lookupname) + else + local rules = lookupdata.rules + if rules then + local fmt = lookupdata.format + -- contextchain[lookupname][unicode] + if fmt == "coverage" then + if lookuptype ~= "chainsub" and lookuptype ~= "chainpos" then + logs.report("otf process","unsupported coverage %s for %s",lookuptype,lookupname) + else + local contexts = contextchain[lookupname] + if not contexts then + contexts = { } + contextchain[lookupname] = contexts + end + local t = { } + for nofrules=1,#rules do -- does #rules>1 happen often? + local rule = rules[nofrules] + local coverage = rule.coverage + if coverage and coverage.current then + local current, before, after, sequence = coverage.current, coverage.before, coverage.after, { } + if before then + uncover(before,sequence,cache,unicodes) + end + local start = #sequence + 1 + uncover(current,sequence,cache,unicodes) + local stop = #sequence + if after then + uncover(after,sequence,cache,unicodes) + end + if sequence[1] then + t[#t+1] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } + for unic, _ in next, sequence[start] do + local cu = contexts[unic] + if not cu then + contexts[unic] = t + end + end + end + end + end + end + elseif fmt == "reversecoverage" then + if lookuptype ~= "reversesub" then + logs.report("otf process","unsupported reverse coverage %s for %s",lookuptype,lookupname) + else + local contexts = reversecontextchain[lookupname] + if not contexts then + contexts = { } + reversecontextchain[lookupname] = contexts + end + local t = { } + for nofrules=1,#rules do + local rule = rules[nofrules] + local reversecoverage = rule.reversecoverage + if reversecoverage and reversecoverage.current then + local current, before, after, replacements, sequence = reversecoverage.current, reversecoverage.before, reversecoverage.after, reversecoverage.replacements, { } + if before then + uncover(before,sequence,cache,unicodes) + end + local start = #sequence + 1 + uncover(current,sequence,cache,unicodes) + local stop = #sequence + if after then + uncover(after,sequence,cache,unicodes) + end + if replacements then + replacements = split(replacements,current[1],cache,unicodes) + end + if sequence[1] then + -- this is different from normal coverage, we assume only replacements + t[#t+1] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } + for unic, _ in next, sequence[start] do + local cu = contexts[unic] + if not cu then + contexts[unic] = t + end + end + end + end + end + end + elseif fmt == "glyphs" then + if lookuptype ~= "chainsub" and lookuptype ~= "chainpos" then + logs.report("otf process","unsupported coverage %s for %s",lookuptype,lookupname) + else + local contexts = contextchain[lookupname] + if not contexts then + contexts = { } + contextchain[lookupname] = contexts + end + local t = { } + for nofrules=1,#rules do + -- nearly the same as coverage so we could as well rename it + local rule = rules[nofrules] + local glyphs = rule.glyphs + if glyphs and glyphs.names then + local fore, back, names, sequence = glyphs.fore, glyphs.back, glyphs.names, { } + if fore and fore ~= "" then + fore = lpegmatch(split_at_space,fore) + uncover(fore,sequence,cache,unicodes) + end + local start = #sequence + 1 + names = lpegmatch(split_at_space,names) + uncover(names,sequence,cache,unicodes) + local stop = #sequence + if back and back ~= "" then + back = lpegmatch(split_at_space,back) + uncover(back,sequence,cache,unicodes) + end + if sequence[1] then + t[#t+1] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } + for unic, _ in next, sequence[start] do + local cu = contexts[unic] + if not cu then + contexts[unic] = t + end + end + end + end + end + end + end + end + end + end + end +end + +function fonts.initializers.node.otf.features(tfmdata,value) + if true then -- value then + if not tfmdata.shared.otfdata.shared.initialized then + local t = trace_preparing and os.clock() + local otfdata = tfmdata.shared.otfdata + local featuredata = otfdata.shared.featuredata + -- caches + featuredata.gsub_multiple = { } + featuredata.gsub_alternate = { } + featuredata.gsub_single = { } + featuredata.gsub_ligature = { } + featuredata.gsub_contextchain = { } + featuredata.gsub_reversecontextchain = { } + featuredata.gpos_pair = { } + featuredata.gpos_single = { } + featuredata.gpos_mark2base = { } + featuredata.gpos_mark2ligature = featuredata.gpos_mark2base + featuredata.gpos_mark2mark = featuredata.gpos_mark2base + featuredata.gpos_cursive = { } + featuredata.gpos_contextchain = featuredata.gsub_contextchain + featuredata.gpos_reversecontextchain = featuredata.gsub_reversecontextchain + -- + prepare_contextchains(tfmdata) + prepare_lookups(tfmdata) + otfdata.shared.initialized = true + if trace_preparing then + logs.report("otf process","preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?") + end + end + end +end diff --git a/tex/context/base/font-otp.lua b/tex/context/base/font-otp.lua new file mode 100644 index 000000000..a80c515ad --- /dev/null +++ b/tex/context/base/font-otp.lua @@ -0,0 +1,504 @@ +if not modules then modules = { } end modules ['font-otp'] = { + version = 1.001, + comment = "companion to font-otf.lua (packing)", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- todo: pack math (but not that much to share) + +local next, type, tostring = next, type, tostring +local sort, concat = table.sort, table.concat + +local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) + +fonts = fonts or { } +fonts.otf = fonts.otf or { } +fonts.otf.enhancers = fonts.otf.enhancers or { } +fonts.otf.glists = fonts.otf.glists or { "gsub", "gpos" } + +local criterium, threshold, tabstr = 1, 0, table.serialize + +local function tabstr(t) -- hashed from core-uti / experiment + local s = { } + for k, v in next, t do + if type(v) == "table" then + s[#s+1] = k.."={"..tabstr(v).."}" + else + s[#s+1] = k.."="..tostring(v) + end + end + sort(s) + return concat(s,",") +end + +function fonts.otf.enhancers.pack(data) + if data then + local h, t, c = { }, { }, { } + local hh, tt, cc = { }, { }, { } + local function pack_1(v) + -- v == table + local tag = tabstr(v) + local ht = h[tag] + if not ht then + ht = #t+1 + t[ht] = v + h[tag] = ht + c[ht] = 1 + else + c[ht] = c[ht] + 1 + end + return ht + end + local function pack_2(v) + -- v == number + if c[v] <= criterium then + return t[v] + else + -- compact hash + local hv = hh[v] + if not hv then + hv = #tt+1 + tt[hv] = t[v] + hh[v] = hv + cc[hv] = c[v] + end + return hv + end + end + local function success(stage,pass) + if #t == 0 then + if trace_loading then + logs.report("load otf","pack quality: nothing to pack") + end + return false + elseif #t >= threshold then + local one, two, rest = 0, 0, 0 + if pass == 1 then + for k,v in next, c do + if v == 1 then + one = one + 1 + elseif v == 2 then + two = two + 1 + else + rest = rest + 1 + end + end + else + for k,v in next, cc do + if v >20 then + rest = rest + 1 + elseif v >10 then + two = two + 1 + else + one = one + 1 + end + end + data.tables = tt + end + if trace_loading then + logs.report("load otf","pack quality: stage %s, pass %s, %s packed, 1-10:%s, 11-20:%s, rest:%s (criterium: %s)", stage, pass, one+two+rest, one, two, rest, criterium) + end + return true + else + if trace_loading then + logs.report("load otf","pack quality: stage %s, pass %s, %s packed, aborting pack (threshold: %s)", stage, pass, #t, threshold) + end + return false + end + end + for pass=1,2 do + local pack = (pass == 1 and pack_1) or pack_2 + for k, v in next, data.glyphs do + v.boundingbox = pack(v.boundingbox) + local l = v.slookups + if l then + for k,v in next, l do + l[k] = pack(v) + end + end + local l = v.mlookups + if l then + for k,v in next, l do + for kk=1,#v do + local vkk = v[kk] + local what = vkk[1] + if what == "pair" then + local t = vkk[3] if t then vkk[3] = pack(t) end + local t = vkk[4] if t then vkk[4] = pack(t) end + elseif what == "position" then + local t = vkk[2] if t then vkk[2] = pack(t) end + end + -- v[kk] = pack(vkk) + end + end + end + local m = v.mykerns + if m then + for k,v in next, m do + m[k] = pack(v) + end + end + local m = v.math + if m then + local mk = m.kerns + if mk then + for k,v in next, mk do + mk[k] = pack(v) + end + end + end + local a = v.anchors + if a then + for k,v in next, a do + if k == "baselig" then + for kk, vv in next, v do + for kkk=1,#vv do + vv[kkk] = pack(vv[kkk]) + end + end + else + for kk, vv in next, v do + v[kk] = pack(vv) + end + end + end + end + end + if data.lookups then + for k, v in next, data.lookups do + if v.rules then + for kk, vv in next, v.rules do + local l = vv.lookups + if l then + vv.lookups = pack(l) + end + local c = vv.coverage + if c then + local cc = c.before if cc then c.before = pack(cc) end + local cc = c.after if cc then c.after = pack(cc) end + local cc = c.current if cc then c.current = pack(cc) end + end + local c = vv.reversecoverage + if c then + local cc = c.before if cc then c.before = pack(cc) end + local cc = c.after if cc then c.after = pack(cc) end + local cc = c.current if cc then c.current = pack(cc) end + end + -- no need to pack vv.glyphs + local c = vv.glyphs + if c then + if c.fore == "" then c.fore = nil end + if c.back == "" then c.back = nil end + end + end + end + end + end + if data.luatex then + local la = data.luatex.anchor_to_lookup + if la then + for lookup, ldata in next, la do + la[lookup] = pack(ldata) + end + end + local la = data.luatex.lookup_to_anchor + if la then + for lookup, ldata in next, la do + la[lookup] = pack(ldata) + end + end + local ls = data.luatex.sequences + if ls then + for feature, fdata in next, ls do + local flags = fdata.flags + if flags then + fdata.flags = pack(flags) + end + local subtables = fdata.subtables + if subtables then + fdata.subtables = pack(subtables) + end + local features = fdata.features + if features then + for script, sdata in next, features do + features[script] = pack(sdata) + end + end + end + end + local ls = data.luatex.lookups + if ls then + for lookup, fdata in next, ls do + local flags = fdata.flags + if flags then + fdata.flags = pack(flags) + end + local subtables = fdata.subtables + if subtables then + fdata.subtables = pack(subtables) + end + end + end + local lf = data.luatex.features + if lf then + for _, g in next, fonts.otf.glists do + local gl = lf[g] + if gl then + for feature, spec in next, gl do + gl[feature] = pack(spec) + end + end + end + end + end + if not success(1,pass) then + return + end + end + if #t > 0 then + for pass=1,2 do + local pack = (pass == 1 and pack_1) or pack_2 + for k, v in next, data.glyphs do + local m = v.mykerns + if m then + v.mykerns = pack(m) + end + local m = v.math + if m then + local mk = m.kerns + if mk then + m.kerns = pack(mk) + end + end + local a = v.anchors + if a then + v.anchors = pack(a) + end + local l = v.mlookups + if l then + for k,v in next, l do + for kk=1,#v do + v[kk] = pack(v[kk]) + end + end + end + end + local ls = data.luatex.sequences + if ls then + for feature, fdata in next, ls do + fdata.features = pack(fdata.features) + end + end + if not success(2,pass) then +--~ return + end + end + end + end +end + +function fonts.otf.enhancers.unpack(data) + if data then + local t = data.tables + if t then + local unpacked = { } + for k, v in next, data.glyphs do + local tv = t[v.boundingbox] if tv then v.boundingbox = tv end + local l = v.slookups + if l then + for k,v in next, l do + local tv = t[v] if tv then l[k] = tv end + end + end + local l = v.mlookups + if l then + for k,v in next, l do + for i=1,#v do + local vi = v[i] + local tv = t[vi] + if tv then + v[i] = tv + if unpacked[tv] then + vi = false + else + unpacked[tv], vi = true, tv + end + end + if vi then + local what = vi[1] + if what == "pair" then + local tv = t[vi[3]] if tv then vi[3] = tv end + local tv = t[vi[4]] if tv then vi[4] = tv end + elseif what == "position" then + local tv = t[vi[2]] if tv then vi[2] = tv end + end + end + end + end + end + local m = v.mykerns + if m then + local tm = t[m] + if tm then + v.mykerns = tm + if unpacked[tm] then + m = false + else + unpacked[tm], m = true, tm + end + end + if m then + for k,v in next, m do + local tv = t[v] if tv then m[k] = tv end + end + end + end + local m = v.math + if m then + local mk = m.kerns + if mk then + local tm = t[mk] + if tm then + m.kerns = tm + if unpacked[tm] then + mk = false + else + unpacked[tm], mk = true, tm + end + end + if mk then + for k,v in next, mk do + local tv = t[v] if tv then mk[k] = tv end + end + end + end + end + local a = v.anchors + if a then + local ta = t[a] + if ta then + v.anchors = ta + if not unpacked[ta] then + unpacked[ta], a = true, ta + else + a = false + end + end + if a then + for k,v in next, a do + if k == "baselig" then + for kk, vv in next, v do + for kkk=1,#vv do + local tv = t[vv[kkk]] if tv then vv[kkk] = tv end + end + end + else + for kk, vv in next, v do + local tv = t[vv] if tv then v[kk] = tv end + end + end + end + end + end + end + if data.lookups then + for k, v in next, data.lookups do + local r = v.rules + if r then + for kk, vv in next, r do + local l = vv.lookups + if l then + local tv = t[l] if tv then vv.lookups = tv end + end + local c = vv.coverage + if c then + local cc = c.before if cc then local tv = t[cc] if tv then c.before = tv end end + cc = c.after if cc then local tv = t[cc] if tv then c.after = tv end end + cc = c.current if cc then local tv = t[cc] if tv then c.current = tv end end + end + local c = vv.reversecoverage + if c then + local cc = c.before if cc then local tv = t[cc] if tv then c.before = tv end end + cc = c.after if cc then local tv = t[cc] if tv then c.after = tv end end + cc = c.current if cc then local tv = t[cc] if tv then c.current = tv end end + end + -- no need to unpack vv.glyphs + end + end + end + end + local luatex = data.luatex + if luatex then + local la = luatex.anchor_to_lookup + if la then + for lookup, ldata in next, la do + local tv = t[ldata] if tv then la[lookup] = tv end + end + end + local la = luatex.lookup_to_anchor + if la then + for lookup, ldata in next, la do + local tv = t[ldata] if tv then la[lookup] = tv end + end + end + local ls = luatex.sequences + if ls then + for feature, fdata in next, ls do + local flags = fdata.flags + if flags then + local tv = t[flags] if tv then fdata.flags = tv end + end + local subtables = fdata.subtables + if subtables then + local tv = t[subtables] if tv then fdata.subtables = tv end + end + local features = fdata.features + if features then + local tv = t[features] + if tv then + fdata.features = tv + if not unpacked[tv] then + unpacked[tv], features = true, tv + else + features = false + end + end + if features then + for script, sdata in next, features do + local tv = t[sdata] if tv then features[script] = tv end + end + end + end + end + end + local ls = luatex.lookups + if ls then + for lookups, fdata in next, ls do + local flags = fdata.flags + if flags then + local tv = t[flags] if tv then fdata.flags = tv end + end + local subtables = fdata.subtables + if subtables then + local tv = t[subtables] if tv then fdata.subtables = tv end + end + end + end + local lf = luatex.features + if lf then + for _, g in next, fonts.otf.glists do + local gl = lf[g] + if gl then + for feature, spec in next, gl do + local tv = t[spec] if tv then gl[feature] = tv end + end + end + end + end + end + data.tables = nil + end + end +end diff --git a/tex/context/base/font-ott.lua b/tex/context/base/font-ott.lua new file mode 100644 index 000000000..2be1bf06c --- /dev/null +++ b/tex/context/base/font-ott.lua @@ -0,0 +1,956 @@ +if not modules then modules = { } end modules ['font-otf'] = { + version = 1.001, + comment = "companion to font-otf.lua (tables)", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local type, next, tonumber, tostring = type, next, tonumber, tostring +local gsub, lower = string.gsub, string.lower + +fonts = fonts or { } +fonts.otf = fonts.otf or { } + +local otf = fonts.otf + +otf.tables = otf.tables or { } +otf.meanings = otf.meanings or { } + +otf.tables.scripts = { + ['dflt'] = 'Default', + + ['arab'] = 'Arabic', + ['armn'] = 'Armenian', + ['bali'] = 'Balinese', + ['beng'] = 'Bengali', + ['bopo'] = 'Bopomofo', + ['brai'] = 'Braille', + ['bugi'] = 'Buginese', + ['buhd'] = 'Buhid', + ['byzm'] = 'Byzantine Music', + ['cans'] = 'Canadian Syllabics', + ['cher'] = 'Cherokee', + ['copt'] = 'Coptic', + ['cprt'] = 'Cypriot Syllabary', + ['cyrl'] = 'Cyrillic', + ['deva'] = 'Devanagari', + ['dsrt'] = 'Deseret', + ['ethi'] = 'Ethiopic', + ['geor'] = 'Georgian', + ['glag'] = 'Glagolitic', + ['goth'] = 'Gothic', + ['grek'] = 'Greek', + ['gujr'] = 'Gujarati', + ['guru'] = 'Gurmukhi', + ['hang'] = 'Hangul', + ['hani'] = 'CJK Ideographic', + ['hano'] = 'Hanunoo', + ['hebr'] = 'Hebrew', + ['ital'] = 'Old Italic', + ['jamo'] = 'Hangul Jamo', + ['java'] = 'Javanese', + ['kana'] = 'Hiragana and Katakana', + ['khar'] = 'Kharosthi', + ['khmr'] = 'Khmer', + ['knda'] = 'Kannada', + ['lao' ] = 'Lao', + ['latn'] = 'Latin', + ['limb'] = 'Limbu', + ['linb'] = 'Linear B', + ['math'] = 'Mathematical Alphanumeric Symbols', + ['mlym'] = 'Malayalam', + ['mong'] = 'Mongolian', + ['musc'] = 'Musical Symbols', + ['mymr'] = 'Myanmar', + ['nko' ] = "N'ko", + ['ogam'] = 'Ogham', + ['orya'] = 'Oriya', + ['osma'] = 'Osmanya', + ['phag'] = 'Phags-pa', + ['phnx'] = 'Phoenician', + ['runr'] = 'Runic', + ['shaw'] = 'Shavian', + ['sinh'] = 'Sinhala', + ['sylo'] = 'Syloti Nagri', + ['syrc'] = 'Syriac', + ['tagb'] = 'Tagbanwa', + ['tale'] = 'Tai Le', + ['talu'] = 'Tai Lu', + ['taml'] = 'Tamil', + ['telu'] = 'Telugu', + ['tfng'] = 'Tifinagh', + ['tglg'] = 'Tagalog', + ['thaa'] = 'Thaana', + ['thai'] = 'Thai', + ['tibt'] = 'Tibetan', + ['ugar'] = 'Ugaritic Cuneiform', + ['xpeo'] = 'Old Persian Cuneiform', + ['xsux'] = 'Sumero-Akkadian Cuneiform', + ['yi' ] = 'Yi', +} + +otf.tables.languages = { + ['dflt'] = 'Default', + + ['aba'] = 'Abaza', + ['abk'] = 'Abkhazian', + ['ady'] = 'Adyghe', + ['afk'] = 'Afrikaans', + ['afr'] = 'Afar', + ['agw'] = 'Agaw', + ['als'] = 'Alsatian', + ['alt'] = 'Altai', + ['amh'] = 'Amharic', + ['ara'] = 'Arabic', + ['ari'] = 'Aari', + ['ark'] = 'Arakanese', + ['asm'] = 'Assamese', + ['ath'] = 'Athapaskan', + ['avr'] = 'Avar', + ['awa'] = 'Awadhi', + ['aym'] = 'Aymara', + ['aze'] = 'Azeri', + ['bad'] = 'Badaga', + ['bag'] = 'Baghelkhandi', + ['bal'] = 'Balkar', + ['bau'] = 'Baule', + ['bbr'] = 'Berber', + ['bch'] = 'Bench', + ['bcr'] = 'Bible Cree', + ['bel'] = 'Belarussian', + ['bem'] = 'Bemba', + ['ben'] = 'Bengali', + ['bgr'] = 'Bulgarian', + ['bhi'] = 'Bhili', + ['bho'] = 'Bhojpuri', + ['bik'] = 'Bikol', + ['bil'] = 'Bilen', + ['bkf'] = 'Blackfoot', + ['bli'] = 'Balochi', + ['bln'] = 'Balante', + ['blt'] = 'Balti', + ['bmb'] = 'Bambara', + ['bml'] = 'Bamileke', + ['bos'] = 'Bosnian', + ['bre'] = 'Breton', + ['brh'] = 'Brahui', + ['bri'] = 'Braj Bhasha', + ['brm'] = 'Burmese', + ['bsh'] = 'Bashkir', + ['bti'] = 'Beti', + ['cat'] = 'Catalan', + ['ceb'] = 'Cebuano', + ['che'] = 'Chechen', + ['chg'] = 'Chaha Gurage', + ['chh'] = 'Chattisgarhi', + ['chi'] = 'Chichewa', + ['chk'] = 'Chukchi', + ['chp'] = 'Chipewyan', + ['chr'] = 'Cherokee', + ['chu'] = 'Chuvash', + ['cmr'] = 'Comorian', + ['cop'] = 'Coptic', + ['cos'] = 'Corsican', + ['cre'] = 'Cree', + ['crr'] = 'Carrier', + ['crt'] = 'Crimean Tatar', + ['csl'] = 'Church Slavonic', + ['csy'] = 'Czech', + ['dan'] = 'Danish', + ['dar'] = 'Dargwa', + ['dcr'] = 'Woods Cree', + ['deu'] = 'German', + ['dgr'] = 'Dogri', + ['div'] = 'Divehi', + ['djr'] = 'Djerma', + ['dng'] = 'Dangme', + ['dnk'] = 'Dinka', + ['dri'] = 'Dari', + ['dun'] = 'Dungan', + ['dzn'] = 'Dzongkha', + ['ebi'] = 'Ebira', + ['ecr'] = 'Eastern Cree', + ['edo'] = 'Edo', + ['efi'] = 'Efik', + ['ell'] = 'Greek', + ['eng'] = 'English', + ['erz'] = 'Erzya', + ['esp'] = 'Spanish', + ['eti'] = 'Estonian', + ['euq'] = 'Basque', + ['evk'] = 'Evenki', + ['evn'] = 'Even', + ['ewe'] = 'Ewe', + ['fan'] = 'French Antillean', + ['far'] = 'Farsi', + ['fin'] = 'Finnish', + ['fji'] = 'Fijian', + ['fle'] = 'Flemish', + ['fne'] = 'Forest Nenets', + ['fon'] = 'Fon', + ['fos'] = 'Faroese', + ['fra'] = 'French', + ['fri'] = 'Frisian', + ['frl'] = 'Friulian', + ['fta'] = 'Futa', + ['ful'] = 'Fulani', + ['gad'] = 'Ga', + ['gae'] = 'Gaelic', + ['gag'] = 'Gagauz', + ['gal'] = 'Galician', + ['gar'] = 'Garshuni', + ['gaw'] = 'Garhwali', + ['gez'] = "Ge'ez", + ['gil'] = 'Gilyak', + ['gmz'] = 'Gumuz', + ['gon'] = 'Gondi', + ['grn'] = 'Greenlandic', + ['gro'] = 'Garo', + ['gua'] = 'Guarani', + ['guj'] = 'Gujarati', + ['hai'] = 'Haitian', + ['hal'] = 'Halam', + ['har'] = 'Harauti', + ['hau'] = 'Hausa', + ['haw'] = 'Hawaiin', + ['hbn'] = 'Hammer-Banna', + ['hil'] = 'Hiligaynon', + ['hin'] = 'Hindi', + ['hma'] = 'High Mari', + ['hnd'] = 'Hindko', + ['ho'] = 'Ho', + ['hri'] = 'Harari', + ['hrv'] = 'Croatian', + ['hun'] = 'Hungarian', + ['hye'] = 'Armenian', + ['ibo'] = 'Igbo', + ['ijo'] = 'Ijo', + ['ilo'] = 'Ilokano', + ['ind'] = 'Indonesian', + ['ing'] = 'Ingush', + ['inu'] = 'Inuktitut', + ['iri'] = 'Irish', + ['irt'] = 'Irish Traditional', + ['isl'] = 'Icelandic', + ['ism'] = 'Inari Sami', + ['ita'] = 'Italian', + ['iwr'] = 'Hebrew', + ['jan'] = 'Japanese', + ['jav'] = 'Javanese', + ['jii'] = 'Yiddish', + ['jud'] = 'Judezmo', + ['jul'] = 'Jula', + ['kab'] = 'Kabardian', + ['kac'] = 'Kachchi', + ['kal'] = 'Kalenjin', + ['kan'] = 'Kannada', + ['kar'] = 'Karachay', + ['kat'] = 'Georgian', + ['kaz'] = 'Kazakh', + ['keb'] = 'Kebena', + ['kge'] = 'Khutsuri Georgian', + ['kha'] = 'Khakass', + ['khk'] = 'Khanty-Kazim', + ['khm'] = 'Khmer', + ['khs'] = 'Khanty-Shurishkar', + ['khv'] = 'Khanty-Vakhi', + ['khw'] = 'Khowar', + ['kik'] = 'Kikuyu', + ['kir'] = 'Kirghiz', + ['kis'] = 'Kisii', + ['kkn'] = 'Kokni', + ['klm'] = 'Kalmyk', + ['kmb'] = 'Kamba', + ['kmn'] = 'Kumaoni', + ['kmo'] = 'Komo', + ['kms'] = 'Komso', + ['knr'] = 'Kanuri', + ['kod'] = 'Kodagu', + ['koh'] = 'Korean Old Hangul', + ['kok'] = 'Konkani', + ['kon'] = 'Kikongo', + ['kop'] = 'Komi-Permyak', + ['kor'] = 'Korean', + ['koz'] = 'Komi-Zyrian', + ['kpl'] = 'Kpelle', + ['kri'] = 'Krio', + ['krk'] = 'Karakalpak', + ['krl'] = 'Karelian', + ['krm'] = 'Karaim', + ['krn'] = 'Karen', + ['krt'] = 'Koorete', + ['ksh'] = 'Kashmiri', + ['ksi'] = 'Khasi', + ['ksm'] = 'Kildin Sami', + ['kui'] = 'Kui', + ['kul'] = 'Kulvi', + ['kum'] = 'Kumyk', + ['kur'] = 'Kurdish', + ['kuu'] = 'Kurukh', + ['kuy'] = 'Kuy', + ['kyk'] = 'Koryak', + ['lad'] = 'Ladin', + ['lah'] = 'Lahuli', + ['lak'] = 'Lak', + ['lam'] = 'Lambani', + ['lao'] = 'Lao', + ['lat'] = 'Latin', + ['laz'] = 'Laz', + ['lcr'] = 'L-Cree', + ['ldk'] = 'Ladakhi', + ['lez'] = 'Lezgi', + ['lin'] = 'Lingala', + ['lma'] = 'Low Mari', + ['lmb'] = 'Limbu', + ['lmw'] = 'Lomwe', + ['lsb'] = 'Lower Sorbian', + ['lsm'] = 'Lule Sami', + ['lth'] = 'Lithuanian', + ['ltz'] = 'Luxembourgish', + ['lub'] = 'Luba', + ['lug'] = 'Luganda', + ['luh'] = 'Luhya', + ['luo'] = 'Luo', + ['lvi'] = 'Latvian', + ['maj'] = 'Majang', + ['mak'] = 'Makua', + ['mal'] = 'Malayalam Traditional', + ['man'] = 'Mansi', + ['map'] = 'Mapudungun', + ['mar'] = 'Marathi', + ['maw'] = 'Marwari', + ['mbn'] = 'Mbundu', + ['mch'] = 'Manchu', + ['mcr'] = 'Moose Cree', + ['mde'] = 'Mende', + ['men'] = "Me'en", + ['miz'] = 'Mizo', + ['mkd'] = 'Macedonian', + ['mle'] = 'Male', + ['mlg'] = 'Malagasy', + ['mln'] = 'Malinke', + ['mlr'] = 'Malayalam Reformed', + ['mly'] = 'Malay', + ['mnd'] = 'Mandinka', + ['mng'] = 'Mongolian', + ['mni'] = 'Manipuri', + ['mnk'] = 'Maninka', + ['mnx'] = 'Manx Gaelic', + ['moh'] = 'Mohawk', + ['mok'] = 'Moksha', + ['mol'] = 'Moldavian', + ['mon'] = 'Mon', + ['mor'] = 'Moroccan', + ['mri'] = 'Maori', + ['mth'] = 'Maithili', + ['mts'] = 'Maltese', + ['mun'] = 'Mundari', + ['nag'] = 'Naga-Assamese', + ['nan'] = 'Nanai', + ['nas'] = 'Naskapi', + ['ncr'] = 'N-Cree', + ['ndb'] = 'Ndebele', + ['ndg'] = 'Ndonga', + ['nep'] = 'Nepali', + ['new'] = 'Newari', + ['ngr'] = 'Nagari', + ['nhc'] = 'Norway House Cree', + ['nis'] = 'Nisi', + ['niu'] = 'Niuean', + ['nkl'] = 'Nkole', + ['nko'] = "N'ko", + ['nld'] = 'Dutch', + ['nog'] = 'Nogai', + ['nor'] = 'Norwegian', + ['nsm'] = 'Northern Sami', + ['nta'] = 'Northern Tai', + ['nto'] = 'Esperanto', + ['nyn'] = 'Nynorsk', + ['oci'] = 'Occitan', + ['ocr'] = 'Oji-Cree', + ['ojb'] = 'Ojibway', + ['ori'] = 'Oriya', + ['oro'] = 'Oromo', + ['oss'] = 'Ossetian', + ['paa'] = 'Palestinian Aramaic', + ['pal'] = 'Pali', + ['pan'] = 'Punjabi', + ['pap'] = 'Palpa', + ['pas'] = 'Pashto', + ['pgr'] = 'Polytonic Greek', + ['pil'] = 'Pilipino', + ['plg'] = 'Palaung', + ['plk'] = 'Polish', + ['pro'] = 'Provencal', + ['ptg'] = 'Portuguese', + ['qin'] = 'Chin', + ['raj'] = 'Rajasthani', + ['rbu'] = 'Russian Buriat', + ['rcr'] = 'R-Cree', + ['ria'] = 'Riang', + ['rms'] = 'Rhaeto-Romanic', + ['rom'] = 'Romanian', + ['roy'] = 'Romany', + ['rsy'] = 'Rusyn', + ['rua'] = 'Ruanda', + ['rus'] = 'Russian', + ['sad'] = 'Sadri', + ['san'] = 'Sanskrit', + ['sat'] = 'Santali', + ['say'] = 'Sayisi', + ['sek'] = 'Sekota', + ['sel'] = 'Selkup', + ['sgo'] = 'Sango', + ['shn'] = 'Shan', + ['sib'] = 'Sibe', + ['sid'] = 'Sidamo', + ['sig'] = 'Silte Gurage', + ['sks'] = 'Skolt Sami', + ['sky'] = 'Slovak', + ['sla'] = 'Slavey', + ['slv'] = 'Slovenian', + ['sml'] = 'Somali', + ['smo'] = 'Samoan', + ['sna'] = 'Sena', + ['snd'] = 'Sindhi', + ['snh'] = 'Sinhalese', + ['snk'] = 'Soninke', + ['sog'] = 'Sodo Gurage', + ['sot'] = 'Sotho', + ['sqi'] = 'Albanian', + ['srb'] = 'Serbian', + ['srk'] = 'Saraiki', + ['srr'] = 'Serer', + ['ssl'] = 'South Slavey', + ['ssm'] = 'Southern Sami', + ['sur'] = 'Suri', + ['sva'] = 'Svan', + ['sve'] = 'Swedish', + ['swa'] = 'Swadaya Aramaic', + ['swk'] = 'Swahili', + ['swz'] = 'Swazi', + ['sxt'] = 'Sutu', + ['syr'] = 'Syriac', + ['tab'] = 'Tabasaran', + ['taj'] = 'Tajiki', + ['tam'] = 'Tamil', + ['tat'] = 'Tatar', + ['tcr'] = 'TH-Cree', + ['tel'] = 'Telugu', + ['tgn'] = 'Tongan', + ['tgr'] = 'Tigre', + ['tgy'] = 'Tigrinya', + ['tha'] = 'Thai', + ['tht'] = 'Tahitian', + ['tib'] = 'Tibetan', + ['tkm'] = 'Turkmen', + ['tmn'] = 'Temne', + ['tna'] = 'Tswana', + ['tne'] = 'Tundra Nenets', + ['tng'] = 'Tonga', + ['tod'] = 'Todo', + ['trk'] = 'Turkish', + ['tsg'] = 'Tsonga', + ['tua'] = 'Turoyo Aramaic', + ['tul'] = 'Tulu', + ['tuv'] = 'Tuvin', + ['twi'] = 'Twi', + ['udm'] = 'Udmurt', + ['ukr'] = 'Ukrainian', + ['urd'] = 'Urdu', + ['usb'] = 'Upper Sorbian', + ['uyg'] = 'Uyghur', + ['uzb'] = 'Uzbek', + ['ven'] = 'Venda', + ['vit'] = 'Vietnamese', + ['wa' ] = 'Wa', + ['wag'] = 'Wagdi', + ['wcr'] = 'West-Cree', + ['wel'] = 'Welsh', + ['wlf'] = 'Wolof', + ['xbd'] = 'Tai Lue', + ['xhs'] = 'Xhosa', + ['yak'] = 'Yakut', + ['yba'] = 'Yoruba', + ['ycr'] = 'Y-Cree', + ['yic'] = 'Yi Classic', + ['yim'] = 'Yi Modern', + ['zhh'] = 'Chinese Hong Kong', + ['zhp'] = 'Chinese Phonetic', + ['zhs'] = 'Chinese Simplified', + ['zht'] = 'Chinese Traditional', + ['znd'] = 'Zande', + ['zul'] = 'Zulu' +} + +otf.tables.features = { + ['aalt'] = 'Access All Alternates', + ['abvf'] = 'Above-Base Forms', + ['abvm'] = 'Above-Base Mark Positioning', + ['abvs'] = 'Above-Base Substitutions', + ['afrc'] = 'Alternative Fractions', + ['akhn'] = 'Akhands', + ['blwf'] = 'Below-Base Forms', + ['blwm'] = 'Below-Base Mark Positioning', + ['blws'] = 'Below-Base Substitutions', + ['c2pc'] = 'Petite Capitals From Capitals', + ['c2sc'] = 'Small Capitals From Capitals', + ['calt'] = 'Contextual Alternates', + ['case'] = 'Case-Sensitive Forms', + ['ccmp'] = 'Glyph Composition/Decomposition', + ['cjct'] = 'Conjunct Forms', + ['clig'] = 'Contextual Ligatures', + ['cpsp'] = 'Capital Spacing', + ['cswh'] = 'Contextual Swash', + ['curs'] = 'Cursive Positioning', + ['dflt'] = 'Default Processing', + ['dist'] = 'Distances', + ['dlig'] = 'Discretionary Ligatures', + ['dnom'] = 'Denominators', + ['dtls'] = 'Dotless Forms', -- math + ['expt'] = 'Expert Forms', + ['falt'] = 'Final glyph Alternates', + ['fin2'] = 'Terminal Forms #2', + ['fin3'] = 'Terminal Forms #3', + ['fina'] = 'Terminal Forms', + ['flac'] = 'Flattened Accents Over Capitals', -- math + ['frac'] = 'Fractions', + ['fwid'] = 'Full Width', + ['half'] = 'Half Forms', + ['haln'] = 'Halant Forms', + ['halt'] = 'Alternate Half Width', + ['hist'] = 'Historical Forms', + ['hkna'] = 'Horizontal Kana Alternates', + ['hlig'] = 'Historical Ligatures', + ['hngl'] = 'Hangul', + ['hojo'] = 'Hojo Kanji Forms', + ['hwid'] = 'Half Width', + ['init'] = 'Initial Forms', + ['isol'] = 'Isolated Forms', + ['ital'] = 'Italics', + ['jalt'] = 'Justification Alternatives', + ['jp04'] = 'JIS2004 Forms', + ['jp78'] = 'JIS78 Forms', + ['jp83'] = 'JIS83 Forms', + ['jp90'] = 'JIS90 Forms', + ['kern'] = 'Kerning', + ['lfbd'] = 'Left Bounds', + ['liga'] = 'Standard Ligatures', + ['ljmo'] = 'Leading Jamo Forms', + ['lnum'] = 'Lining Figures', + ['locl'] = 'Localized Forms', + ['mark'] = 'Mark Positioning', + ['med2'] = 'Medial Forms #2', + ['medi'] = 'Medial Forms', + ['mgrk'] = 'Mathematical Greek', + ['mkmk'] = 'Mark to Mark Positioning', + ['mset'] = 'Mark Positioning via Substitution', + ['nalt'] = 'Alternate Annotation Forms', + ['nlck'] = 'NLC Kanji Forms', + ['nukt'] = 'Nukta Forms', + ['numr'] = 'Numerators', + ['onum'] = 'Old Style Figures', + ['opbd'] = 'Optical Bounds', + ['ordn'] = 'Ordinals', + ['ornm'] = 'Ornaments', + ['palt'] = 'Proportional Alternate Width', + ['pcap'] = 'Petite Capitals', + ['pnum'] = 'Proportional Figures', + ['pref'] = 'Pre-base Forms', + ['pres'] = 'Pre-base Substitutions', + ['pstf'] = 'Post-base Forms', + ['psts'] = 'Post-base Substitutions', + ['pwid'] = 'Proportional Widths', + ['qwid'] = 'Quarter Widths', + ['rand'] = 'Randomize', + ['rkrf'] = 'Rakar Forms', + ['rlig'] = 'Required Ligatures', + ['rphf'] = 'Reph Form', + ['rtbd'] = 'Right Bounds', + ['rtla'] = 'Right-To-Left Alternates', + ['rtlm'] = 'Right To Left Math', -- math + ['ruby'] = 'Ruby Notation Forms', + ['salt'] = 'Stylistic Alternates', + ['sinf'] = 'Scientific Inferiors', + ['size'] = 'Optical Size', + ['smcp'] = 'Small Capitals', + ['smpl'] = 'Simplified Forms', + ['ss01'] = 'Stylistic Set 1', + ['ss02'] = 'Stylistic Set 2', + ['ss03'] = 'Stylistic Set 3', + ['ss04'] = 'Stylistic Set 4', + ['ss05'] = 'Stylistic Set 5', + ['ss06'] = 'Stylistic Set 6', + ['ss07'] = 'Stylistic Set 7', + ['ss08'] = 'Stylistic Set 8', + ['ss09'] = 'Stylistic Set 9', + ['ss10'] = 'Stylistic Set 10', + ['ss11'] = 'Stylistic Set 11', + ['ss12'] = 'Stylistic Set 12', + ['ss13'] = 'Stylistic Set 13', + ['ss14'] = 'Stylistic Set 14', + ['ss15'] = 'Stylistic Set 15', + ['ss16'] = 'Stylistic Set 16', + ['ss17'] = 'Stylistic Set 17', + ['ss18'] = 'Stylistic Set 18', + ['ss19'] = 'Stylistic Set 19', + ['ss20'] = 'Stylistic Set 20', + ['ssty'] = 'Script Style', -- math + ['subs'] = 'Subscript', + ['sups'] = 'Superscript', + ['swsh'] = 'Swash', + ['titl'] = 'Titling', + ['tjmo'] = 'Trailing Jamo Forms', + ['tnam'] = 'Traditional Name Forms', + ['tnum'] = 'Tabular Figures', + ['trad'] = 'Traditional Forms', + ['twid'] = 'Third Widths', + ['unic'] = 'Unicase', + ['valt'] = 'Alternate Vertical Metrics', + ['vatu'] = 'Vattu Variants', + ['vert'] = 'Vertical Writing', + ['vhal'] = 'Alternate Vertical Half Metrics', + ['vjmo'] = 'Vowel Jamo Forms', + ['vkna'] = 'Vertical Kana Alternates', + ['vkrn'] = 'Vertical Kerning', + ['vpal'] = 'Proportional Alternate Vertical Metrics', + ['vrt2'] = 'Vertical Rotation', + ['zero'] = 'Slashed Zero', + + ['trep'] = 'Traditional TeX Replacements', + ['tlig'] = 'Traditional TeX Ligatures', +} + +otf.tables.baselines = { + ['hang'] = 'Hanging baseline', + ['icfb'] = 'Ideographic character face bottom edge baseline', + ['icft'] = 'Ideographic character face tope edige baseline', + ['ideo'] = 'Ideographic em-box bottom edge baseline', + ['idtp'] = 'Ideographic em-box top edge baseline', + ['math'] = 'Mathmatical centered baseline', + ['romn'] = 'Roman baseline' +} + +-- can be sped up by local tables + +function otf.tables.to_tag(id) + return stringformat("%4s",lower(id)) +end + +local function resolve(tab,id) + if tab and id then + id = lower(id) + return tab[id] or tab[gsub(id," ","")] or tab['dflt'] or '' + else + return "unknown" + end +end + +function otf.meanings.script(id) + return resolve(otf.tables.scripts,id) +end +function otf.meanings.language(id) + return resolve(otf.tables.languages,id) +end +function otf.meanings.feature(id) + return resolve(otf.tables.features,id) +end +function otf.meanings.baseline(id) + return resolve(otf.tables.baselines,id) +end + +otf.tables.to_scripts = table.reverse_hash(otf.tables.scripts ) +otf.tables.to_languages = table.reverse_hash(otf.tables.languages) +otf.tables.to_features = table.reverse_hash(otf.tables.features ) + +local scripts = otf.tables.scripts +local languages = otf.tables.languages +local features = otf.tables.features + +local to_scripts = otf.tables.to_scripts +local to_languages = otf.tables.to_languages +local to_features = otf.tables.to_features + +for k, v in next, to_features do + local stripped = gsub(k,"%-"," ") + to_features[stripped] = v + local stripped = gsub(k,"[^a-zA-Z0-9]","") + to_features[stripped] = v +end +for k, v in next, to_features do + to_features[lower(k)] = v +end + +otf.meanings.checkers = { + rand = function(v) + return v and "random" + end +} + +local checkers = otf.meanings.checkers + +function otf.meanings.normalize(features) + local h = { } + for k,v in next, features do + k = lower(k) + if k == "language" or k == "lang" then + v = gsub(lower(v),"[^a-z0-9%-]","") + k = language + if not languages[v] then + h.language = to_languages[v] or "dflt" + else + h.language = v + end + elseif k == "script" then + v = gsub(lower(v),"[^a-z0-9%-]","") + if not scripts[v] then + h.script = to_scripts[v] or "dflt" + else + h.script = v + end + else + if type(v) == "string" then + local b = v:is_boolean() + if type(b) == "nil" then + v = tonumber(v) or lower(v) + else + v = b + end + end + k = to_features[k] or k + local c = checkers[k] + h[k] = c and c(v) or v + end + end + return h +end + +-- When I feel the need ... + +--~ otf.tables.aat = { +--~ [ 0] = { +--~ name = "allTypographicFeaturesType", +--~ [ 0] = "allTypeFeaturesOnSelector", +--~ [ 1] = "allTypeFeaturesOffSelector", +--~ }, +--~ [ 1] = { +--~ name = "ligaturesType", +--~ [0 ] = "requiredLigaturesOnSelector", +--~ [1 ] = "requiredLigaturesOffSelector", +--~ [2 ] = "commonLigaturesOnSelector", +--~ [3 ] = "commonLigaturesOffSelector", +--~ [4 ] = "rareLigaturesOnSelector", +--~ [5 ] = "rareLigaturesOffSelector", +--~ [6 ] = "logosOnSelector ", +--~ [7 ] = "logosOffSelector ", +--~ [8 ] = "rebusPicturesOnSelector", +--~ [9 ] = "rebusPicturesOffSelector", +--~ [10] = "diphthongLigaturesOnSelector", +--~ [11] = "diphthongLigaturesOffSelector", +--~ [12] = "squaredLigaturesOnSelector", +--~ [13] = "squaredLigaturesOffSelector", +--~ [14] = "abbrevSquaredLigaturesOnSelector", +--~ [15] = "abbrevSquaredLigaturesOffSelector", +--~ }, +--~ [ 2] = { +--~ name = "cursiveConnectionType", +--~ [ 0] = "unconnectedSelector", +--~ [ 1] = "partiallyConnectedSelector", +--~ [ 2] = "cursiveSelector ", +--~ }, +--~ [ 3] = { +--~ name = "letterCaseType", +--~ [ 0] = "upperAndLowerCaseSelector", +--~ [ 1] = "allCapsSelector ", +--~ [ 2] = "allLowerCaseSelector", +--~ [ 3] = "smallCapsSelector ", +--~ [ 4] = "initialCapsSelector", +--~ [ 5] = "initialCapsAndSmallCapsSelector", +--~ }, +--~ [ 4] = { +--~ name = "verticalSubstitutionType", +--~ [ 0] = "substituteVerticalFormsOnSelector", +--~ [ 1] = "substituteVerticalFormsOffSelector", +--~ }, +--~ [ 5] = { +--~ name = "linguisticRearrangementType", +--~ [ 0] = "linguisticRearrangementOnSelector", +--~ [ 1] = "linguisticRearrangementOffSelector", +--~ }, +--~ [ 6] = { +--~ name = "numberSpacingType", +--~ [ 0] = "monospacedNumbersSelector", +--~ [ 1] = "proportionalNumbersSelector", +--~ }, +--~ [ 7] = { +--~ name = "appleReserved1Type", +--~ }, +--~ [ 8] = { +--~ name = "smartSwashType", +--~ [ 0] = "wordInitialSwashesOnSelector", +--~ [ 1] = "wordInitialSwashesOffSelector", +--~ [ 2] = "wordFinalSwashesOnSelector", +--~ [ 3] = "wordFinalSwashesOffSelector", +--~ [ 4] = "lineInitialSwashesOnSelector", +--~ [ 5] = "lineInitialSwashesOffSelector", +--~ [ 6] = "lineFinalSwashesOnSelector", +--~ [ 7] = "lineFinalSwashesOffSelector", +--~ [ 8] = "nonFinalSwashesOnSelector", +--~ [ 9] = "nonFinalSwashesOffSelector", +--~ }, +--~ [ 9] = { +--~ name = "diacriticsType", +--~ [ 0] = "showDiacriticsSelector", +--~ [ 1] = "hideDiacriticsSelector", +--~ [ 2] = "decomposeDiacriticsSelector", +--~ }, +--~ [10] = { +--~ name = "verticalPositionType", +--~ [ 0] = "normalPositionSelector", +--~ [ 1] = "superiorsSelector ", +--~ [ 2] = "inferiorsSelector ", +--~ [ 3] = "ordinalsSelector ", +--~ }, +--~ [11] = { +--~ name = "fractionsType", +--~ [ 0] = "noFractionsSelector", +--~ [ 1] = "verticalFractionsSelector", +--~ [ 2] = "diagonalFractionsSelector", +--~ }, +--~ [12] = { +--~ name = "appleReserved2Type", +--~ }, +--~ [13] = { +--~ name = "overlappingCharactersType", +--~ [ 0] = "preventOverlapOnSelector", +--~ [ 1] = "preventOverlapOffSelector", +--~ }, +--~ [14] = { +--~ name = "typographicExtrasType", +--~ [0 ] = "hyphensToEmDashOnSelector", +--~ [1 ] = "hyphensToEmDashOffSelector", +--~ [2 ] = "hyphenToEnDashOnSelector", +--~ [3 ] = "hyphenToEnDashOffSelector", +--~ [4 ] = "unslashedZeroOnSelector", +--~ [5 ] = "unslashedZeroOffSelector", +--~ [6 ] = "formInterrobangOnSelector", +--~ [7 ] = "formInterrobangOffSelector", +--~ [8 ] = "smartQuotesOnSelector", +--~ [9 ] = "smartQuotesOffSelector", +--~ [10] = "periodsToEllipsisOnSelector", +--~ [11] = "periodsToEllipsisOffSelector", +--~ }, +--~ [15] = { +--~ name = "mathematicalExtrasType", +--~ [ 0] = "hyphenToMinusOnSelector", +--~ [ 1] = "hyphenToMinusOffSelector", +--~ [ 2] = "asteriskToMultiplyOnSelector", +--~ [ 3] = "asteriskToMultiplyOffSelector", +--~ [ 4] = "slashToDivideOnSelector", +--~ [ 5] = "slashToDivideOffSelector", +--~ [ 6] = "inequalityLigaturesOnSelector", +--~ [ 7] = "inequalityLigaturesOffSelector", +--~ [ 8] = "exponentsOnSelector", +--~ [ 9] = "exponentsOffSelector", +--~ }, +--~ [16] = { +--~ name = "ornamentSetsType", +--~ [ 0] = "noOrnamentsSelector", +--~ [ 1] = "dingbatsSelector ", +--~ [ 2] = "piCharactersSelector", +--~ [ 3] = "fleuronsSelector ", +--~ [ 4] = "decorativeBordersSelector", +--~ [ 5] = "internationalSymbolsSelector", +--~ [ 6] = "mathSymbolsSelector", +--~ }, +--~ [17] = { +--~ name = "characterAlternativesType", +--~ [ 0] = "noAlternatesSelector", +--~ }, +--~ [18] = { +--~ name = "designComplexityType", +--~ [ 0] = "designLevel1Selector", +--~ [ 1] = "designLevel2Selector", +--~ [ 2] = "designLevel3Selector", +--~ [ 3] = "designLevel4Selector", +--~ [ 4] = "designLevel5Selector", +--~ }, +--~ [19] = { +--~ name = "styleOptionsType", +--~ [ 0] = "noStyleOptionsSelector", +--~ [ 1] = "displayTextSelector", +--~ [ 2] = "engravedTextSelector", +--~ [ 3] = "illuminatedCapsSelector", +--~ [ 4] = "titlingCapsSelector", +--~ [ 5] = "tallCapsSelector ", +--~ }, +--~ [20] = { +--~ name = "characterShapeType", +--~ [0 ] = "traditionalCharactersSelector", +--~ [1 ] = "simplifiedCharactersSelector", +--~ [2 ] = "jis1978CharactersSelector", +--~ [3 ] = "jis1983CharactersSelector", +--~ [4 ] = "jis1990CharactersSelector", +--~ [5 ] = "traditionalAltOneSelector", +--~ [6 ] = "traditionalAltTwoSelector", +--~ [7 ] = "traditionalAltThreeSelector", +--~ [8 ] = "traditionalAltFourSelector", +--~ [9 ] = "traditionalAltFiveSelector", +--~ [10] = "expertCharactersSelector", +--~ }, +--~ [21] = { +--~ name = "numberCaseType", +--~ [ 0] = "lowerCaseNumbersSelector", +--~ [ 1] = "upperCaseNumbersSelector", +--~ }, +--~ [22] = { +--~ name = "textSpacingType", +--~ [ 0] = "proportionalTextSelector", +--~ [ 1] = "monospacedTextSelector", +--~ [ 2] = "halfWidthTextSelector", +--~ [ 3] = "normallySpacedTextSelector", +--~ }, +--~ [23] = { +--~ name = "transliterationType", +--~ [ 0] = "noTransliterationSelector", +--~ [ 1] = "hanjaToHangulSelector", +--~ [ 2] = "hiraganaToKatakanaSelector", +--~ [ 3] = "katakanaToHiraganaSelector", +--~ [ 4] = "kanaToRomanizationSelector", +--~ [ 5] = "romanizationToHiraganaSelector", +--~ [ 6] = "romanizationToKatakanaSelector", +--~ [ 7] = "hanjaToHangulAltOneSelector", +--~ [ 8] = "hanjaToHangulAltTwoSelector", +--~ [ 9] = "hanjaToHangulAltThreeSelector", +--~ }, +--~ [24] = { +--~ name = "annotationType", +--~ [ 0] = "noAnnotationSelector", +--~ [ 1] = "boxAnnotationSelector", +--~ [ 2] = "roundedBoxAnnotationSelector", +--~ [ 3] = "circleAnnotationSelector", +--~ [ 4] = "invertedCircleAnnotationSelector", +--~ [ 5] = "parenthesisAnnotationSelector", +--~ [ 6] = "periodAnnotationSelector", +--~ [ 7] = "romanNumeralAnnotationSelector", +--~ [ 8] = "diamondAnnotationSelector", +--~ }, +--~ [25] = { +--~ name = "kanaSpacingType", +--~ [ 0] = "fullWidthKanaSelector", +--~ [ 1] = "proportionalKanaSelector", +--~ }, +--~ [26] = { +--~ name = "ideographicSpacingType", +--~ [ 0] = "fullWidthIdeographsSelector", +--~ [ 1] = "proportionalIdeographsSelector", +--~ }, +--~ [103] = { +--~ name = "cjkRomanSpacingType", +--~ [ 0] = "halfWidthCJKRomanSelector", +--~ [ 1] = "proportionalCJKRomanSelector", +--~ [ 2] = "defaultCJKRomanSelector", +--~ [ 3] = "fullWidthCJKRomanSelector", +--~ }, +--~ } diff --git a/tex/context/base/font-pat.lua b/tex/context/base/font-pat.lua new file mode 100644 index 000000000..6aba4d47e --- /dev/null +++ b/tex/context/base/font-pat.lua @@ -0,0 +1,126 @@ +if not modules then modules = { } end modules ['font-pat'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local match, lower, find = string.match, string.lower, string.find + +local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) + +-- this will become a per font patch file +-- +-- older versions of latin modern didn't have the designsize set +-- so for them we get it from the name + +local patches = fonts.otf.enhancers.patches + +local function patch(data,filename) + if data.design_size == 0 then + local ds = match(file.basename(lower(filename)),"(%d+)") + if ds then + if trace_loading then + logs.report("load otf","patching design size (%s)",ds) + end + data.design_size = tonumber(ds) * 10 + end + end + local uni_to_ind = data.map.map + if not uni_to_ind[0x391] then + -- beware, this is a hack, features for latin often don't apply to greek + -- but lm has not much features anyway (and only greek for math) + if trace_loading then + logs.report("load otf","adding 13 greek capitals") + end + uni_to_ind[0x391] = uni_to_ind[0x41] + uni_to_ind[0x392] = uni_to_ind[0x42] + uni_to_ind[0x395] = uni_to_ind[0x45] + uni_to_ind[0x397] = uni_to_ind[0x48] + uni_to_ind[0x399] = uni_to_ind[0x49] + uni_to_ind[0x39A] = uni_to_ind[0x4B] + uni_to_ind[0x39C] = uni_to_ind[0x4D] + uni_to_ind[0x39D] = uni_to_ind[0x4E] + uni_to_ind[0x39F] = uni_to_ind[0x4F] + uni_to_ind[0x3A1] = uni_to_ind[0x52] + uni_to_ind[0x3A4] = uni_to_ind[0x54] + uni_to_ind[0x3A7] = uni_to_ind[0x58] + uni_to_ind[0x396] = uni_to_ind[0x5A] + end + -- better make this into a feature + -- + -- local glyphs = data.glyphs + -- for i=0x300,0x36F do + -- local c = glyphs[uni_to_ind[i]] + -- if c and c.width == 0 then + -- local boundingbox = c.boundingbox + -- c.width = boundingbox[3] - boundingbox[1] + -- end + -- end +end + +patches["^lmroman"] = patch +patches["^lmsans"] = patch +patches["^lmtypewriter"] = patch + +-- for some reason (either it's a bug in the font, or it's +-- a problem in the library) the palatino arabic fonts don't +-- have the mkmk features properly set up + +local function patch(data,filename) + local gpos = data.gpos + if gpos then + for k=1,#gpos do + local v = gpos[k] + if not v.features and v.type == "gpos_mark2mark" then + if trace_loading then + logs.report("load otf","patching mkmk feature (name: %s)", v.name or "?") + end + v.features = { + { + scripts = { + { + langs = { "ARA ", "FAR ", "URD ", "dflt" }, + script = "arab", + }, + }, + tag = "mkmk" + } + } + end + end + end +end + +patches["palatino.*arabic"] = patch + +local function patch_domh(data,filename,threshold) + local m = data.math + if m then + local d = m.DisplayOperatorMinHeight or 0 + if d < threshold then + if trace_loading then + logs.report("load otf","patching DisplayOperatorMinHeight(%s -> %s)",d,threshold) + end + m.DisplayOperatorMinHeight = threshold + end + end + if tex.luatexversion < 48 then + for _, g in next, data.glyphs do + local name = g.name + if find(name,"^integral$") or find(name,"^integral%.vsize") then + local width, italic = g.width or 0, g.italic_correction or 0 + local newwidth = width - italic + if trace_loading then + logs.report("load otf","patching width of %s: %s (width) - %s (italic) = %s",name,width,italic,newwidth) + end + g.width = newwidth + end + end + end +end + +patches["cambria"] = function(data,filename) patch_domh(data,filename,2800) end +patches["cambmath"] = function(data,filename) patch_domh(data,filename,2800) end +patches["asana"] = function(data,filename) patch_domh(data,filename,1350) end diff --git a/tex/context/base/font-run.mkii b/tex/context/base/font-run.mkii new file mode 100644 index 000000000..0a0ddd057 --- /dev/null +++ b/tex/context/base/font-run.mkii @@ -0,0 +1,326 @@ +%D \module +%D [ file=font-run, +%D version=1998.09.11, % (second) +%D version=2001.02.20, % (third) +%D title=\CONTEXT\ Font Macros, +%D subtitle=Runtime Macros, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D [This code is hooked into the core macros and saves some +%D format space.] + +\unprotect + +\gdef\@@onlyenglish#1% + {\blank{\tttf \type {#1} is only available in the english interface}\blank} + +\gdef\dosetshowfonttitle#1% + {\tabskip\zeropoint + \parindent\zeropoint + \setlocalhsize + \doifelsenothing{#1} + {\def\title{[\the\bodyfontsize]}} + {\switchtobodyfont[#1]\def\title{[#1]}} + \doifsomething\fontclass + {\doifnot{[\fontclass]}\title + {\edef\title{[\fontclass]\space\title}}}} + +\gdef\showbodyfont + {\dosingleempty\doshowbodyfont} + +\gdef\doshowbodyfont[#1]% + {\ifinsidefloat\else\startbaselinecorrection\fi + \vbox + {\dosetshowfonttitle{#1}% + \def\bigstrut##1##2% + {\hbox{\vrule + \!!height##1\strutht + \!!depth ##2\strutdp + \!!width \zeropoint}} + \def\next##1##2##3% + {\tf##3\sc##3% + \sl##3\it##3\bf##3\bs##3\bi##3% + \tfx##3\tfxx##3% + \tfa##3\tfb##3\tfc##3\tfd##3&\cr}% + \halign to \localhsize + {\bigstrut{1.5}{2}##&\vrule## + \tabskip=\!!zeropoint \!!plus 1fill + &\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule## + &\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule## + &\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule## + &\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule## + &\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule## + &\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule## + &\hfil##\hfil&\vrule##&\hfil##\hfil#\vrule + \tabskip=\!!zeropoint\cr + \noalign{\hrule} + &\multispan{29}{\vrule\hfil\tttf\strut\title\hfil + \llap{\string\mr\hbox to 1em{\hss:\hss}$\mr \languageparameter\c!text$\quad}\vrule}\cr + \noalign{\hrule}\next{}{\tt\string}{} + \noalign{\hrule}\next{\tt\string\rm}\rm{\languageparameter\c!text} + \noalign{\hrule}\next{\tt\string\ss}\ss{\languageparameter\c!text} + \noalign{\hrule}\next{\tt\string\tt}\tt{\languageparameter\c!text} + \noalign{\hrule}}} + \ifinsidefloat\else\stopbaselinecorrection\fi} + +\gdef\showbodyfontenvironment + {\dosingleempty\doshowbodyfontenvironment} + +\gdef\doshowbodyfontenvironment[#1]% + {\ifinsidefloat\else\startbaselinecorrection\fi + \vbox + {\dosetshowfonttitle{#1}% + \def\next##1% + {&&\getvalue{\bodyfontvariable{##1\s!text}}% + &&\getvalue{\bodyfontvariable{##1\s!script}}% + &&\getvalue{\bodyfontvariable{##1\s!scriptscript}}% + &&\getvalue{\bodyfontvariable{##1\c!x}}% + &&\getvalue{\bodyfontvariable{##1\c!xx}}% + &&\getvalue{\bodyfontvariable{##1\interfaced\v!small}}% + &&\getvalue{\bodyfontvariable{##1\interfaced\v!big}}% + &&\edef\next{\getvalue{\bodyfontvariable{##1\c!interlinespace}}}\ifx\next\empty not set\else\next\fi&\cr + \noalign{\hrule}} + \halign to \localhsize + {##&\vrule##\strut + \tabskip=\!!zeropoint \!!plus 1fill + &\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule## + &\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule## + &\hfil##\hfil&\vrule##&\hfil##\hfil#\vrule + \tabskip\zeropoint\cr + \noalign{\hrule} + &\multispan{17}{\vrule\hfil\tttf\strut\title\hfil}\vrule\cr + \noalign{\hrule} + &&\tttf\tx\s!text&&\tttf\tx\s!script&&\tttf\tx\s!scriptscript + &&\tttf\tx\c!x&&\tttf\tx\c!xx&&\tttf\tx\v!small&&\tttf\tx\v!big + &&\tttf\tx\c!interlinespace&\cr + \noalign{\hrule} + \@EA\globalprocesscommalist\@EA[\bodyfontenvironmentlist]\next}} + \ifinsidefloat\else\stopbaselinecorrection\fi} + +\gdef\showfont + {\dodoubleempty\doshowfont} + +\gdef\doshowfont[#1][#2]% + {\bgroup + \boxrulewidth=.1pt + \setupcolors[\c!state=\v!local]% + \iffirstargument + \definefont[\s!dummy][#1]\dummy + \else\ifdim2.5em>.05\hsize \tx + \ifdim2.5em>.05\hsize \txx + \fi\fi\fi + \ifinsidefloat\else\startbaselinecorrection\fi + \vbox\bgroup + \forgetall + \startoverlay + {\vbox + {\tf \setstrut \dummy + \localcolortrue \offinterlineskip + \dostepwiserecurse{0}{15}{1} + {\let\row\recurselevel + \hbox + {\red + \dostepwiserecurse{0}{15}{1} + {\let\col\recurselevel + \@EA\scratchcounter\normaldblquote\hexnumber\row\hexnumber\col\relax + \edef\rowcol{\the\scratchcounter}% + \iffontchar\font\scratchcounter + \setbox\scratchbox\ruledhbox{\black\char\scratchcounter}% + \ifdim\wd\scratchbox>\zeropoint + \donetrue \else \donefalse + \fi + \else + \setbox\scratchbox\hbox{\gray\vrule\!!width1ex\!!height.5ex\!!depth.5ex}% + \fi + \startoverlay + {\tf\vbox to 2.5em + {\vss + \hbox to 2.5em + {\dummy\ifdim\interwordspace=\zeropoint + \setbox\scratchbox\hbox + {\raise.5\dp\scratchbox\hbox + {\lower.5\ht\scratchbox\copy\scratchbox}}% + \ht\scratchbox\zeropoint + \dp\scratchbox\zeropoint + \else + \ht\scratchbox\strutht + \dp\scratchbox\strutdp + \strut + \fi + \hss\copy\scratchbox\hss} + \vss}} + {\tf\vbox to 2.3em + {\ifdone + \hsize2.4em + \blue + \edef\theshowfontspecs + {\ifnum\hyphenchar\font=\rowcol hyph\else + \ifnum\skewchar \font=\rowcol skew\fi\fi}% + \tinyfont % after \edef, else wrong font analyzed + \doifnot\v!unknown{#2}{{\red\theshowfontspecs}}\hfill\number\rowcol + \vfill + \octnumber\rowcol\hfill\lchexnumbers\rowcol + \par + \fi}} + \stopoverlay}}} + \par}} + {\tf\basegrid[\c!nx=16,\c!ny=16,\c!dx=2.5,\c!dy=2.5,\c!unit=em]} + \stopoverlay + \nointerlineskip + \vskip2pt + % the \noexpand before \blank is needed for non etex + \edef\ascii{name: {\noexpand\black\fontname\font}} + \def\do##1##2{\ifx##1\undefined\else\ifx##1\empty\else##2\fi\fi}% + \doifelse\v!unknown{#2} + {\edef\theshowfontspecs + {name: {\noexpand\black\fontname\font}}} + {\edef\theshowfontspecs + {name: {\noexpand\black\fontname\font}\noexpand\quad + \do\currentencoding{encoding: {\noexpand\black\currentencoding\noexpand\quad}}% + \do\currentmapping {mapping: {\noexpand\black\currentmapping \noexpand\quad}}% + \do\fonthandling {handling: {\noexpand\black\fonthandling }}}}% + \tf % also sets em + \hbox to 40em{\blue\hfill\tinyfont\setstrut\strut\theshowfontspecs} + \egroup + \ifinsidefloat\else\stopbaselinecorrection\fi + \egroup} + +\gdef\showfontstyle + {\dotripleempty\doshowfontstyle} + +\gdef\doshowfontstyle[#1][#2][#3]% + {\ifthirdargument + \bgroup + \raggedright + \switchtobodyfont[#1] + \def\dodocommand##1##2% + {\starttextrule{\tttf #1 ##1 ##2} + \nobreak \getvalue{##2}% + \dorecurse{255} + {\iffontchar\font\recurselevel\relax + \char\recurselevel\relax\space + \fi} + \par \nobreak + \stoptextrule} + \def\docommand##1% + {\getvalue{##1}\processcommacommand[#3]{\dodocommand{##1}}} + \processcommalist[#2]\docommand + \egroup + \else\ifsecondargument + \showfontstyle[#1][#2][\fontalternativelist]% math is gone + \else + \showfontstyle[#1][\c!rm]\showfontstyle[#1][\c!ss] + \showfontstyle[#1][\c!tt]\showfontstyle[#1][\c!mm] + \fi\fi} + +\gdef\showligature#1% + {\hbox{\type{#1}\enspace\red\ruledhbox{\black#1}}} + +\gdef\showligatures[#1]% + {\ifx\starttabulate\undefined + \@@onlyenglish\showligatures + \else + \bgroup + \setupcolors[\c!state=\v!local]% + \def\show##1{\hbox{\red\ruledhbox{\black##1}}}% + \definefont[\s!dummy][#1]\dummy + \starttabulate[|*{9}{c|}] + \NC\type{ff}\NC\type{fi}\NC\type{fl}\NC\type{ffi}\NC\type{ffl}\NC + \type{``}\NC\type{''}\NC\type{--}\NC\type{---}\NC\NR + \NC\show{ff}\NC\show{fi}\NC\show{fl}\NC\show{ffi}\NC\show{ffl}\NC + \show{``}\NC\show{''}\NC\show{--}\NC\show{---}\NC\NR + \stoptabulate + \egroup + \fi} + +\gdef\showfontstrip + {\dosingleempty\doshowfontstrip} + +\gdef\doshowfontstrip[#1]% + {\ifx\starttabulate\undefined + \@@onlyenglish\showfontstrip + \else + \bgroup + \def\dofontstripa##1##2% + {\tttf\string##1} + \def\dofontstripb##1##2% + {\ruledhbox{\switchtobodyfont[#1]##1{##2}}} + \def\dofontstripc##1##2% + {\setbox\scratchbox\hbox{\switchtobodyfont[#1]##1{##2}}% + \tt\tfx\the\ht\scratchbox}% + \def\dofontstripd##1##2% + {\setbox\scratchbox\hbox{\switchtobodyfont[#1]##1{##2}}% + \tt\tfx\the\wd\scratchbox}% + \def\fontstrip##1##2##3% + {\NC##2\rm{##3}\NC + ##2\ss{##3}\NC + ##2\tt{##3}\NC + ##2\mathematics{##3}\NC + \tttf##1\NR} + \starttabulate[|c|c|c|c|c|] + \fontstrip\relax\dofontstripa\empty + \TB + \fontstrip\relax\dofontstripb{xxxx} + \fontstrip\relax\dofontstripb{12345} + \fontstrip\relax\dofontstripb{(Agw)} + \TB + \fontstrip{(x height)}\dofontstripc{x} + \fontstrip{(m width)}\dofontstripd{m} + \stoptabulate + \egroup + \fi} + +\ifx\databox\undefined \newbox\databox \fi + +\gdef\testminimalbaseline#1% + {\setbox\databox\ruledhbox{#1}% + \scratchdimen\ht\databox + \advance\scratchdimen\dp\databox + \scratchtoks{#1}% + \expanded + {\NC \ruledhbox{\the\scratchtoks} + \noexpand \NC -> + \noexpand \NC \the\scratchdimen + \noexpand \NC = + \noexpand \NC \the\ht\databox + \noexpand \NC + + \noexpand \NC \the\dp\databox + \noexpand \NC \ifdim\scratchdimen<\baselineskip < + \else\ifdim\scratchdimen=\baselineskip = + \else > \fi\fi + \noexpand \NC \the\baselineskip + \noexpand \NC (\ifdim\scratchdimen>\baselineskip not \fi ok) + \noexpand \NC \noexpand \NR }} + +\gdef\showminimalbaseline + {\ifx\starttabulate\undefined + \@@onlyenglish\showminimalbaseline + \else + \starttabulate[||T|T|T|T|T|T|T|T|T|] + \testminimalbaseline{\hbox to 1em{\hss\showstruts\strut\hss}}% + \testminimalbaseline{(/)}% + \testminimalbaseline{$\frac{1}{2}x^2_3$} + \stoptabulate + \fi} + +\gdef\showkerning#1% + {\bgroup + \let\MPfshowcommand\ruledhbox + \setMPtext\s!dummy{#1}% + \startMPcode draw textext(\MPstring\s!dummy);\stopMPcode + \egroup} + +\gdef\showcharratio + {\dowithnextboxcontent + {\switchtobodyfont[10pt]}% + {(\expanded{\withoutpt\the\nextboxht},% + \expanded{\withoutpt\the\nextboxdp})}% + \hbox} + +\protect \endinput diff --git a/tex/context/base/font-run.mkiv b/tex/context/base/font-run.mkiv new file mode 100644 index 000000000..aad6bebe8 --- /dev/null +++ b/tex/context/base/font-run.mkiv @@ -0,0 +1,337 @@ +%D \module +%D [ file=font-run, +%D version=1998.09.11, % (second) +%D version=2001.02.20, % (third) +%D title=\CONTEXT\ Font Macros, +%D subtitle=Runtime Macros, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D [This code is hooked into the core macros and saves some +%D format space.] + +\unprotect + +\gdef\@@onlyenglish#1% + {\blank{\tttf \type {#1} is only available in the english interface}\blank} + +\gdef\dosetshowfonttitle#1% + {\tabskip\zeropoint + \parindent\zeropoint + \setlocalhsize + \doifelsenothing{#1} + {\def\title{[\the\bodyfontsize]}} + {\switchtobodyfont[#1]\def\title{[#1]}} + \doifsomething\fontclass + {\doifnot{[\fontclass]}\title + {\edef\title{[\fontclass]\space\title}}}} + +\gdef\showbodyfont + {\dosingleempty\doshowbodyfont} + +\gdef\doshowbodyfont[#1]% + {\ifinsidefloat\else\startbaselinecorrection\fi + \vbox + {\dosetshowfonttitle{#1}% + \def\bigstrut##1##2% + {\hbox{\vrule + \!!height##1\strutht + \!!depth ##2\strutdp + \!!width \zeropoint}} + \def\next##1##2##3% + {\tf##3\sc##3% + \sl##3\it##3\bf##3\bs##3\bi##3% + \tfx##3\tfxx##3% + \tfa##3\tfb##3\tfc##3\tfd##3&\cr}% + \halign to \localhsize + {\bigstrut{1.5}{2}##&\vrule## + \tabskip=\!!zeropoint \!!plus 1fill + &\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule## + &\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule## + &\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule## + &\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule## + &\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule## + &\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule## + &\hfil##\hfil&\vrule##&\hfil##\hfil#\vrule + \tabskip=\!!zeropoint\cr + \noalign{\hrule} + &\multispan{29}{\vrule\hfil\tttf\strut\title\hfil + \llap{\string\mr\hbox to 1em{\hss:\hss}$\mr \languageparameter\c!text$\quad}\vrule}\cr + \noalign{\hrule}\next{}{\tt\string}{} + \noalign{\hrule}\next{\tt\string\rm}\rm{\languageparameter\c!text} + \noalign{\hrule}\next{\tt\string\ss}\ss{\languageparameter\c!text} + \noalign{\hrule}\next{\tt\string\tt}\tt{\languageparameter\c!text} + \noalign{\hrule}}} + \ifinsidefloat\else\stopbaselinecorrection\fi} + +\gdef\showbodyfontenvironment + {\dosingleempty\doshowbodyfontenvironment} + +\gdef\doshowbodyfontenvironment[#1]% + {\ifinsidefloat\else\startbaselinecorrection\fi + \vbox + {\dosetshowfonttitle{#1}% + \def\next##1% + {&&\getvalue{\bodyfontvariable{##1\s!text}}% + &&\getvalue{\bodyfontvariable{##1\s!script}}% + &&\getvalue{\bodyfontvariable{##1\s!scriptscript}}% + &&\getvalue{\bodyfontvariable{##1\c!x}}% + &&\getvalue{\bodyfontvariable{##1\c!xx}}% + &&\getvalue{\bodyfontvariable{##1\interfaced\v!small}}% + &&\getvalue{\bodyfontvariable{##1\interfaced\v!big}}% + &&\edef\next{\getvalue{\bodyfontvariable{##1\c!interlinespace}}}\ifx\next\empty not set\else\next\fi&\cr + \noalign{\hrule}} + \halign to \localhsize + {##&\vrule##\strut + \tabskip=\!!zeropoint \!!plus 1fill + &\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule## + &\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule##&\hfil##\hfil&\vrule## + &\hfil##\hfil&\vrule##&\hfil##\hfil#\vrule + \tabskip\zeropoint\cr + \noalign{\hrule} + &\multispan{17}{\vrule\hfil\tttf\strut\title\hfil}\vrule\cr + \noalign{\hrule} + &&\tttf\tx\s!text&&\tttf\tx\s!script&&\tttf\tx\s!scriptscript + &&\tttf\tx\c!x&&\tttf\tx\c!xx&&\tttf\tx\v!small&&\tttf\tx\v!big + &&\tttf\tx\c!interlinespace&\cr + \noalign{\hrule} + \@EA\globalprocesscommalist\@EA[\bodyfontenvironmentlist]\next}} + \ifinsidefloat\else\stopbaselinecorrection\fi} + +\gdef\showfont + {\dodoubleempty\doshowfont} + +\gdef\doshowfont[#1][#2]% + {\bgroup + \iffirstargument + \definefont[\s!dummy][#1]\dummy + \fi + \doifelsenothing{#2} + {\dodoshowfont{#1}{0}} + {\doifelse{#2}\v!all + {\dostepwiserecurse{0}{255}{1} + {\donefalse + \let\charplane\recurselevel + \dostepwiserecurse{0}{255}{1} + {\iffontchar\font\numexpr\charplane*256+\recurselevel\relax + \donetrue + \exitloop + \fi}% + \ifdone + \dodoshowfont{#1}\charplane + \fi}} + {\processcommalist[#2]{\dodoshowfont{#1}}}}% + \egroup} + +\gdef\dodoshowfont#1#2% + {\bgroup + \edef\charplane{\number#2}% + \boxrulewidth=.1pt + \iffirstargument +% \definefont[\s!dummy][#1]\dummy + \else\ifdim2.5em>.05\hsize \tx + \ifdim2.5em>.05\hsize \txx + \fi\fi\fi + \ifinsidefloat\else\startbaselinecorrection\fi + \vbox\bgroup + \forgetall + \startoverlay + {\vbox + {\tf \setstrut \dummy + \offinterlineskip + \dostepwiserecurse{0}{15}{1} + {\let\row\recurselevel + \hbox + {\red + \dostepwiserecurse{0}{15}{1} + {\let\col\recurselevel + \@EA\scratchcounter\string"\hexnumber\row\hexnumber\col\relax + \edef\rowcol{\the\scratchcounter}% + \iffontchar\font\scratchcounter + \setbox\scratchbox\ruledhbox{\black\char\numexpr\charplane*256+\scratchcounter\relax}% + \ifdim\wd\scratchbox>\zeropoint + \donetrue \else \donefalse + \fi + \else + \setbox\scratchbox\hbox{\gray\vrule\!!width1ex\!!height.5ex\!!depth.5ex}% + \fi + \startoverlay + {\tf\vbox to 2.5em + {\vss + \hbox to 2.5em + {\dummy\ifdim\interwordspace=\zeropoint + \setbox\scratchbox\hbox + {\raise.5\dp\scratchbox\hbox + {\lower.5\ht\scratchbox\copy\scratchbox}}% + \ht\scratchbox\zeropoint + \dp\scratchbox\zeropoint + \else + \ht\scratchbox\strutht + \dp\scratchbox\strutdp + \strut + \fi + \hss\copy\scratchbox\hss} + \vss}} + {\tf\vbox to 2.3em + {\ifdone + \hsize2.4em + \blue + \tinyfont + \hfill\number\rowcol + \vfill + \octnumber\rowcol\hfill\lchexnumbers\rowcol + \par + \fi}} + \stopoverlay}}} + \par}} + {\tf\basegrid[\c!nx=16,\c!ny=16,\c!dx=2.5,\c!dy=2.5,\c!unit=em]} + \stopoverlay + \nointerlineskip + \vskip2pt + % the \noexpand before \blank is needed for non etex + \edef\ascii{name: {\noexpand\black\fontname\font}} + \def\do##1##2{\ifx##1\undefined\else\ifx##1\empty\else##2\fi\fi}% + \edef\theshowfontspecs{name: {\noexpand\black\fontname\font}\quad plane: \charplane\enspace"\hexnumber\charplane}% + \tf % also sets em + \hbox to 40em{\blue\hfill\tinyfont\setstrut\strut\theshowfontspecs} + \egroup + \ifinsidefloat\else\stopbaselinecorrection\fi + \egroup} + +\gdef\showfontstyle + {\dotripleempty\doshowfontstyle} + +\gdef\doshowfontstyle[#1][#2][#3]% + {\ifthirdargument + \bgroup + \raggedright + \switchtobodyfont[#1] + \def\dodocommand##1##2% + {\starttextrule{\tttf #1 ##1 ##2} + \nobreak \getvalue{##2}% + \dorecurse{255} + {\iffontchar\font\recurselevel\relax + \char\recurselevel\relax\space + \fi} + \par \nobreak + \stoptextrule} + \def\docommand##1% + {\getvalue{##1}\processcommacommand[#3]{\dodocommand{##1}}} + \processcommalist[#2]\docommand + \egroup + \else\ifsecondargument + \showfontstyle[#1][#2][\fontalternativelist]% math is gone + \else + \showfontstyle[#1][\c!rm]\showfontstyle[#1][\c!ss] + \showfontstyle[#1][\c!tt]\showfontstyle[#1][\c!mm] + \fi\fi} + +\gdef\showligature#1% + {\hbox{\type{#1}\enspace\red\ruledhbox{\black#1}}} + +\gdef\showligatures[#1]% + {\ifx\starttabulate\undefined + \@@onlyenglish\showligatures + \else + \bgroup + \def\show##1{\hbox{\red\ruledhbox{\black##1}}}% + \definefont[\s!dummy][#1]\dummy + \starttabulate[|*{9}{c|}] + \NC\type{ff}\NC\type{fi}\NC\type{fl}\NC\type{ffi}\NC\type{ffl}\NC + \type{``}\NC\type{''}\NC\type{--}\NC\type{---}\NC\NR + \NC\show{ff}\NC\show{fi}\NC\show{fl}\NC\show{ffi}\NC\show{ffl}\NC + \show{``}\NC\show{''}\NC\show{--}\NC\show{---}\NC\NR + \stoptabulate + \egroup + \fi} + +\gdef\showfontstrip + {\dosingleempty\doshowfontstrip} + +\gdef\doshowfontstrip[#1]% + {\ifx\starttabulate\undefined + \@@onlyenglish\showfontstrip + \else + \bgroup + \def\dofontstripa##1##2% + {\tttf\string##1} + \def\dofontstripb##1##2% + {\ruledhbox{\switchtobodyfont[#1]##1{##2}}} + \def\dofontstripc##1##2% + {\setbox\scratchbox\hbox{\switchtobodyfont[#1]##1{##2}}% + \tt\tfx\the\ht\scratchbox}% + \def\dofontstripd##1##2% + {\setbox\scratchbox\hbox{\switchtobodyfont[#1]##1{##2}}% + \tt\tfx\the\wd\scratchbox}% + \def\fontstrip##1##2##3% + {\NC##2\rm{##3}\NC + ##2\ss{##3}\NC + ##2\tt{##3}\NC + ##2\mathematics{##3}\NC + \tttf##1\NR} + \starttabulate[|c|c|c|c|c|] + \fontstrip\relax\dofontstripa\empty + \TB + \fontstrip\relax\dofontstripb{xxxx} + \fontstrip\relax\dofontstripb{12345} + \fontstrip\relax\dofontstripb{(Agw)} + \TB + \fontstrip{(x height)}\dofontstripc{x} + \fontstrip{(m width)}\dofontstripd{m} + \stoptabulate + \egroup + \fi} + +\ifx\databox\undefined \newbox\databox \fi + +\gdef\testminimalbaseline#1% + {\setbox\databox\ruledhbox{#1}% + \scratchdimen\ht\databox + \advance\scratchdimen\dp\databox + \scratchtoks{#1}% + \expanded + {\NC \ruledhbox{\the\scratchtoks} + \noexpand \NC -> + \noexpand \NC \the\scratchdimen + \noexpand \NC = + \noexpand \NC \the\ht\databox + \noexpand \NC + + \noexpand \NC \the\dp\databox + \noexpand \NC \ifdim\scratchdimen<\baselineskip < + \else\ifdim\scratchdimen=\baselineskip = + \else > \fi\fi + \noexpand \NC \the\baselineskip + \noexpand \NC (\ifdim\scratchdimen>\baselineskip not \fi ok) + \noexpand \NC \noexpand \NR }} + +\gdef\showminimalbaseline + {\ifx\starttabulate\undefined + \@@onlyenglish\showminimalbaseline + \else + \starttabulate[||T|T|T|T|T|T|T|T|T|] + \testminimalbaseline{\hbox to 1em{\hss\showstruts\strut\hss}}% + \testminimalbaseline{(/)}% + \testminimalbaseline{$\frac{1}{2}x^2_3$} + \stoptabulate + \fi} + +\gdef\showkerning#1% + {\bgroup + \let\MPfshowcommand\ruledhbox + \setMPtext\s!dummy{#1}% + \startMPcode draw textext(\MPstring\s!dummy);\stopMPcode + \egroup} + +\gdef\showcharratio + {\dowithnextboxcontent + {\switchtobodyfont[10pt]}% + {(\expanded{\withoutpt\the\nextboxht},% + \expanded{\withoutpt\the\nextboxdp})}% + \hbox} + +\protect \endinput diff --git a/tex/context/base/font-syn.lua b/tex/context/base/font-syn.lua new file mode 100644 index 000000000..5ad92e002 --- /dev/null +++ b/tex/context/base/font-syn.lua @@ -0,0 +1,1454 @@ +if not modules then modules = { } end modules ['font-syn'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- todo: subs in lookups requests + +local utf = unicode.utf8 +local next, tonumber = next, tonumber +local gsub, lower, match, find, lower, upper = string.gsub, string.lower, string.match, string.find, string.lower, string.upper +local find, gmatch = string.find, string.gmatch +local concat, sort, format = table.concat, table.sort, string.format +local lpegmatch = lpeg.match +local utfgsub, utflower = utf.gsub, utf.lower +local unpack = unpack or table.unpack + +local trace_names = false trackers.register("fonts.names", function(v) trace_names = v end) +local trace_warnings = false trackers.register("fonts.warnings", function(v) trace_warnings = v end) + +--[[ldx-- +This module implements a name to filename resolver. Names are resolved +using a table that has keys filtered from the font related files.
+--ldx]]-- + +local texsprint = (tex and tex.sprint) or print + +fonts = fonts or { } +input = input or { } +texmf = texmf or { } + +fonts.names = fonts.names or { } +fonts.names.filters = fonts.names.filters or { } +fonts.names.data = fonts.names.data or { } + +local names = fonts.names +local filters = fonts.names.filters + +names.version = 1.103 +names.basename = "names" +names.saved = false +names.loaded = false +names.be_clever = true +names.enabled = true +names.autoreload = toboolean(os.env['MTX.FONTS.AUTOLOAD'] or os.env['MTX_FONTS_AUTOLOAD'] or "no") +names.cache = containers.define("fonts","data",names.version,true) + +--[[ldx-- +A few helpers.
+--ldx]]-- + +local P, C, Cc, Cs, Carg = lpeg.P, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Carg + +-- what to do with 'thin' + +local weights = Cs ( -- not extra + P("demibold") + + P("semibold") + + P("mediumbold") + + P("ultrabold") + + P("extrabold") + + P("ultralight") + + P("bold") + + P("demi") + + P("semi") + + P("light") + + P("medium") + + P("heavy") + + P("ultra") + + P("black") + + P("bol") -- / "bold" + + P("regular") / "normal" +) + +local styles = Cs ( + P("reverseoblique") / "reverseitalic" + + P("regular") / "normal" + + P("italic") + + P("oblique") / "italic" + + P("slanted") + + P("roman") / "normal" + + P("ital") / "italic" + + P("ita") / "italic" +) + +local widths = Cs( + P("condensed") + + P("thin") + + P("expanded") + + P("cond") / "condensed" + + P("normal") + + P("book") / "normal" +) + +local variants = Cs( -- fax casual + P("smallcaps") + + P("oldstyle") + + P("caps") / "smallcaps" +) + +local any = P(1) + +local analysed_table + +local analyser = Cs ( + ( + weights / function(s) analysed_table[1] = s return "" end + + styles / function(s) analysed_table[2] = s return "" end + + widths / function(s) analysed_table[3] = s return "" end + + variants / function(s) analysed_table[4] = s return "" end + + any + )^0 +) + +local splitter = lpeg.splitat("-") + +function names.splitspec(askedname) + local name, weight, style, width, variant = lpegmatch(splitter,askedname) + weight = weight and lpegmatch(weights, weight) or weight + style = style and lpegmatch(styles, style) or style + width = width and lpegmatch(widths, width) or width + variant = variant and lpegmatch(variants,variant) or variant + if trace_names then + logs.report("fonts","requested name '%s' split in name '%s', weight '%s', style '%s', width '%s' and variant '%s'", + askedname,name or '',weight or '',style or '',width or '',variant or '') + end + if not weight or not weight or not width or not variant then + weight, style, width, variant = weight or "normal", style or "normal", width or "normal", variant or "normal" + if trace_names then + logs.report("fonts","request '%s' normalized to '%s-%s-%s-%s-%s'", + askedname,name,weight,style,width,variant) + end + end + return name or askedname, weight, style, width, variant +end + +local function analysespec(somename) + if somename then + analysed_table = { } + local name = lpegmatch(analyser,somename) + return name, analysed_table[1], analysed_table[2], analysed_table[3], analysed_table[4] + end +end + +--[[ldx-- +It would make sense to implement the filters in the related modules, +but to keep the overview, we define them here.
+--ldx]]-- + +filters.otf = fontloader.info +filters.ttf = fontloader.info +filters.ttc = fontloader.info +filters.dfont = fontloader.info + +function fontloader.fullinfo(...) + local ff = fontloader.open(...) + if ff then + local d = ff and fontloader.to_table(ff) + d.glyphs, d.subfonts, d.gpos, d.gsub, d.lookups = nil, nil, nil, nil, nil + fontloader.close(ff) + return d + else + return nil, "error in loading font" + end +end + +filters.otf = fontloader.fullinfo + +function filters.afm(name) + -- we could parse the afm file as well, and then report an error but + -- it's not worth the trouble + local pfbname = resolvers.find_file(file.removesuffix(name)..".pfb","pfb") or "" + if pfbname == "" then + pfbname = resolvers.find_file(file.removesuffix(file.basename(name))..".pfb","pfb") or "" + end + if pfbname ~= "" then + local f = io.open(name) + if f then + local hash = { } + for line in f:lines() do + local key, value = match(line,"^(.+)%s+(.+)%s*$") + if key and #key > 0 then + hash[lower(key)] = value + end + if find(line,"StartCharMetrics") then + break + end + end + f:close() + return hash + end + end + return nil, "no matching pfb file" +end + +function filters.pfb(name) + return fontloader.info(name) +end + +--[[ldx-- +The scanner loops over the filters using the information stored in +the file databases. Watch how we check not only for the names, but also +for combination with the weight of a font.
+--ldx]]-- + +filters.list = { + "otf", "ttf", "ttc", "dfont", "afm", +--~ "ttc", "otf", "ttf", "dfont", "afm", +} + +names.xml_configuration_file = "fonts.conf" -- a bit weird format, bonus feature +names.environment_path_variable = "OSFONTDIR" -- the official way, in minimals etc + +filters.paths = { } +filters.names = { } + +function names.getpaths(trace) + local hash, result = { }, { } + local function collect(t) + for i=1, #t do + local v = resolvers.clean_path(t[i]) + v = gsub(v,"/+$","") + local key = lower(v) + if not hash[key] then + hash[key], result[#result+1] = true, v + end + end + end + local path = names.environment_path_variable or "" + if path ~= "" then + collect(resolvers.expanded_path_list(path)) + end + if xml then + local confname = names.xml_configuration_file or "" + if confname ~= "" then + -- first look in the tex tree + local name = resolvers.find_file(confname,"other") + if name == "" then + -- after all, fontconfig is a unix thing + name = file.join("/etc",confname) + if not lfs.isfile(name) then + name = "" -- force quit + end + end + if name ~= "" and lfs.isfile(name) then + if trace_names then + logs.report("fontnames","loading fontconfig file: %s",name) + end + local xmldata = xml.load(name) + -- begin of untested mess + xml.include(xmldata,"include","",true,function(incname) + if not file.is_qualified_path(incname) then + local path = file.dirname(name) -- main name + if path ~= "" then + incname = file.join(path,incname) + end + end + if lfs.isfile(incname) then + if trace_names then + logs.report("fontnames","merging included fontconfig file: %s",incname) + end + return io.loaddata(incname) + elseif trace_names then + logs.report("fontnames","ignoring included fontconfig file: %s",incname) + end + end) + -- end of untested mess + local fontdirs = xml.collect_texts(xmldata,"dir",true) + if trace_names then + logs.report("fontnames","%s dirs found in fontconfig",#fontdirs) + end + collect(fontdirs) + end + end + end + function names.getpaths() + return result + end + return result +end + +local function cleanname(name) + return (gsub(lower(name),"[^%a%d]","")) + -- once we can load files with utf names, we can play with the following: + -- return (utfgsub(utfgsub(lower(str),"[^%a%A%d]",""),"%s","")) +end + +names.cleanname = cleanname + +local function check_names(result) + local names = result.names + if names then + for i=1,#names do + local name = names[i] + if name.lang == "English (US)" then + return name.names + end + end + end +end + +local function walk_tree(pathlist,suffix,identify) + if pathlist then + for i=1,#pathlist do + local path = pathlist[i] + path = resolvers.clean_path(path .. "/") + path = gsub(path,"/+","/") + local pattern = path .. "**." .. suffix -- ** forces recurse + logs.report("fontnames", "globbing path %s",pattern) + local t = dir.glob(pattern) + sort(t,sorter) + for j=1,#t do + local completename = t[j] + identify(completename,file.basename(completename),suffix,completename) + end + end + end +end + +local function check_name(data,result,filename,suffix,subfont) + -- shortcuts + local specifications = data.specifications + local families = data.families + -- prepare + local names = check_names(result) + -- fetch + local familyname = (names and names.preffamilyname) or result.familyname + local fullname = (names and names.fullname) or result.fullname + local fontname = result.fontname + local subfamily = (names and names.subfamily) + local modifiers = (names and names.prefmodifiers) + local weight = (names and names.weight) or result.weight + local italicangle = tonumber(result.italicangle) + local subfont = subfont or nil + local rawname = fullname or fontname or familyname + -- normalize + familyname = familyname and cleanname(familyname) + fullname = fullname and cleanname(fullname) + fontname = fontname and cleanname(fontname) + subfamily = subfamily and cleanname(subfamily) + modifiers = modifiers and cleanname(modifiers) + weight = weight and cleanname(weight) + italicangle = (italicangle == 0) and nil + -- analyse + local a_name, a_weight, a_style, a_width, a_variant = analysespec(fullname or fontname or familyname) + -- check + local width = a_width + local variant = a_variant + local style = modifiers and gsub(modifiers,"[^%a]","") + if not style and italicangle then + style = "italic" + end + if not variant or variant == "" then + variant = "normal" + end + if not weight or weight == "" then + weight = a_weight + end + if not style or style == "" then + style = a_style + end + if not familyname then + familyname = a_name + end + fontname = fontname or fullname or familyname or file.basename(filename) + fullname = fullname or fontname + familyname = familyname or fontname + specifications[#specifications + 1] = { + filename = filename, + format = lower(suffix), + subfont = subfont, + rawname = rawname, + familyname = familyname, + fullname = fullname, + fontname = fontname, + subfamily = subfamily, + modifiers = modifiers, + weight = weight, + style = style, + width = width, + variant = variant, + minsize = result.design_range_bottom or 0, + maxsize = result.design_range_top or 0, + designsize = result.design_size or 0, + } +end + +local function cleanupkeywords() + local data = names.data + local specifications = names.data.specifications + if specifications then + local weights, styles, widths, variants = { }, { }, { }, { } + for i=1,#specifications do + local s = specifications[i] + -- fix (sofar styles are taken from the name, and widths from the specification) + local _, b_weight, b_style, b_width, b_variant = analysespec(s.weight) + local _, c_weight, c_style, c_width, c_variant = analysespec(s.style) + local _, d_weight, d_style, d_width, d_variant = analysespec(s.width) + local _, e_weight, e_style, e_width, e_variant = analysespec(s.variant) + local _, f_weight, f_style, f_width, f_variant = analysespec(s.fullname or "") + local weight = b_weight or c_weight or d_weight or e_weight or f_weight or "normal" + local style = b_style or c_style or d_style or e_style or f_style or "normal" + local width = b_width or c_width or d_width or e_width or f_width or "normal" + local variant = b_variant or c_variant or d_variant or e_variant or f_variant or "normal" + if not weight or weight == "" then weight = "normal" end + if not style or style == "" then style = "normal" end + if not width or width == "" then width = "normal" end + if not variant or variant == "" then variant = "normal" end + weights [weight ] = (weights [weight ] or 0) + 1 + styles [style ] = (styles [style ] or 0) + 1 + widths [width ] = (widths [width ] or 0) + 1 + variants[variant] = (variants[variant] or 0) + 1 + if weight ~= s.weight then + s.fontweight = s.weight + end + s.weight, s.style, s.width, s.variant = weight, style, width, variant + end + local stats = data.statistics + stats.used_weights, stats.used_styles, stats.used_widths, stats.used_variants = weights, styles, widths, variants + end +end + +local function collectstatistics() + local data = names.data + local specifications = data.specifications + if specifications then + local weights, styles, widths, variants = { }, { }, { }, { } + for i=1,#specifications do + local s = specifications[i] + local weight, style, width, variant = s.weight, s.style, s.width, s.variant + if weight then weights [weight ] = (weights [weight ] or 0) + 1 end + if style then styles [style ] = (styles [style ] or 0) + 1 end + if width then widths [width ] = (widths [width ] or 0) + 1 end + if variant then variants[variant] = (variants[variant] or 0) + 1 end + end + local stats = data.statistics + stats.weights, stats.styles, stats.widths, stats.variants, stats.fonts = weights, styles, widths, variants, #specifications + end +end + +local function collecthashes() + local data = names.data + local mappings = data.mappings + local fallbacks = data.fallbacks + local specifications = data.specifications + local nofmappings, noffallbacks = 0, 0 + if specifications then + -- maybe multiple passes + for index=1,#specifications do + local s = specifications[index] + local format, fullname, fontname, familyname, weight, subfamily = s.format, s.fullname, s.fontname, s.familyname, s.weight, s.subfamily + local mf, ff = mappings[format], fallbacks[format] + if fullname and not mf[fullname] then + mf[fullname], nofmappings = index, nofmappings + 1 + end + if fontname and not mf[fontname] then + mf[fontname], nofmappings = index, nofmappings + 1 + end + if familyname and weight then + local madename = familyname .. weight + if not mf[madename] and not ff[madename] then + ff[madename], noffallbacks = index, noffallbacks + 1 + end + end + if familyname and subfamily then + local extraname = familyname .. subfamily + if not mf[extraname] and not ff[extraname] then + ff[extraname], noffallbacks = index, noffallbacks + 1 + end + end + if familyname then + if not mf[familyname] and not ff[familyname] then + ff[familyname], noffallbacks = index, noffallbacks + 1 + end + end + end + end + return nofmappings, noffallbacks +end + +local function collectfamilies() + local data = names.data + local specifications = data.specifications + local families = data.families + for index=1,#specifications do + local familyname = specifications[index].familyname + local family = families[familyname] + if not family then + families[familyname] = { index } + else + family[#family+1] = index + end + end +end + +local function checkduplicate(where) -- fails on "Romantik" but that's a border case anyway + local data = names.data + local mapping = data[where] + local specifications, loaded = data.specifications, { } + if specifications and mapping then + for _, m in next, mapping do + for k, v in next, m do + local s = specifications[v] + local hash = format("%s-%s-%s-%s-%s",s.familyname,s.weight or "*",s.style or "*",s.width or "*",s.variant or "*") + local h = loaded[hash] + if h then + local ok = true + local fn = s.filename + for i=1,#h do + local hn = s.filename + if h[i] == fn then + ok = false + break + end + end + if ok then + h[#h+1] = fn + end + else + loaded[hash] = { s.filename } + end + end + end + end + local n = 0 + for k, v in table.sortedhash(loaded) do + local nv = #v + if nv > 1 then + if trace_warnings then + logs.report("fontnames", "double lookup: %s => %s",k,concat(v," | ")) + end + n = n + nv + end + end + logs.report("fontnames", "%s double lookups in %s",n,where) +end + +local function checkduplicates() + checkduplicate("mappings") + checkduplicate("fallbacks") +end + +local sorter = function(a,b) + return a > b -- to be checked +end + +local function sorthashes() + local data, list = names.data, filters.list + local mappings, fallbacks, sorted_mappings, sorted_fallbacks = data.mappings, data.fallbacks, { }, { } + data.sorted_mappings, data.sorted_fallbacks = sorted_mappings, sorted_fallbacks + for i=1,#list do + local l = list[i] + sorted_mappings[l], sorted_fallbacks[l] = table.keys(mappings[l]), table.keys(fallbacks[l]) + sort(sorted_mappings[l],sorter) + sort(sorted_fallbacks[l],sorter) + end + data.sorted_families = table.keys(data.families) + sort(data.sorted_families,sorter) +end + +local function unpackreferences() + local data = names.data + local specifications = data.specifications + if specifications then + for k, v in next, data.families do + for i=1,#v do + v[i] = specifications[v[i]] + end + end + local mappings = data.mappings + if mappings then + for _, m in next, mappings do + for k, v in next, m do + m[k] = specifications[v] + end + end + end + local fallbacks = data.fallbacks + if fallbacks then + for _, f in next, fallbacks do + for k, v in next, f do + f[k] = specifications[v] + end + end + end + end +end + +local function analysefiles() + local data = names.data + local done, totalnofread, totalnofskipped = { }, 0, 0 + local skip_paths, skip_names = filters.paths, filters.names + local function identify(completename,name,suffix,storedname) + local basename = file.basename(completename) + local basepath = file.dirname(completename) + if done[name] then + -- already done (avoid otf afm clash) + elseif not io.exists(completename) then + -- weird error + elseif not file.is_qualified_path(completename) and resolvers.find_file(completename,suffix) == "" then + -- not locateble by backend anyway + else + nofread = nofread + 1 + if #skip_paths > 0 then + for i=1,#skip_paths do + if find(basepath,skip_paths[i]) then + if trace_names then + logs.report("fontnames","rejecting path of %s font %s",suffix,completename) + logs.push() + end + return + end + end + end + if #skip_names > 0 then + for i=1,#skip_paths do + if find(basename,skip_names[i]) then + done[name] = true + if trace_names then + logs.report("fontnames","rejecting name of %s font %s",suffix,completename) + logs.push() + end + return + end + end + end + if trace_names then + logs.report("fontnames","identifying %s font %s",suffix,completename) + logs.push() + end + local result, message = filters[lower(suffix)](completename) + if trace_names then + logs.pop() + end + if result then + if not result[1] then + local ok = check_name(data,result,storedname,suffix) + if not ok then + nofskipped = nofskipped + 1 + end + else + for r=1,#result do + local ok = check_name(data,result[r],storedname,suffix,r-1) -- subfonts start at zero + if not ok then + nofskipped = nofskipped + 1 + end + end + end + if trace_warnings and message and message ~= "" then + logs.report("fontnames","warning when identifying %s font %s: %s",suffix,completename,message) + end + elseif trace_warnings then + logs.report("fontnames","error when identifying %s font %s: %s",suffix,completename,message or "unknown") + end + done[name] = true + end + end + local function traverse(what, method) + local list = filters.list + for n=1,#list do + local suffix = list[n] + local t = os.gettimeofday() -- use elapser + nofread, nofskipped = 0, 0 + suffix = lower(suffix) + logs.report("fontnames", "identifying %s font files with suffix %s",what,suffix) + method(suffix) + suffix = upper(suffix) + logs.report("fontnames", "identifying %s font files with suffix %s",what,suffix) + method(suffix) + totalnofread, totalnofskipped = totalnofread + nofread, totalnofskipped + nofskipped + local elapsed = os.gettimeofday() - t + logs.report("fontnames", "%s %s files identified, %s hash entries added, runtime %0.3f seconds",nofread,what,nofread-nofskipped,elapsed) + end + end + if not trace_warnings then + logs.report("fontnames", "warnings are disabled (tracker 'fonts.warnings')") + end + traverse("tree", function(suffix) -- TEXTREE only + resolvers.with_files(".*%." .. suffix .. "$", function(method,root,path,name) + if method == "file" then + local completename = root .."/" .. path .. "/" .. name + identify(completename,name,suffix,name,name) + end + end) + end) + if texconfig.kpse_init then + -- we do this only for a stupid names run, not used for context itself, + -- using the vars is to clumsy so we just stick to a full scan instead + traverse("lsr", function(suffix) -- all trees + local pathlist = resolvers.split_path(resolvers.show_path("ls-R") or "") + walk_tree(pathlist,suffix,identify) + end) + else + traverse("system", function(suffix) -- OSFONTDIR cum suis + walk_tree(names.getpaths(trace),suffix,identify) + end) + end + data.statistics.readfiles, data.statistics.skippedfiles = totalnofread, totalnofskipped +end + +local function rejectclashes() -- just to be sure, so no explicit afm will be found then + local specifications, used, okay = names.data.specifications, { }, { } + for i=1,#specifications do + local s = specifications[i] + local f = s.fontname + if f then + local fnd, fnm = used[f], s.filename + if fnd then + if trace_warnings then + logs.report("fontnames", "fontname '%s' clashes, rejecting '%s' in favor of '%s'",f,fnm,fnd) + end + else + used[f], okay[#okay+1] = fnm, s + end + else + okay[#okay+1] = s + end + end + local d = #specifications - #okay + if d > 0 then + logs.report("fontnames", "%s files rejected due to clashes",d) + end + names.data.specifications = okay +end + +local function resetdata() + local mappings, fallbacks = { }, { } + for _, k in next, filters.list do + mappings[k], fallbacks[k] = { }, { } + end + names.data = { + version = names.version, + mappings = mappings, + fallbacks = fallbacks, + specifications = { }, + families = { }, + statistics = { }, + data_state = resolvers.data_state(), + } +end + +function names.identify() + resetdata() + analysefiles() + rejectclashes() + collectfamilies() + collectstatistics() + cleanupkeywords() + collecthashes() + checkduplicates() + -- sorthashes() -- will be resorted when saved +end + +function names.is_permitted(name) + return containers.is_usable(names.cache(), name) +end +function names.write_data(name,data) + containers.write(names.cache(),name,data) +end +function names.read_data(name) + return containers.read(names.cache(),name) +end + +function names.load(reload,verbose) + if not names.loaded then + if reload then + if names.is_permitted(names.basename) then + names.identify(verbose) + names.write_data(names.basename,names.data) + else + logs.report("font table", "unable to access database cache") + end + names.saved = true + end + local data = names.read_data(names.basename) + names.data = data + if not names.saved then + if not data or not next(data) or not data.specifications or not next(data.specifications) then + names.load(true) + end + names.saved = true + end + if not data then + logs.report("font table", "accessing the data table failed") + else + unpackreferences() + sorthashes() + end + names.loaded = true + end +end + +local function list_them(mapping,sorted,pattern,t,all) + if mapping[pattern] then + t[pattern] = mapping[pattern] + else + for k=1,#sorted do + local v = sorted[k] + if not t[v] and find(v,pattern) then + t[v] = mapping[v] + if not all then + return + end + end + end + end +end + +function names.list(pattern,reload,all) -- here? + names.load(reload) + if names.loaded then + local t = { } + local data = names.data + if data then + local list = filters.list + local mappings, sorted_mappings = data.mappings, data.sorted_mappings + local fallbacks, sorted_fallbacks = data.fallbacks, data.sorted_fallbacks + for i=1,#list do + local format = list[i] + list_them(mappings[format],sorted_mappings[format],pattern,t,all) + if next(t) and not all then + return t + end + list_them(fallbacks[format],sorted_fallbacks[format],pattern,t,all) + if next(t) and not all then + return t + end + end + end + return t + end +end + +local reloaded = false + +local function is_reloaded() + if not reloaded then + local data = names.data + if names.autoreload then + local c_status = table.serialize(resolvers.data_state()) + local f_status = table.serialize(data.data_state) + if c_status == f_status then + -- logs.report("fonts","font database matches configuration and file hashes") + return + else + logs.report("fonts","font database does not match configuration and file hashes") + end + end + names.loaded = false + reloaded = true + io.flush() + names.load(true) + end +end + +--[[ldx-- +The resolver also checks if the cached names are loaded. Being clever +here is for testing purposes only (it deals with names prefixed by an +encoding name).
+--ldx]]-- + +local function fuzzy(mapping,sorted,name,sub) + local condensed = gsub(name,"[^%a%d]","") + for k=1,#sorted do + local v = sorted[k] + if find(v,condensed) then + return mapping[v], v + end + end +end + +-- we could cache a lookup .. maybe some day ... (only when auto loaded!) + +local function foundname(name,sub) -- sub is not used currently + local data = names.data + local mappings, sorted_mappings = data.mappings, data.sorted_mappings + local fallbacks, sorted_fallbacks = data.fallbacks, data.sorted_fallbacks + local list = filters.list + -- dilemma: we lookup in the order otf ttf ttc ... afm but now an otf fallback + -- can come after an afm match ... well, one should provide nice names anyway + -- and having two lists is not an option + for i=1,#list do + local l = list[i] + local found = mappings[l][name] + if found then + if trace_names then + logs.report("fonts","resolved via direct name match: '%s'",name) + end + return found + end + end + for i=1,#list do + local l = list[i] + local found, fname = fuzzy(mappings[l],sorted_mappings[l],name,sub) + if found then + if trace_names then + logs.report("fonts","resolved via fuzzy name match: '%s' => '%s'",name,fname) + end + return found + end + end + for i=1,#list do + local l = list[i] + local found = fallbacks[l][name] + if found then + if trace_names then + logs.report("fonts","resolved via direct fallback match: '%s'",name) + end + return found + end + end + for i=1,#list do + local l = list[i] + local found, fname = fuzzy(sorted_mappings[l],sorted_fallbacks[l],name,sub) + if found then + if trace_names then + logs.report("fonts","resolved via fuzzy fallback match: '%s' => '%s'",name,fname) + end + return found + end + end +end + +function names.resolvedspecification(askedname,sub) + if askedname and askedname ~= "" and names.enabled then + askedname = cleanname(askedname) + names.load() + local found = foundname(askedname,sub) + if not found and is_reloaded() then + found = foundname(askedname,sub) + end + return found + end +end + +function names.resolve(askedname,sub) + local found = names.resolvedspecification(askedname,sub) + if found then + return found.filename, found.subfont and found.rawname + end +end + +-- specified search + +local function s_collect_weight_style_width_variant(found,done,all,weight,style,width,variant,family) + if family then + for i=1,#family do + local f = family[i] + if f and weight == f.weight and style == f.style and width == f.width and variant == f.variant then + found[#found+1], done[f] = f, true + if not all then return end + end + end + end +end +local function m_collect_weight_style_width_variant(found,done,all,weight,style,width,variant,families,sorted,strictname) + for i=1,#sorted do + local k = sorted[i] + local family = families[k] + for i=1,#family do + local f = family[i] + if not done[f] and weight == f.weight and style == f.style and width == f.width and variant == f.variant and find(f.fontname,strictname) then + found[#found+1], done[f] = f, true + if not all then return end + end + end + end +end + +local function s_collect_weight_style_width(found,done,all,weight,style,width,family) + if family then + for i=1,#family do + local f = family[i] + if f and weight == f.weight and style == f.style and width == f.width then + found[#found+1], done[f] = f, true + if not all then return end + end + end + end +end +local function m_collect_weight_style_width(found,done,all,weight,style,width,families,sorted,strictname) + for i=1,#sorted do + local k = sorted[i] + local family = families[k] + for i=1,#family do + local f = family[i] + if not done[f] and weight == f.weight and style == f.style and width == f.width and find(f.fontname,strictname) then + found[#found+1], done[f] = f, true + if not all then return end + end + end + end +end + +local function s_collect_weight_style(found,done,all,weight,style,family) + if family then + for i=1,#family do local f = family[i] + if f and weight == f.weight and style == f.style then + found[#found+1], done[f] = f, true + if not all then return end + end + end + end +end +local function m_collect_weight_style(found,done,all,weight,style,families,sorted,strictname) + for i=1,#sorted do + local k = sorted[i] + local family = families[k] + for i=1,#family do + local f = family[i] + if not done[f] and weight == f.weight and style == f.style and find(f.fontname,strictname) then + found[#found+1], done[f] = f, true + if not all then return end + end + end + end +end + +local function s_collect_style_width(found,done,all,style,width,family) + if family then + for i=1,#family do local f = family[i] + if f and style == f.style and width == f.width then + found[#found+1], done[f] = f, true + if not all then return end + end + end + end +end +local function m_collect_style_width(found,done,all,style,width,families,sorted,strictname) + for i=1,#sorted do + local k = sorted[i] + local family = families[k] + for i=1,#family do + local f = family[i] + if not done[f] and style == f.style and width == f.width and find(f.fontname,strictname) then + found[#found+1], done[f] = f, true + if not all then return end + end + end + end +end + +local function s_collect_weight(found,done,all,weight,family) + if family then + for i=1,#family do local f = family[i] + if f and weight == f.weight then + found[#found+1], done[f] = f, true + if not all then return end + end + end + end +end +local function m_collect_weight(found,done,all,weight,families,sorted,strictname) + for i=1,#sorted do + local k = sorted[i] + local family = families[k] + for i=1,#family do + local f = family[i] + if not done[f] and weight == f.weight and find(f.fontname,strictname) then + found[#found+1], done[f] = f, true + if not all then return end + end + end + end +end + +local function s_collect_style(found,done,all,style,family) + if family then + for i=1,#family do local f = family[i] + if f and style == f.style then + found[#found+1], done[f] = f, true + if not all then return end + end + end + end +end +local function m_collect_style(found,done,all,style,families,sorted,strictname) + for i=1,#sorted do + local k = sorted[i] + local family = families[k] + for i=1,#family do + local f = family[i] + if not done[f] and style == f.style and find(f.fontname,strictname) then + found[#found+1], done[f] = f, true + if not all then return end + end + end + end +end + +local function s_collect_width(found,done,all,width,family) + if family then + for i=1,#family do local f = family[i] + if f and width == f.width then + found[#found+1], done[f] = f, true + if not all then return end + end + end + end +end +local function m_collect_width(found,done,all,width,families,sorted,strictname) + for i=1,#sorted do + local k = sorted[i] + local family = families[k] + for i=1,#family do + local f = family[i] + if not done[f] and width == f.width and find(f.fontname,strictname) then + found[#found+1], done[f] = f, true + if not all then return end + end + end + end +end + +local function s_collect(found,done,all,family) + if family then + for i=1,#family do local f = family[i] + if f then + found[#found+1], done[f] = f, true + if not all then return end + end + end + end +end +local function m_collect(found,done,all,families,sorted,strictname) + for i=1,#sorted do + local k = sorted[i] + local family = families[k] + for i=1,#family do + local f = family[i] + if not done[f] and find(f.fontname,strictname) then + found[#found+1], done[f] = f, true + if not all then return end + end + end + end +end + +local function collect(stage,found,done,name,weight,style,width,variant,all) + local data = names.data + local families, sorted = data.families, data.sorted_families + strictname = "^".. name -- to be checked + local family = families[name] + if trace_names then + logs.report("fonts","resolving name '%s', weight '%s', style '%s', width '%s', variant '%s'", + name or "?",tostring(weight),tostring(style),tostring(width),tostring(variant)) + end + --~ print(name,table.serialize(family)) + if weight and weight ~= "" then + if style and style ~= "" then + if width and width ~= "" then + if variant and variant ~= "" then + if trace_names then + logs.report("fonts","resolving stage %s, name '%s', weight '%s', style '%s', width '%s', variant '%s'",stage,name,weight,style,width,variant) + end + s_collect_weight_style_width_variant(found,done,all,weight,style,width,variant,family) + m_collect_weight_style_width_variant(found,done,all,weight,style,width,variant,families,sorted,strictname) + else + if trace_names then + logs.report("fonts","resolving stage %s, name '%s', weight '%s', style '%s', width '%s'",stage,name,weight,style,width) + end + s_collect_weight_style_width(found,done,all,weight,style,width,family) + m_collect_weight_style_width(found,done,all,weight,style,width,families,sorted,strictname) + end + else + if trace_names then + logs.report("fonts","resolving stage %s, name '%s', weight '%s', style '%s'",stage,name,weight,style) + end + s_collect_weight_style(found,done,all,weight,style,family) + m_collect_weight_style(found,done,all,weight,style,families,sorted,strictname) + end + else + if trace_names then + logs.report("fonts","resolving stage %s, name '%s', weight '%s'",stage,name,weight) + end + s_collect_weight(found,done,all,weight,family) + m_collect_weight(found,done,all,weight,families,sorted,strictname) + end + elseif style and style ~= "" then + if width and width ~= "" then + if trace_names then + logs.report("fonts","resolving stage %s, name '%s', style '%s', width '%s'",stage,name,style,width) + end + s_collect_style_width(found,done,all,style,width,family) + m_collect_style_width(found,done,all,style,width,families,sorted,strictname) + else + if trace_names then + logs.report("fonts","resolving stage %s, name '%s', style '%s'",stage,name,style) + end + s_collect_style(found,done,all,style,family) + m_collect_style(found,done,all,style,families,sorted,strictname) + end + elseif width and width ~= "" then + if trace_names then + logs.report("fonts","resolving stage %s, name '%s', width '%s'",stage,name,width) + end + s_collect_width(found,done,all,width,family) + m_collect_width(found,done,all,width,families,sorted,strictname) + else + if trace_names then + logs.report("fonts","resolving stage %s, name '%s'",stage,name) + end + s_collect(found,done,all,family) + m_collect(found,done,all,families,sorted,strictname) + end +end + +function heuristic(name,weight,style,width,variant,all) -- todo: fallbacks + local found, done = { }, { } +--~ print(name,weight,style,width,variant) + weight, style, width, variant = weight or "normal", style or "normal", width or "normal", variant or "normal" + name = cleanname(name) + collect(1,found,done,name,weight,style,width,variant,all) + -- still needed ? + if #found == 0 and variant ~= "normal" then -- not weight + variant = "normal" + collect(4,found,done,name,weight,style,width,variant,all) + end + if #found == 0 and width ~= "normal" then + width = "normal" + collect(2,found,done,name,weight,style,width,variant,all) + end + if #found == 0 and weight ~= "normal" then -- not style + weight = "normal" + collect(3,found,done,name,weight,style,width,variant,all) + end + if #found == 0 and style ~= "normal" then -- not weight + style = "normal" + collect(4,found,done,name,weight,style,width,variant,all) + end + -- + local nf = #found + if trace_names then + if nf then + local t = { } + for i=1,nf do + t[#t+1] = format("'%s'",found[i].fontname) + end + logs.report("fonts","name '%s' resolved to %s instances: %s",name,nf,concat(t," ")) + else + logs.report("fonts","name '%s' unresolved",name) + end + end + if all then + return nf > 0 and found + else + return found[1] + end +end + +function names.specification(askedname,weight,style,width,variant,reload,all) + if askedname and askedname ~= "" and names.enabled then + askedname = lower(askedname) -- or cleanname + names.load(reload) + local found = heuristic(askedname,weight,style,width,variant,all) + if not found and is_reloaded() then + found = heuristic(askedname,weight,style,width,variant,all) + if not filename then + found = foundname(askedname) -- old method + end + end + return found + end +end + +function names.collect(askedname,weight,style,width,variant,reload,all) + if askedname and askedname ~= "" and names.enabled then + askedname = lower(askedname) -- or cleanname + names.load(reload) + local list = heuristic(askedname,weight,style,width,variant,true) + if not list or #list == 0 and is_reloaded() then + list = heuristic(askedname,weight,style,width,variant,true) + end + return list + end +end + +function names.collectspec(askedname,reload,all) + local name, weight, style, width, variant = names.splitspec(askedname) + return names.collect(name,weight,style,width,variant,reload,all) +end + +function names.resolvespec(askedname,sub) + local found = names.specification(names.splitspec(askedname)) + if found then + return found.filename, found.subfont and found.rawname + end +end + +function names.collectfiles(askedname,reload) -- no all + if askedname and askedname ~= "" and names.enabled then + askedname = lower(askedname) -- or cleanname + names.load(reload) + local list = { } + local basename = file.basename + local specifications = names.data.specifications + for i=1,#specifications do + local s = specifications[i] + if find(lower(basename(s.filename)),askedname) then + list[#list+1] = s + end + end + return list + end +end + +--[[ldx-- +Fallbacks, not permanent but a transition thing.
+--ldx]]-- + +names.new_to_old = { + ["lmroman10-capsregular"] = "lmromancaps10-oblique", + ["lmroman10-capsoblique"] = "lmromancaps10-regular", + ["lmroman10-demi"] = "lmromandemi10-oblique", + ["lmroman10-demioblique"] = "lmromandemi10-regular", + ["lmroman8-oblique"] = "lmromanslant8-regular", + ["lmroman9-oblique"] = "lmromanslant9-regular", + ["lmroman10-oblique"] = "lmromanslant10-regular", + ["lmroman12-oblique"] = "lmromanslant12-regular", + ["lmroman17-oblique"] = "lmromanslant17-regular", + ["lmroman10-boldoblique"] = "lmromanslant10-bold", + ["lmroman10-dunhill"] = "lmromandunh10-oblique", + ["lmroman10-dunhilloblique"] = "lmromandunh10-regular", + ["lmroman10-unslanted"] = "lmromanunsl10-regular", + ["lmsans10-demicondensed"] = "lmsansdemicond10-regular", + ["lmsans10-demicondensedoblique"] = "lmsansdemicond10-oblique", + ["lmsansquotation8-bold"] = "lmsansquot8-bold", + ["lmsansquotation8-boldoblique"] = "lmsansquot8-boldoblique", + ["lmsansquotation8-oblique"] = "lmsansquot8-oblique", + ["lmsansquotation8-regular"] = "lmsansquot8-regular", + ["lmtypewriter8-regular"] = "lmmono8-regular", + ["lmtypewriter9-regular"] = "lmmono9-regular", + ["lmtypewriter10-regular"] = "lmmono10-regular", + ["lmtypewriter12-regular"] = "lmmono12-regular", + ["lmtypewriter10-italic"] = "lmmono10-italic", + ["lmtypewriter10-oblique"] = "lmmonoslant10-regular", + ["lmtypewriter10-capsoblique"] = "lmmonocaps10-oblique", + ["lmtypewriter10-capsregular"] = "lmmonocaps10-regular", + ["lmtypewriter10-light"] = "lmmonolt10-regular", + ["lmtypewriter10-lightoblique"] = "lmmonolt10-oblique", + ["lmtypewriter10-lightcondensed"] = "lmmonoltcond10-regular", + ["lmtypewriter10-lightcondensedoblique"] = "lmmonoltcond10-oblique", + ["lmtypewriter10-dark"] = "lmmonolt10-bold", + ["lmtypewriter10-darkoblique"] = "lmmonolt10-boldoblique", + ["lmtypewritervarwd10-regular"] = "lmmonoproplt10-regular", + ["lmtypewritervarwd10-oblique"] = "lmmonoproplt10-oblique", + ["lmtypewritervarwd10-light"] = "lmmonoprop10-regular", + ["lmtypewritervarwd10-lightoblique"] = "lmmonoprop10-oblique", + ["lmtypewritervarwd10-dark"] = "lmmonoproplt10-bold", + ["lmtypewritervarwd10-darkoblique"] = "lmmonoproplt10-boldoblique", +} + +names.old_to_new = table.swapped(names.new_to_old) + +function names.exists(name) + local found = false + local list = filters.list + for k=1,#list do + local v = list[k] + found = (resolvers.find_file(name,v) or "") ~= "" + if found then + return found + end + end + return ((resolvers.find_file(name,"tfm") or "") ~= "") or ((names.resolve(name) or "") ~= "") +end + +-- for i=1,fonts.names.lookup(pattern) do +-- texio.write_nl(fonts.names.getkey("filename",i)) +-- end + +local lastlookups, lastpattern = { }, "" + +function names.lookup(pattern,name,reload) -- todo: find + if lastpattern ~= pattern then + names.load(reload) + local specifications = names.data.specifications + local families = names.data.families + local lookups = specifications + if name then + lookups = families[name] + elseif not find(pattern,"=") then + lookups = families[pattern] + end + if trace_names then + logs.report("fonts","starting with %s lookups for '%s'",#lookups,pattern) + end + if lookups then + for key, value in gmatch(pattern,"([^=,]+)=([^=,]+)") do + local t = { } + for i=1,#lookups do + local s = lookups[i] + if s[key] == value then + t[#t+1] = lookups[i] + end + end + if trace_names then + logs.report("fonts","%s matches for key '%s' with value '%s'",#t,key,value) + end + lookups = t + end + end + lastpattern = pattern + lastlookups = lookups or { } + end + return #lastlookups +end + +function names.getlookupkey(key,n) + local l = lastlookups[n or 1] + return (l and l[key]) or "" +end + +function names.noflookups() + return #lastlookups +end + +function names.getlookups(pattern,name,reload) + if pattern then + names.lookup(pattern,name,reload) + end + return lastlookups +end + +function table.formatcolumns(result) + if result and #result > 0 then + local widths = { } + local first = result[1] + local n = #first + for i=1,n do + widths[i] = 0 + end + for i=1,#result do + local r = result[i] + for j=1,n do + local w = #r[j] + if w > widths[j] then + widths[j] = w + end + end + end + for i=1,n do + widths[i] = "%-" .. widths[i] .. "s" + end + local template = concat(widths," ") + for i=1,#result do + local str = format(template,unpack(result[i])) + result[i] = string.strip(str) + end + end + return result +end diff --git a/tex/context/base/font-tfm.lua b/tex/context/base/font-tfm.lua new file mode 100644 index 000000000..31ae2cae1 --- /dev/null +++ b/tex/context/base/font-tfm.lua @@ -0,0 +1,736 @@ +if not modules then modules = { } end modules ['font-tfm'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local utf = unicode.utf8 + +local next, format, match, lower, gsub = next, string.format, string.match, string.lower, string.gsub +local concat, sortedkeys, utfbyte, serialize = table.concat, table.sortedkeys, utf.byte, table.serialize + +local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) +local trace_scaling = false trackers.register("fonts.scaling" , function(v) trace_scaling = v end) + +-- tfmdata has also fast access to indices and unicodes +-- to be checked: otf -> tfm -> tfmscaled +-- +-- watch out: no negative depths and negative eights permitted in regular fonts + +--[[ldx-- +Here we only implement a few helper functions.
+--ldx]]-- + +fonts = fonts or { } +fonts.tfm = fonts.tfm or { } +fonts.ids = fonts.ids or { } + +local tfm = fonts.tfm + +fonts.loaded = fonts.loaded or { } +fonts.dontembed = fonts.dontembed or { } +fonts.triggers = fonts.triggers or { } -- brrr +fonts.initializers = fonts.initializers or { } +fonts.initializers.common = fonts.initializers.common or { } + +local fontdata = fonts.ids +local disc = node.id('disc') +local glyph = node.id('glyph') +local set_attribute = node.set_attribute + +--[[ldx-- +The next function encapsulates the standard
We need to normalize the scale factor (in scaled points). This has to
+do with the fact that
Before a font is passed to
Beware, the boundingbox is passed as reference so we may not overwrite it +in the process; numbers are of course copies. Here 65536 equals 1pt. (Due to +excessive memory usage in CJK fonts, we no longer pass the boundingbox.)
+--ldx]]-- + +fonts.trace_scaling = false + +-- the following hack costs a bit of runtime but safes memory +-- +-- basekerns are scaled and will be hashed by table id +-- sharedkerns are unscaled and are be hashed by concatenated indexes + +--~ function tfm.check_base_kerns(tfmdata) +--~ if tfm.share_base_kerns then +--~ local sharedkerns = tfmdata.sharedkerns +--~ if sharedkerns then +--~ local basekerns = { } +--~ tfmdata.basekerns = basekerns +--~ return sharedkerns, basekerns +--~ end +--~ end +--~ return nil, nil +--~ end + +--~ function tfm.prepare_base_kerns(tfmdata) +--~ if tfm.share_base_kerns and not tfmdata.sharedkerns then +--~ local sharedkerns = { } +--~ tfmdata.sharedkerns = sharedkerns +--~ for u, chr in next, tfmdata.characters do +--~ local kerns = chr.kerns +--~ if kerns then +--~ local hash = concat(sortedkeys(kerns), " ") +--~ local base = sharedkerns[hash] +--~ if not base then +--~ sharedkerns[hash] = kerns +--~ else +--~ chr.kerns = base +--~ end +--~ end +--~ end +--~ end +--~ end + +-- we can have cache scaled characters when we are in node mode and don't have +-- protruding and expansion: hash == fullname @ size @ protruding @ expansion +-- but in practice (except from mk) the otf hash will be enough already so it +-- makes no sense to mess up the code now + +local charactercache = { } + +-- The scaler is only used for otf and afm and virtual fonts. If +-- a virtual font has italic correction make sure to set the +-- has_italic flag. Some more flags will be added in the future. + +function tfm.calculate_scale(tfmtable, scaledpoints, relativeid) + if scaledpoints < 0 then + scaledpoints = (- scaledpoints/1000) * tfmtable.designsize -- already in sp + end + local units = tfmtable.units or 1000 + local delta = scaledpoints/units -- brr, some open type fonts have 2048 + return scaledpoints, delta, units +end + +function tfm.do_scale(tfmtable, scaledpoints, relativeid) + -- tfm.prepare_base_kerns(tfmtable) -- optimalization + local t = { } -- the new table + local scaledpoints, delta, units = tfm.calculate_scale(tfmtable, scaledpoints, relativeid) + t.units_per_em = units or 1000 + local hdelta, vdelta = delta, delta + -- unicoded unique descriptions shared cidinfo characters changed parameters indices + for k,v in next, tfmtable do + if type(v) == "table" then + -- print(k) + else + t[k] = v + end + end + local extend_factor = tfmtable.extend_factor or 0 + if extend_factor ~= 0 and extend_factor ~= 1 then + hdelta = hdelta * extend_factor + t.extend = extend_factor * 1000 + else + t.extend = 1000 + end + local slant_factor = tfmtable.slant_factor or 0 + if slant_factor ~= 0 then + t.slant = slant_factor * 1000 + else + t.slant = 0 + end + -- status + local isvirtual = tfmtable.type == "virtual" or tfmtable.virtualized + local hasmath = (tfmtable.math_parameters ~= nil and next(tfmtable.math_parameters) ~= nil) or (tfmtable.MathConstants ~= nil and next(tfmtable.MathConstants) ~= nil) + local nodemode = tfmtable.mode == "node" + local hasquality = tfmtable.auto_expand or tfmtable.auto_protrude + local hasitalic = tfmtable.has_italic + -- + t.parameters = { } + t.characters = { } + t.MathConstants = { } + -- fast access + local descriptions = tfmtable.descriptions or { } + t.unicodes = tfmtable.unicodes + t.indices = tfmtable.indices + t.marks = tfmtable.marks +t.goodies = tfmtable.goodies +t.colorscheme = tfmtable.colorscheme +--~ t.embedding = tfmtable.embedding + t.descriptions = descriptions + if tfmtable.fonts then + t.fonts = table.fastcopy(tfmtable.fonts) -- hm also at the end + end + local tp = t.parameters + local mp = t.math_parameters + local tfmp = tfmtable.parameters -- let's check for indexes + -- + tp.slant = (tfmp.slant or tfmp[1] or 0) + tp.space = (tfmp.space or tfmp[2] or 0)*hdelta + tp.space_stretch = (tfmp.space_stretch or tfmp[3] or 0)*hdelta + tp.space_shrink = (tfmp.space_shrink or tfmp[4] or 0)*hdelta + tp.x_height = (tfmp.x_height or tfmp[5] or 0)*vdelta + tp.quad = (tfmp.quad or tfmp[6] or 0)*hdelta + tp.extra_space = (tfmp.extra_space or tfmp[7] or 0)*hdelta + local protrusionfactor = (tp.quad ~= 0 and 1000/tp.quad) or 0 + local tc = t.characters + local characters = tfmtable.characters + local nameneeded = not tfmtable.shared.otfdata --hack + local changed = tfmtable.changed or { } -- for base mode + local ischanged = changed and next(changed) + local indices = tfmtable.indices + local luatex = tfmtable.luatex + local tounicode = luatex and luatex.tounicode + local defaultwidth = luatex and luatex.defaultwidth or 0 + local defaultheight = luatex and luatex.defaultheight or 0 + local defaultdepth = luatex and luatex.defaultdepth or 0 + -- experimental, sharing kerns (unscaled and scaled) saves memory + -- local sharedkerns, basekerns = tfm.check_base_kerns(tfmtable) + -- loop over descriptions (afm and otf have descriptions, tfm not) + -- there is no need (yet) to assign a value to chr.tonunicode + local scaledwidth = defaultwidth * hdelta + local scaledheight = defaultheight * vdelta + local scaleddepth = defaultdepth * vdelta + local stackmath = tfmtable.ignore_stack_math ~= true + local private = fonts.private + local sharedkerns = { } + for k,v in next, characters do + local chr, description, index + if ischanged then + -- basemode hack + local c = changed[k] + if c then + description = descriptions[c] or v + v = characters[c] or v + index = (indices and indices[c]) or c + else + description = descriptions[k] or v + index = (indices and indices[k]) or k + end + else + description = descriptions[k] or v + index = (indices and indices[k]) or k + end + local width = description.width + local height = description.height + local depth = description.depth + if width then width = hdelta*width else width = scaledwidth end + if height then height = vdelta*height else height = scaledheight end + -- if depth then depth = vdelta*depth else depth = scaleddepth end + if depth and depth ~= 0 then + depth = delta*depth + if nameneeded then + chr = { + name = description.name, + index = index, + height = height, + depth = depth, + width = width, + } + else + chr = { + index = index, + height = height, + depth = depth, + width = width, + } + end + else + -- this saves a little bit of memory time and memory, esp for big cjk fonts + if nameneeded then + chr = { + name = description.name, + index = index, + height = height, + width = width, + } + else + chr = { + index = index, + height = height, + width = width, + } + end + end + -- if trace_scaling then + -- logs.report("define font","t=%s, u=%s, i=%s, n=%s c=%s",k,chr.tounicode or k,description.index,description.name or '-',description.class or '-') + -- end + if tounicode then + local tu = tounicode[index] -- nb: index! + if tu then + chr.tounicode = tu + end + end + if hasquality then + -- we could move these calculations elsewhere (saves calculations) + local ve = v.expansion_factor + if ve then + chr.expansion_factor = ve*1000 -- expansionfactor, hm, can happen elsewhere + end + local vl = v.left_protruding + if vl then + chr.left_protruding = protrusionfactor*width*vl + end + local vr = v.right_protruding + if vr then + chr.right_protruding = protrusionfactor*width*vr + end + end + -- todo: hasitalic + if hasitalic then + local vi = description.italic or v.italic + if vi and vi ~= 0 then + chr.italic = vi*hdelta + end + end + -- to be tested + if hasmath then + -- todo, just operate on descriptions.math + local vn = v.next + if vn then + chr.next = vn + else + local vv = v.vert_variants + if vv then + local t = { } + for i=1,#vv do + local vvi = vv[i] + t[i] = { + ["start"] = (vvi["start"] or 0)*vdelta, + ["end"] = (vvi["end"] or 0)*vdelta, + ["advance"] = (vvi["advance"] or 0)*vdelta, + ["extender"] = vvi["extender"], + ["glyph"] = vvi["glyph"], + } + end + chr.vert_variants = t + --~ local ic = v.vert_italic_correction + --~ if ic then + --~ chr.italic = ic * hdelta + --~ print(format("0x%05X -> %s",k,chr.italic)) + --~ end + else + local hv = v.horiz_variants + if hv then + local t = { } + for i=1,#hv do + local hvi = hv[i] + t[i] = { + ["start"] = (hvi["start"] or 0)*hdelta, + ["end"] = (hvi["end"] or 0)*hdelta, + ["advance"] = (hvi["advance"] or 0)*hdelta, + ["extender"] = hvi["extender"], + ["glyph"] = hvi["glyph"], + } + end + chr.horiz_variants = t + end + end + end + local vt = description.top_accent + if vt then + chr.top_accent = vdelta*vt + end + if stackmath then + local mk = v.mathkerns + if mk then + local kerns = { } + local v = mk.top_right if v then local k = { } for i=1,#v do local vi = v[i] + k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern } + end kerns.top_right = k end + local v = mk.top_left if v then local k = { } for i=1,#v do local vi = v[i] + k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern } + end kerns.top_left = k end + local v = mk.bottom_left if v then local k = { } for i=1,#v do local vi = v[i] + k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern } + end kerns.bottom_left = k end + local v = mk.bottom_right if v then local k = { } for i=1,#v do local vi = v[i] + k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern } + end kerns.bottom_right = k end + chr.mathkern = kerns -- singular + end + end + end + if not nodemode then + local vk = v.kerns + if vk then + --~ if sharedkerns then + --~ local base = basekerns[vk] -- hashed by table id, not content + --~ if not base then + --~ base = {} + --~ for k,v in next, vk do base[k] = v*hdelta end + --~ basekerns[vk] = base + --~ end + --~ chr.kerns = base + --~ else + --~ local tt = {} + --~ for k,v in next, vk do tt[k] = v*hdelta end + --~ chr.kerns = tt + --~ end + local s = sharedkerns[vk] + if not s then + s = { } + for k,v in next, vk do s[k] = v*hdelta end + sharedkerns[vk] = s + end + chr.kerns = s + end + local vl = v.ligatures + if vl then + if true then + chr.ligatures = vl -- shared + else + local tt = { } + for i,l in next, vl do + tt[i] = l + end + chr.ligatures = tt + end + end + end + if isvirtual then + local vc = v.commands + if vc then + -- we assume non scaled commands here + -- tricky .. we need to scale pseudo math glyphs too + -- which is why we deal with rules too + local ok = false + for i=1,#vc do + local key = vc[i][1] + if key == "right" or key == "down" then + ok = true + break + end + end + if ok then + local tt = { } + for i=1,#vc do + local ivc = vc[i] + local key = ivc[1] + if key == "right" then + tt[#tt+1] = { key, ivc[2]*hdelta } + elseif key == "down" then + tt[#tt+1] = { key, ivc[2]*vdelta } + elseif key == "rule" then + tt[#tt+1] = { key, ivc[2]*vdelta, ivc[3]*hdelta } + else -- not comment + tt[#tt+1] = ivc -- shared since in cache and untouched + end + end + chr.commands = tt + else + chr.commands = vc + end + end + end + tc[k] = chr + end + -- t.encodingbytes, t.filename, t.fullname, t.name: elsewhere + t.size = scaledpoints + t.factor = delta + t.hfactor = hdelta + t.vfactor = vdelta + if t.fonts then + t.fonts = table.fastcopy(t.fonts) -- maybe we virtualize more afterwards + end + if hasmath then + -- mathematics.extras.copy(t) -- can be done elsewhere if needed + local ma = tfm.mathactions + for i=1,#ma do + ma[i](t,tfmtable,delta,hdelta,vdelta) -- what delta? + end + end + -- needed for \high cum suis + local tpx = tp.x_height + if hasmath then + if not tp[13] then tp[13] = .86*tpx end -- mathsupdisplay + if not tp[14] then tp[14] = .86*tpx end -- mathsupnormal + if not tp[15] then tp[15] = .86*tpx end -- mathsupcramped + if not tp[16] then tp[16] = .48*tpx end -- mathsubnormal + if not tp[17] then tp[17] = .48*tpx end -- mathsubcombined + if not tp[22] then tp[22] = 0 end -- mathaxisheight + if t.MathConstants then t.MathConstants.AccentBaseHeight = nil end -- safeguard + end + t.tounicode = 1 + t.cidinfo = tfmtable.cidinfo + -- we have t.name=metricfile and t.fullname=RealName and t.filename=diskfilename + -- when collapsing fonts, luatex looks as both t.name and t.fullname as ttc files + -- can have multiple subfonts + if hasmath then + if trace_defining then + logs.report("define font","math enabled for: name '%s', fullname: '%s', filename: '%s'",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename") + end + else + if trace_defining then + logs.report("define font","math disabled for: name '%s', fullname: '%s', filename: '%s'",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename") + end + t.nomath, t.MathConstants = true, nil + end + if not t.psname then + -- name used in pdf file as well as for selecting subfont in ttc/dfont + t.psname = t.fontname or (t.fullname and fonts.names.cleanname(t.fullname)) + end + if trace_defining then + logs.report("define font","used for accesing subfont: '%s'",t.psname or "nopsname") + logs.report("define font","used for subsetting: '%s'",t.fontname or "nofontname") + end +--~ print(t.fontname,table.serialize(t.MathConstants)) + return t, delta +end + +--[[ldx-- +The reason why the scaler is split, is that for a while we experimented
+with a helper function. However, in practice the
Analyzers run per script and/or language and are needed in order to +process features right.
+--ldx]]-- + +fonts.analyzers = fonts.analyzers or { } +fonts.analyzers.aux = fonts.analyzers.aux or { } +fonts.analyzers.methods = fonts.analyzers.methods or { } +fonts.analyzers.initializers = fonts.analyzers.initializers or { } + +-- todo: analyzers per script/lang, cross font, so we need an font id hash -> script +-- e.g. latin -> hyphenate, arab -> 1/2/3 analyze + +-- an example analyzer (should move to font-ota.lua) + +local state = attributes.private('state') + +function fonts.analyzers.aux.setstate(head,font) + local tfmdata = fontdata[font] + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + local first, last, current, n, done = nil, nil, head, 0, false -- maybe make n boolean + while current do + local id = current.id + if id == glyph and current.font == font then + local d = descriptions[current.char] + if d then + if d.class == "mark" then + done = true + set_attribute(current,state,5) -- mark + elseif n == 0 then + first, last, n = current, current, 1 + set_attribute(current,state,1) -- init + else + last, n = current, n+1 + set_attribute(current,state,2) -- medi + end + else -- finish + if first and first == last then + set_attribute(last,state,4) -- isol + elseif last then + set_attribute(last,state,3) -- fina + end + first, last, n = nil, nil, 0 + end + elseif id == disc then + -- always in the middle + set_attribute(current,state,2) -- midi + last = current + else -- finish + if first and first == last then + set_attribute(last,state,4) -- isol + elseif last then + set_attribute(last,state,3) -- fina + end + first, last, n = nil, nil, 0 + end + current = current.next + end + if first and first == last then + set_attribute(last,state,4) -- isol + elseif last then + set_attribute(last,state,3) -- fina + end + return head, done +end + +function tfm.replacements(tfm,value) + -- tfm.characters[0x0022] = table.fastcopy(tfm.characters[0x201D]) + -- tfm.characters[0x0027] = table.fastcopy(tfm.characters[0x2019]) + -- tfm.characters[0x0060] = table.fastcopy(tfm.characters[0x2018]) + -- tfm.characters[0x0022] = tfm.characters[0x201D] + tfm.characters[0x0027] = tfm.characters[0x2019] + -- tfm.characters[0x0060] = tfm.characters[0x2018] +end + +-- checking + +function tfm.checked_filename(metadata,whatever) + local foundfilename = metadata.foundfilename + if not foundfilename then + local askedfilename = metadata.filename or "" + if askedfilename ~= "" then + foundfilename = resolvers.findbinfile(askedfilename,"") or "" + if foundfilename == "" then + logs.report("fonts","source file '%s' is not found",askedfilename) + foundfilename = resolvers.findbinfile(file.basename(askedfilename),"") or "" + if foundfilename ~= "" then + logs.report("fonts","using source file '%s' (cache mismatch)",foundfilename) + end + end + elseif whatever then + logs.report("fonts","no source file for '%s'",whatever) + foundfilename = "" + end + metadata.foundfilename = foundfilename + -- logs.report("fonts","using source file '%s'",foundfilename) + end + return foundfilename +end + +-- status info + +statistics.register("fonts load time", function() + return statistics.elapsedseconds(fonts) +end) diff --git a/tex/context/base/font-tra.mkiv b/tex/context/base/font-tra.mkiv new file mode 100644 index 000000000..e47c8c49d --- /dev/null +++ b/tex/context/base/font-tra.mkiv @@ -0,0 +1,113 @@ +%D \module +%D [ file=font-tra, +%D version=2009.01.02, % or so +%D title=\CONTEXT\ Font Macros, +%D subtitle=Tracing, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\unprotect + +%D \macros +%D {doiffontpresentelse} +%D +%D \starttyping +%D \doiffontpresentelse{texnansi-lmr10}{YES}{NO} +%D \doiffontpresentelse{adam-lindsay-modern-serif}{YES}{NO} +%D \stoptyping + +\def\doiffontpresentelse#1{\ctxlua{commands.doifelse(fonts.names.exists("#1"))}} + +% experimental, maybe this becomes a module + +\newbox\otfcollector + +\unexpanded\def\startotfcollecting{\ctxlua{nodes.tracers.steppers.start()}} +\unexpanded\def\stopotfcollecting {\ctxlua{nodes.tracers.steppers.stop()}} +\def\resetotfcollecting{\ctxlua{nodes.tracers.steppers.reset()}} + +% Rather experimental: +% +% \page \showotfcomposition{arabtype*arab-default at 48pt}{-1}{الضَّرَّ} \page +% \page \showotfcomposition{arabtype*arab-default at 48pt}{-1}{لِلّٰهِ} \page + +\def\showotfstepglyphs#1% + {\ctxlua{nodes.tracers.steppers.glyphs(\number\otfcollector,#1)}% + \unhbox\otfcollector} + +\def\otfstepcharcommand#1#2% font char + {\removeunwantedspaces + \hskip.5em plus .125em\relax + U+\hexnumber{#2}:\ruledhbox{\ctxlua{nodes.tracers.fontchar(#1,#2)}}% + \hskip.5em plus .125em\relax} + +\def\otfstepmessagecommand#1#2% + {\begingroup + \tttf\language\minusone + \veryraggedright + \hangindent1em + \hangafter\plusone + \dontleavehmode\hbox{\detokenize{#1}}\removeunwantedspaces + \doifsomething{#2}{\break\detokenize{#2}}\endgraf + \endgroup + \blank} + +\def\showotfstepchars#1% + {\ctxlua{nodes.tracers.steppers.codes(#1,\!!bs\detokenize{\otfstepcharcommand}\!!es)}} + +\def\showotfstepmessages#1% + {\ctxlua{nodes.tracers.steppers.messages(#1,\!!bs\detokenize{\otfstepmessagecommand}\!!es,true)}} + +\def\showotfstepfeatures + {\ctxlua{nodes.tracers.steppers.features()}} + +\def\showotfsteps + {\dontleavehmode\bgroup\tttf \language\minusone features: \showotfstepfeatures\egroup + \blank + \dontleavehmode\bgroup\tttf result:\egroup + \blank + \startlinecorrection + \ruledhbox\bgroup\box\otfcompositionbox\egroup + \stoplinecorrection + \dorecurse{\ctxlua{nodes.tracers.steppers.nofsteps()}} + {\blank + \showotfstepmessages\recurselevel + \blank + \startlinecorrection + \dontleavehmode\bgroup\resetallattributes\pardir TLT\textdir TLT\relax\tttf\recurselevel: \showotfstepchars\recurselevel\egroup + \stoplinecorrection + \blank + \startlinecorrection + \ruledhbox % can be mode + \bgroup\resetallattributes\showotfstepglyphs\recurselevel\egroup % reset is new, we don't want additional processing + \stoplinecorrection + \blank}} + +\unexpanded\def\startotfsample + {\enabletrackers[*otf.sample]% beware, kind of global + \startotfcollecting + \begingroup} + +\unexpanded\def\stopotfsample + {\endgroup + \stopotfcollecting + \disabletrackers[*otf.sample]% beware, kind of global: otf.sample + \showotfsteps + \resetotfcollecting} + +\newbox\otfcompositionbox + +\def\showotfcomposition#1#2#3% {font*features at size}, rl=-1, text + {\begingroup + \setupcolors[\c!state=\v!start]% can be option + \startotfsample + \global\setbox\otfcompositionbox\hbox{\definedfont[#1]\ifnum#2<0 \textdir TRT\else\ifnum#2>0 \textdir TLT\fi\fi\relax#3}% + \stopotfsample + \endgroup} + +\protect \endinput diff --git a/tex/context/base/font-uni.mkii b/tex/context/base/font-uni.mkii new file mode 100644 index 000000000..02ab6ce2c --- /dev/null +++ b/tex/context/base/font-uni.mkii @@ -0,0 +1,444 @@ +%D \module +%D [ file=font-uni, +%D version=1999.10.10, +%D title=\CONTEXT\ Font Macros, +%D subtitle=\UNICODE, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Font Macros / Unicode} + +%D In \XETEX, unicode support is straightforward, so we +%D simply output a \type {\char} with a 16||bit number. + +\ifnum\texengine=\xetexengine + \unexpanded\def\uchar#1#2{\char\numexpr#2+#1*\pluscclvi\relax} + \let\uc\uchar + \expandafter \endinput +\fi + +%D Now comes the more traditional 8 bit \TEX\ hackery. + +%D I wrote this module when Wang Lei asked me how to use +%D Chinese in \CONTEXT. From the samples he sent me, I deduced +%D that some mixture of one and two byte encoding was used, +%D which he confirmed. Since \TEX\ normally does not use the +%D characters $>127$, so as long as the two byte characters +%D have a first character with code $>127$, we can use active +%D characters to handle them. In an optimistic mood, I called +%D this module the \UNICODE\ font module. In the module that +%D handles Chinese, we will see that some more interpretation +%D is involved, which is why the macros handling those +%D characters look ahead. + +\unprotect + +%D \macros +%D {handleunicodeflowglyph, uchar, +%D handleunicodeglyph, insertunicodeglyph, +%D unicodeposition, unicodeone, unicodetwo} +%D +%D For the moment \UNICODE\ support is rather primitive but +%D nevertheless effective. The reference to \UNICODE\ is not +%D entirely correct, since in many cases one will use \quote +%D {older} mappings, but in principle, \UNICODE\ can be +%D supported. +%D +%D We expect each character to come as two eight bit +%D characters. Those doubles are handled by making all +%D characters in the range $>127$ active, so that they can +%D pick up the next one, and act upon both their values. +%D Internally only numbers are used. A first implementation +%D simply internally prefixed the second part of the \UNICODE\ +%D pair with \type {\string} or \type {\char}, but this was +%D not that handy when it came to testing those values. +%D Because in principle we are dealing with an encoding, the +%D making active is handled in \type {enco-uni}. +%D +%D There are two commands to handle unicode characters: +%D +%D \starttyping +%D \handleunicodeflowglyph{number}{character} +%D \uchar{number}{number} +%D \stoptyping +%D +%D The first one can be assigned to an active character, the +%D second one can be used to directly access a glyph. Both +%D command call \type {\handleunicodeglyph} that in turn +%D calls \type {\insertunicodeglyph}. Both can be overruled +%D in specialized modules. The low level command \type +%D {\unicodeglyph} can best be left untouched, which is not +%D so much a problem because there is a hook into this macro: +%D \type {\unicodecharcommand}. +%D +%D In most cases one will redefine \type {\handleunicodeglyph} +%D in such a way that it identifies special situations first, +%D takes some actions next, calls \type {\insertunicodeglyph}, +%D if needed with \type {\unicodecharcommand} changed, and +%D finally does some finishing: +%D +%D \starttyping +%D \def\handleunicodeglyph +%D {take actions based on \unicodeone-two-position cq. \nextutoken +%D redefine \unicodecharcommand if needed +%D expand \insertunicodeglyph +%D take some final actions} +%D \stoptyping + +\newcount\unicodeposition + +%D The multistep approach is needed to pick up the second +%D token, since this token can have any value and any +%D catcode. + +% the \relax trick prevents eating up the space (needed for +% korean + +\def\handleunicodeflowglyph#1#2% + {\begingroup + \edef\unicodeone{#1}% + \@EA\afterassignment\@EA\dohandleunicodeflowglyph % two redundant ea's + \@EA\chardef\@EA\nexttoken\@EA`\string#2\relax} + +\def\dohandleunicodeflowglyph\relax + {\futurelet\nextutoken\dodohandleunicodeflowglyph} + +\def\dodohandleunicodeflowglyph % todo tex (or maybe no longer) + {\edef\unicodetwo{\the\nexttoken}% + \unicodeposition\numexpr\unicodeone*256+\unicodetwo\relax + \handleunicodeglyph + \endgroup} + +\unexpanded\def\uchar#1#2% use as standalone glyph + {\begingroup + \edef\unicodeone{#1}% + \edef\unicodetwo{#2}% + \unicodeposition\numexpr\unicodeone*256+\unicodetwo\relax + \handleunicodeglyph + \endgroup} + +\let\nextutoken\relax + +\unexpanded\def\lookaheaduchar#1#2% + {\def\dolookaheaduchar{\uchar{#1}{#2}\let\nextutoken\relax}% + \futurelet\nextutoken\dolookaheaduchar} + +\def\dohandleucflowglyph + {\unicodeposition\numexpr\unicodeone*256+\unicodetwo\relax + \handleunicodeglyph + \endgroup} + +\unexpanded\def\uc#1#2% used in tricky situations + {\begingroup + \edef\unicodeone{#1}% + \edef\unicodetwo{#2}% + \futurelet\nextutoken\dohandleucflowglyph} + +\def\insertunicodeglyph + {\unicodeglyph\unicodeone\unicodetwo} + +\let\handleunicodeglyph\insertunicodeglyph + +%D One can use the \type {\unicodeposition} in the macros +%D that handle pre and post material. + +%D \macros +%D {unicodestyle, unicodecharcommand} +%D +%D Each character pair will become one glyph. Because \TEX\ +%D cannot handle fonts with more that 256 characters, we use +%D \TFM\ files for each range. The first character of the pair +%D is appended to the name of a font, and the second is used to +%D access the glyph in that font. This means that a particular +%D font is split up in subfonts with names in the range: +%D +%D \starttyping +%DThis is very experimental code! Not yet adapted to recent +changes. This will change.
+--ldx]]-- + +-- define.methods elsewhere ! + +fonts = fonts or { } +fonts.vf = fonts.vf or { } + +local vf = fonts.vf +local tfm = fonts.tfm + +fonts.define = fonts.define or { } +fonts.define.methods = fonts.define.methods or { } + +vf.combinations = vf.combinations or { } +vf.aux = vf.aux or { } +vf.aux.combine = vf.aux.combine or { } + +function fonts.define.methods.install(tag, rules) + vf.combinations[tag] = rules + fonts.define.methods[tag] = function(specification) + return vf.combine(specification,tag) + end +end + +function vf.aux.combine.assign(g, name, from, to, start, force) + local f, id = vf.aux.combine.load(g,name) + if f and id then + -- optimize for whole range, then just g = f + if not from then from, to = 0, 0xFF00 end + if not to then to = from end + if not start then start = from end + local fc, gc = f.characters, g.characters + local fd, gd = f.descriptions, g.descriptions + local hn = #g.fonts+1 + g.fonts[hn] = { id = id } -- no need to be sparse + for i=from,to do + if fc[i] and (force or not gc[i]) then + gc[i] = table.fastcopy(fc[i]) -- can be optimized + gc[i].commands = { { 'slot', hn, start } } + gd[i] = fd[i] + end + start = start + 1 + end + if not g.parameters and #g.fonts > 0 then -- share this code ! + g.parameters = table.fastcopy(f.parameters) + g.italicangle = f.italicangle + g.ascender = f.ascender + g.descender = f.descender + g.factor = f.factor -- brrr + end + end +end + +function vf.aux.combine.process(g,list) + if list then + for _,v in next, list do + (vf.aux.combine.commands[v[1]] or nop)(g,v) + end + end +end + +function vf.aux.combine.load(g,name) + return tfm.read_and_define(name or g.specification.name,g.specification.size) +end + +function vf.aux.combine.names(g,name,force) + local f, id = tfm.read_and_define(name,g.specification.size) + if f and id then + local fc, gc = f.characters, g.characters + local fd, gd = f.descriptions, g.descriptions + g.fonts[#g.fonts+1] = { id = id } -- no need to be sparse + local hn = #g.fonts + for k, v in next, fc do + if force or not gc[k] then + gc[k] = table.fastcopy(v) + gc[k].commands = { { 'slot', hn, k } } + gd[i] = fd[i] + end + end + if not g.parameters and #g.fonts > 0 then -- share this code ! + g.parameters = table.fastcopy(f.parameters) + g.italicangle = f.italicangle + g.ascender = f.ascender + g.descender = f.descender + g.factor = f.factor -- brrr + end + end +end + +vf.aux.combine.commands = { + ["initialize"] = function(g,v) vf.aux.combine.assign (g,g.name) end, + ["include-method"] = function(g,v) vf.aux.combine.process (g,vf.combinations[v[2]]) end, -- name + ["copy-parameters"] = function(g,v) vf.aux.combine.parameters(g,v[2]) end, -- name + ["copy-range"] = function(g,v) vf.aux.combine.assign (g,v[2],v[3],v[4],v[5],true) end, -- name, from-start, from-end, to-start + ["copy-char"] = function(g,v) vf.aux.combine.assign (g,v[2],v[3],v[3],v[4],true) end, -- name, from, to + ["fallback-range"] = function(g,v) vf.aux.combine.assign (g,v[2],v[3],v[4],v[5],false) end, -- name, from-start, from-end, to-start + ["fallback-char"] = function(g,v) vf.aux.combine.assign (g,v[2],v[3],v[3],v[4],false) end, -- name, from, to + ["copy_names"] = function(g,v) vf.aux.combine.names (g,v[2],true) end, + ["fallback_names"] = function(g,v) vf.aux.combine.names (g,v[2],false) end, +} + +function vf.combine(specification,tag) + local g = { + name = specification.name, + -- type = 'virtual', + virtualized = true, + fonts = { }, + characters = { }, + descriptions = { }, + specification = table.fastcopy(specification) + } + vf.aux.combine.process(g,vf.combinations[tag]) + return g +end + +vf.aux.combine.commands["feature"] = function(g,v) + local key, value = v[2], v[3] + if key then + if value == nil then + value = true + end + if g.specification and g.specification.features.normal then + g.specification.features.normal[key] = value -- otf? + end + end +end + +-- simple example with features + +fonts.define.methods.install( + "ligatures", { + { "feature", "liga" } , + { "feature", "dlig" } , + { "initialize" } , + } +) + +--~ fonts.define.methods.install ( +--~ "ligatures-x", { +--~ { "feature", "liga" } , +--~ { "feature", "dlig" } , +--~ { "initialize" } , +--~ { "lineheight" } +--~ } +--~ ) + +--~ fonts.define.methods.install( +--~ "lmsymbol10", { +--~ { "fallback_names", "lmsy10.afm" } , +--~ { "fallback_names", "msam10.afm" } , +--~ { "fallback_names", "msbm10.afm" } +--~ } +--~ ) +--~ \font\TestFont=dummy@lmsymbol10 at 24pt + +-- docu case + +--~ fonts.define.methods.install( +--~ "weird", { +--~ { "copy-range", "lmroman10-regular" } , +--~ { "copy-char", "lmroman10-regular", 65, 66 } , +--~ { "copy-range", "lmsans10-regular", 0x0100, 0x01FF } , +--~ { "copy-range", "lmtypewriter10-regular", 0x0200, 0xFF00 } , +--~ { "fallback-range", "lmtypewriter10-regular", 0x0000, 0x0200 } +--~ } +--~ ) + +-- demo case -> move to module + +fonts.define.methods["demo-1"] = function(specification) + local name = specification.name -- symbolic name + local size = specification.size -- given size + local f, id = tfm.read_and_define('lmroman10-regular',size) + if f and id then + local capscale, digscale = 0.85, 0.75 + -- f.name, f.type = name, 'virtual' + f.name, f.virtualized = name, true + f.fonts = { + { id = id }, + { name = 'lmsans10-regular' , size = size*capscale }, -- forced extra name + { name = 'lmtypewriter10-regular', size = size*digscale } -- forced extra name + } + local i_is_of_category = characters.i_is_of_category + local characters, descriptions = f.characters, f.descriptions + local red = {'special','pdf: 1 0 0 rg'} + local green = {'special','pdf: 0 1 0 rg'} + local blue = {'special','pdf: 0 0 1 rg'} + local black = {'special','pdf: 0 g'} + for u,v in next, characters do + if u and i_is_of_category(u,'lu') then + v.width = capscale*v.width + v.commands = { red, {'slot',2,u}, black } + elseif u and i_is_of_category(u,'nd') then + v.width = digscale*v.width + v.commands = { blue, {'slot',3,u}, black } + else + v.commands = { green, {'slot',1,u}, black } + end + end + end + return f +end diff --git a/tex/context/base/font-xtx.lua b/tex/context/base/font-xtx.lua new file mode 100644 index 000000000..eac75dd29 --- /dev/null +++ b/tex/context/base/font-xtx.lua @@ -0,0 +1,122 @@ +if not modules then modules = { } end modules ['font-xtx'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + 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 lpegmatch = lpeg.match + +local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) + +--[[ldx-- +Choosing a font by name and specififying its size is only part of the
+game. In order to prevent complex commands,
For the sake of users who have defined fonts using that syntax, we +will support it, but we will provide additional methods as well. +Normally users will not use this direct way, but use a more abstract +interface.
+ +The next one is the official one. However, in the plain +variant we need to support the crappy [] specification as +well and that does not work too well with the general design +of the specifier.
+--ldx]]-- + +--~ function fonts.define.specify.colonized(specification) -- xetex mode +--~ local list = { } +--~ if specification.detail and specification.detail ~= "" then +--~ for v in gmatch(specification.detail,"%s*([^;]+)%s*") do +--~ local a, b = match(v,"^(%S*)%s*=%s*(%S*)$") +--~ if a and b then +--~ list[a] = b:is_boolean() +--~ if type(list[a]) == "nil" then +--~ list[a] = b +--~ end +--~ else +--~ local a, b = match(v,"^([%+%-]?)%s*(%S+)$") +--~ if a and b then +--~ list[b] = a ~= "-" +--~ end +--~ end +--~ end +--~ end +--~ specification.features.normal = list +--~ return specification +--~ end + +--~ check("oeps/BI:+a;-b;c=d") +--~ check("[oeps]/BI:+a;-b;c=d") +--~ check("file:oeps/BI:+a;-b;c=d") +--~ check("name:oeps/BI:+a;-b;c=d") + +local list = { } + +fonts.define.specify.colonized_default_lookup = "file" + +local function issome () list.lookup = fonts.define.specify.colonized_default_lookup end +local function isfile () list.lookup = 'file' end +local function isname () list.lookup = 'name' end +local function thename(s) list.name = s end +local function issub (v) list.sub = v end +local function iscrap (s) list.crap = string.lower(s) end +local function istrue (s) list[s] = 'yes' end +local function isfalse(s) list[s] = 'no' end +local function iskey (k,v) list[k] = v end + +local function istrue (s) list[s] = true end +local function isfalse(s) list[s] = false end + +local spaces = lpeg.P(" ")^0 +local namespec = (1-lpeg.S("/:("))^0 -- was: (1-lpeg.S("/: ("))^0 +local crapspec = spaces * lpeg.P("/") * (((1-lpeg.P(":"))^0)/iscrap) * spaces +local filename = (lpeg.P("file:")/isfile * (namespec/thename)) + (lpeg.P("[") * lpeg.P(true)/isname * (((1-lpeg.P("]"))^0)/thename) * lpeg.P("]")) +local fontname = (lpeg.P("name:")/isname * (namespec/thename)) + lpeg.P(true)/issome * (namespec/thename) +local sometext = (lpeg.R("az","AZ","09") + lpeg.S("+-."))^1 +local truevalue = lpeg.P("+") * spaces * (sometext/istrue) +local falsevalue = lpeg.P("-") * spaces * (sometext/isfalse) +local keyvalue = (lpeg.C(sometext) * spaces * lpeg.P("=") * spaces * lpeg.C(sometext))/iskey +local somevalue = sometext/istrue +local subvalue = lpeg.P("(") * (lpeg.C(lpeg.P(1-lpeg.S("()"))^1)/issub) * lpeg.P(")") -- for Kim +local option = spaces * (keyvalue + falsevalue + truevalue + somevalue) * spaces +local options = lpeg.P(":") * spaces * (lpeg.P(";")^0 * option)^0 +local pattern = (filename + fontname) * subvalue^0 * crapspec^0 * options^0 + +local normalize_meanings = fonts.otf.meanings.normalize + +function fonts.define.specify.colonized(specification) -- xetex mode + list = { } + lpegmatch(pattern,specification.specification) +--~ for k, v in next, list do +--~ list[k] = v:is_boolean() +--~ if type(list[a]) == "nil" then +--~ list[k] = v +--~ end +--~ end + list.crap = nil -- style not supported, maybe some day + if list.name then + specification.name = list.name + list.name = nil + end + if list.lookup then + specification.lookup = list.lookup + list.lookup = nil + end + if list.sub then + specification.sub = list.sub + list.sub = nil + end +-- specification.features.normal = list + specification.features.normal = normalize_meanings(list) + return specification +end + +fonts.define.register_split(":", fonts.define.specify.colonized) diff --git a/tex/context/base/font-xtx.mkii b/tex/context/base/font-xtx.mkii new file mode 100644 index 000000000..d6086b44e --- /dev/null +++ b/tex/context/base/font-xtx.mkii @@ -0,0 +1,376 @@ +%D \module +%D [ file=font-xtx, +%D version=2004.09.11, +%D title=\CONTEXT\ Font Macros, +%D subtitle=\XETEX\ Hacks, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=\PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\ifnum\texengine=\xetexengine + \writestatus{loading}{ConTeXt Font Macros / XeTeX Hacks} +\else + \endinput +\fi + +\unprotect + +%D Loading: + +%D for some reason xetex does not support [filename] for tfm files and +%D quotes also behave kind of strange " vs ' vs [ vs ... +%D +%D \starttyping +%D \font\myfont = msam7 % ok +%D \font\myfont = "msam7" % also ok +%D \font\myfont = "msam7" at 8pt % error +%D \stoptyping + +\newconditional\tracexetexfonts + +%D Because \XETEX\ is not that fast on locating fonts we cache lookups so +%D that we minimize the test. It saves a little bit of runtime, depending +%D on the number of fonts loaded (which is normally not that much). + +\def\doiffoundXTXfontelse#1#2% + {\ifcsname xtx@fnt@#2\somefontspec\endcsname + \ifconditional\tracexetexfonts + \writestatus\m!fonts{already checked #1: #2\somefontspec\space (state: \number\csname xtx@fnt@#2\somefontspec\endcsname)}% + \fi + \else + \suppressfontnotfounderror\plusone + \font\xetextempfont=#2\somefontspec\relax + \suppressfontnotfounderror\zerocount + \edef\xetextempfont{\fontname\xetextempfont}% + \global\expandafter\chardef\csname xtx@fnt@#2\somefontspec\endcsname + \ifx\xetextempfont\nullfontname + \zerocount \ifconditional\tracexetexfonts + \writestatus\m!fonts{not found #1: #2\somefontspec}% + \fi + \else + \plusone \ifconditional\tracexetexfonts + \writestatus\m!fonts{found #1: #2\somefontspec}% + \fi + \fi + \fi + \ifcase\csname xtx@fnt@#2\somefontspec\endcsname + \expandafter\secondoftwoarguments + \else + \expandafter\firstoftwoarguments + \fi} + +\def\docheckfontfilenameprefix#1:#2:#3#4\relax + {\edef\!!stringa{#1}% + \edef\!!stringb{#2}% + \ifx\!!stringb\empty + % no prefix + \let\checkedfontfile\!!stringa + \doiffoundXTXfontelse{1a}{\checkedfontfile\checkedfontfeatures} + {\edef\checkedfontfile{\checkedfontfile\checkedfontfeatures}} + {\doiffoundXTXfontelse{1b}{"\checkedfontfile\checkedfontfeatures"} + {\edef\checkedfontfile{"\checkedfontfile\checkedfontfeatures"}} + {\doiffoundXTXfontelse{1c}{"[\checkedfontfile]\checkedfontfeatures"} + {\edef\checkedfontfile{"[\checkedfontfile]\checkedfontfeatures"}} + {}}}% + \else\ifx\!!stringa\v!file + % force file, only file check when no spaces + \let\checkedfontfile\!!stringb + \doiffoundXTXfontelse{2a}{"[\checkedfontfile]\checkedfontfeatures"} + {\edef\checkedfontfile{"[\checkedfontfile]\checkedfontfeatures"}} + {\doiffoundXTXfontelse{2b}{"\checkedfontfile\checkedfontfeatures"} + {\edef\checkedfontfile{"\checkedfontfile\checkedfontfeatures"}} + {}}% + \else\ifx\!!stringa\v!name + % force name, always lookup by xetex itself, "" forces otf/ttf/type1 + \edef\checkedfontfile{"\!!stringb\checkedfontfeatures"}% + \ifconditional\tracexetexfonts + \writestatus\m!fonts{no checking 3a: \checkedfontfile}% + \fi + \else + % whatever, maybe even xetex spec, forget about features + \edef\checkedfontfile{"\!!stringa\!!stringb"}% + \ifconditional\tracexetexfonts + \writestatus\m!fonts{no checking 3b: \checkedfontfile}% + \fi + \fi\fi\fi} + +\newconditional\enabledfontfeatures + +\def\checkfontfilename% -- todo: integrate so that we call do.. directly + {\expandafter\docheckfontfilename\fontfile*\empty*\relax} + +\def\docheckfontfilename#1*#2#3*#4\relax % class overrules file + {\settrue\enabledfontfeatures + \edef\checkedfontfeatures + {\expandafter\ifx\csname\fontclass\s!features\endcsname\empty + \ifx\@@fontfeatures\empty\ifx#2\empty\else#2#3\fi\else\@@fontfeatures\fi + \else\expandafter\ifx\csname\fontclass\s!features\endcsname\relax % redundant, will go away + \ifx\@@fontfeatures\empty\ifx#2\empty\else#2#3\fi\else\@@fontfeatures\fi + \else + \csname\fontclass\s!features\endcsname + \fi\fi}% + \ifx\checkedfontfeatures\empty + % done + \else + \edef\checkedfontfeatures{\executeifdefined{\??fa\checkedfontfeatures}\empty}% + \ifx\checkedfontfeatures\empty + % done + \else + \let\convertedfontfeatures\empty + \processcommacommand[\checkedfontfeatures]\doconvertfontfeatures % raw + \ifx\convertedfontfeatures\empty + \let\checkedfontfeatures\empty + \else\ifconditional\enabledfontfeatures + \edef\checkedfontfeatures{:\convertedfontfeatures}% + \else + \let\checkedfontfeatures\empty + \fi\fi + \fi + \fi + \docheckfontfilenameprefix#1:\empty:\empty\relax + \doshowcheckedfontfeatures} + +\edef\@@fontfeaturesareno {features\v!no} +\edef\@@fontfeaturesareoff{features\v!off} + +\def\dodoconvertfontfeatures#1=#2#3=#4\relax + {\ifx#2\empty + % invalid feature + \else\ifcsname @xtx@#1@#2#3\endcsname + \expandafter\ifx\csname @xtx@#1@#2#3\endcsname\empty\else + \edef\convertedfontfeatures{\convertedfontfeatures\csname @xtx@#1@#2#3\endcsname;}% + \fi + \else + \edef\!!stringa{#1}% + \edef\!!stringb{#2#3}% + \edef\!!stringc{#1#2#3}% + \ifx\!!stringc\@@fontfeaturesareoff + \setfalse\enabledfontfeatures + \else\ifx\!!stringc\@@fontfeaturesareno + \setfalse\enabledfontfeatures + \else + \edef\convertedfontfeatures + {\convertedfontfeatures + \ifx\!!stringb\v!yes + +\!!stringa + \else\ifx\!!stringb\v!on + +\!!stringa + \else\ifx\!!stringb\v!no + -\!!stringa + \else\ifx\!!stringb\v!off + -\!!stringa + \else + \!!stringa=\!!stringb + \fi\fi\fi\fi;}% + \fi\fi + \fi\fi} + +\def\doconvertfontfeatures#1% + {\dodoconvertfontfeatures#1=\empty=\relax} + +\def\remapfontfeature #1 #2 #3 {\setevalue{@xtx@#1@#2}{#3}} + +% this may move to another file, maybe font-xtx + +\remapfontfeature tlig yes mapping=tlig +%remapfontfeature tlig no mapping= +\remapfontfeature trep yes {} +\remapfontfeature trep no {} +\remapfontfeature texligatures yes mapping=tlig +%remapfontfeature texligatures no mapping= +%remapfontfeature texquotes yes mapping=tex-text +%remapfontfeature texquotes no mapping= + +%D Variants: + +\unexpanded\def\variant[#1]% + {\dosetscaledfont + \font\variantfont\truefontname{\fontstringA\fontstylesuffix\fontvariant\fontstringA{#1}} at \scaledfont + \variantfont} + +%D Possible optimizations: + +% \def\updatefontparameters +% {\edef\@@fontfeatures{\truefontdata\fontfile\s!features}% +% \edef\@@fontskewchar{\truefontdata\fontfile\s!skewchar}} + +% \def\setfontcharacteristics +% {\updatefontparameters % redundant, will go away, faster too +% \the\everyfont} + +% \let\synchronizepatternswithfont\relax + +%D Names: + +% We need to move the feature into the filename else it may be +% overloaded by another reference. For instance the definition of +% a regular and caps variant can use the same font. + +% We could use an indirect method ... store in 'array' and refer to +% slot. + +\def\definefontsynonym[#1]#2[#3]% + {\edef\@@fontname{#1}% + \edef\@@fontfile{#3}% + \doifnextoptionalelse\dodefinefontsynonym\nodefinefontsynonym} + +\def\nodefinefontsynonym + {\@EA\let\csname\??ff\fontclass\@@fontname\endcsname\@@fontfile} + +\def\dodefinefontsynonym[#1]% + {\edef\@@fontdata{#1}% + \ifx\@@fontdata\empty + \nodefinefontsynonym + \else + \ifx\fontclass\empty + \getfontparameters + \else + \getglobalfontparameters + \fi + \ifcsname\??ff\@@fontfile\s!features\endcsname + \@EA\edef\csname\??ff\fontclass\@@fontname\endcsname{\@@fontfile*\csname\??ff\@@fontfile\s!features\endcsname}% + \@EA\let\csname\??ff\@@fontfile\s!features\endcsname\undefined + \else + \nodefinefontsynonym + \fi + \fi} + +\let\definefontfile\definefontsynonym % dedicated to Taco Hoekwater + +% simple version +% +% \def\truefontname#1% +% {\@EA\dotruefontname#1*\relax} +% +% \def\dotruefontname#1*#2\relax +% {\ifcsname\??ff\fontclass#1\endcsname +% \@EA\truefontname\csname\??ff\fontclass#1\endcsname +% \else\ifcsname\??ff#1\endcsname +% \@EA\truefontname\csname\??ff#1\endcsname +% \else +% #1% +% \fi\fi} +% +% last counts +% +% \def\truefontname#1% +% {\@EA\dotruefontname#1*\empty*\relax} +% +% \def\dotruefontname#1*#2#3*#4\relax +% {\ifcsname\??ff\fontclass#1\endcsname +% \ifx#2\empty +% \@EA\truefontname\csname\??ff\fontclass#1\endcsname +% \else +% \@EA\truefontname\csname\??ff\fontclass#1\endcsname*#2#3% +% \fi +% \else\ifcsname\??ff#1\endcsname +% \ifx#2\empty +% \@EA\truefontname\csname\??ff#1\endcsname +% \else +% \@EA\truefontname\csname\??ff#1\endcsname*#2#3% +% \fi +% \else +% \ifx#2\empty +% #1% +% \else +% #1*#2#3% +% \fi +% \fi\fi} +% +% first counts + +\def\truefontname#1% + {\@EA\dotruefontname#1*\empty*\relax} + +\def\dotruefontname#1*#2#3*#4\relax + {\ifcsname\??ff\fontclass#1\endcsname + \ifx#2\empty + \@EA\truefontname\csname\??ff\fontclass#1\endcsname + \else + \@EA\redotruefontname\csname\??ff\fontclass#1\endcsname*#2#3% + \fi + \else\ifcsname\??ff#1\endcsname + \ifx#2\empty + \@EA\truefontname\csname\??ff#1\endcsname + \else + \@EA\redotruefontname\csname\??ff#1\endcsname*#2#3% + \fi + \else + #1\ifx#2\empty\else*#2#3\fi + \fi\fi} + +\def\redotruefontname#1% + {\@EA\dodotruefontname#1*\relax} + +\def\dodotruefontname#1*#2\relax + {\ifcsname\??ff\fontclass#1\endcsname + \@EA\redotruefontname\csname\??ff\fontclass#1\endcsname + \else\ifcsname\??ff#1\endcsname + \@EA\redotruefontname\csname\??ff#1\endcsname + \else + #1% + \fi\fi} + +%D Default: + +\def\defaultfontfile{file:lmmono10-regular} + +%D Maybe: + +% \def\updatefontparameters +% {\edef\@@fontfeatures{\truefontdata\fontfile \s!features}% +% \edef\@@fontskewchar{\truefontdata\fontfile \s!skewchar}} + +% \def\setfontcharacteristics +% {%\updatefontparameters % redundant, will go away, faster too +% \the\everyfont +% \synchronizepatternswithfont} + +\protect \endinput + +% \starttypescript[serif] [myzhfont] +% \definefontsynonym [Serif] [file:SimSun] +% \definefontsynonym [SerifBold] [file:SimSun] +% \definefontsynonym [SerifItalic] [file:SimSun] +% \definefontsynonym [SerifBoldItalic] [file:SimSun] +% \stoptypescript +% \starttypescript[sans] [myzhfont] +% \definefontsynonym [Sans] [file:SimSun] +% \definefontsynonym [SansBold] [file:SimSun] +% \definefontsynonym [SansItalic] [file:SimSun] +% \definefontsynonym [SansBoldItalic] [file:SimSun] +% \stoptypescript +% \starttypescript[mono] [myzhfont] +% \definefontsynonym [Mono] [file:SimSun] +% \definefontsynonym [MonoBold] [file:SimSun] +% \definefontsynonym [MonoItalic] [file:SimSun] +% \definefontsynonym [MonoBoldItalic] [file:SimSun] +% \stoptypescript +% \definetypeface [myzhfont] [rm] [serif][myzhfont] [default] +% \definetypeface [myzhfont] [ss] [sans] [myzhfont] [default] +% \definetypeface [myzhfont] [tt] [mono] [myzhfont] [default] + +% \starttext +% % on windows: make sure fonts.conf has no cache mentioned +% % +% % 64 sec xetex, 11 sec luatex (56 sec xetex when \nobigmath) +% % +% \setupbodyfont[myzhfont] \dorecurse{10000}{{hello {\switchtobodyfont[myzhfont] 你好}}\par} +% % +% % 67 sec xetex, 11.5 sec luatex +% % +% % \dorecurse{10000}{{hello {\switchtobodyfont[myzhfont] 你好}}\par} +% % +% % 5 sec xetex, 7 sec luatex +% % +% % \setupbodyfont[myzhfont] \dorecurse{10000}{{hello {你好}}\par} +% % +% % 5 sec xetex, 7 sec luatex +% % +% % \setupbodyfont[myzhfont] \dorecurse{10000}{{\bf hello {你好}}\par} +% \stoptext + diff --git a/tex/context/base/grph-fig.mkii b/tex/context/base/grph-fig.mkii new file mode 100644 index 000000000..c7f990af2 --- /dev/null +++ b/tex/context/base/grph-fig.mkii @@ -0,0 +1,559 @@ +%D \module +%D [ file=grph-fig, +%D version=2006.08.26, % overhaul of 1997.03.31 +%D title=\CONTEXT\ Graphic Macros, +%D subtitle=Figure Inclusion, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Graphic Macros / Figure Handling} + +\unprotect + +\def\setupexternalfigures + {\dosingleempty\dosetupexternalfigures} + +\def\dosetupexternalfigures[#1]% + {\getparameters[\??ef][#1]% local settings + \getparameters[\??ex][#1]% global settings + \setfigurepathlist} % the path may be used elsewhere too (as in x-res-04) + +\presetlocalframed[\??ef] + +\newconditional\externalfigurelevel % true=background false=normal +\newconditional\externalfigureflush % true=place false=ignore + +\setfalse\externalfigurelevel +\settrue \externalfigureflush + +\def\doplaceexternalfigure[#1][#2][#3][#4][#5]% + {\doifsomething{#2}% catches \defineexternalfigure dummies + {\doifundefinedelse{\??ef\??ef#2} + {\dodoplaceexternalfigure[#1][#2][#3][#4][#5]} + {\doifelse{#1}{#2} + {\dodoplaceexternalfigure[#1][#2][#3][#4][#5]} + {\getvalue{\??ef\??ef#2}[#5]}}}} + +\def\dodoplaceexternalfigure[#1][#2][#3][#4][#5]% + {\bgroup + \pushmacro\textunderscore + \edef\textunderscore{\string_}% brrr, temp hack, still needed? + \calculateexternalfigure [][#1][#2][#3][#4][#5]% [] is dummy dwcomp + \calculateexternalscreenfigure[][#1][#2][#3][#4][#5]% [] is dummy dwcomp + \popmacro\textunderscore + \box\foundexternalfigure + \egroup} + +\def\externalfigurereplacement#1#2#3% + {\setupcolors + [\c!state=\v!local]% + \expanded{\localframed + [\??ef] + [\c!width=\figurewidth, + \c!height=\figureheight, + \c!background=\v!screen, + \c!backgroundscreen=.8, + \c!frame=\@@efframe]}% + {\tt\tfxx \nohyphens + name: \expanded{\verbatimstring{#1}}\\% + file: \expanded{\verbatimstring{#2}}\\% + state: \expanded{\verbatimstring{#3}}}} + +\def\externalfigureplaceholder#1#2#3% + {\localframed + [\??ef] + [\c!width=#2, + \c!height=#3, + \c!frame=\v!on]% + {\tt\tfxx \nohyphens + name: \expanded{\verbatimstring{#1}}\\% + state: \expanded{\verbatimstring{placeholder}}}} + +% new: more convenient/efficient than +% +% \use..[a][a][setting] \externalfigure[b][a] +% +% is equivalent to: +% +% \def..[a][setting] \externalfigure[b][a] +% +% see x-res modules for usage: +% +% \defineexternalfigure[name][settings] + +\def\defineexternalfigure + {\dodoubleargument\dodefineexternalfigure} + +\def\dodefineexternalfigure[#1][#2]% + {\setvalue{\??ef\??ef#1}{\doplaceexternalfigure[#1][][][#2]}} + +\def\getexternalfigure#1% efef has 4 args already and take an 5th + {\wait} % OBSOLETE + +% \useexternalfigure[alpha][koe] +% \useexternalfigure[beta] [koe] [breedte=1cm] +% \useexternalfigure[gamma][koe][alpha] +% \useexternalfigure[delta][koe][alpha][breedte=2cm] +% +% volle breedte: \externalfigure[koe] \par +% 3cm breed: \externalfigure[koe] [breedte=3cm] \par +% volle breedte: \externalfigure[alpha] \par +% 1cm breed: \externalfigure[beta] \par +% volle breedte: \externalfigure[gamma] \par +% 2cm breed: \externalfigure[delta] \par +% 4cm breed: \externalfigure[beta] [breedte=4cm] \par +% 5cm breed: \externalfigure[gamma][breedte=5cm] \par + +% \defineexternalfigure[a][width=10cm] +% \defineexternalfigure[b][width=5cm] +% \externalfigure[cow][a] +% \externalfigure[cow][b][height=8cm] + +% \useexternalfigure[x][cow][width=10cm,height=1cm] +% \externalfigure[x] +% \externalfigure[x][width=3cm] + +\def\useexternalfigure + {\doquadrupleempty\douseexternalfigure} + +% [label] [filename] +% [label] [filename] [parent] +% [label] [filename] [parent] [settings] +% [label] [filename] [settings] + +\def\useexternalfigure + {\doquadrupleempty\douseexternalfigure} + +\def\douseexternalfigure[#1][#2][#3][#4]% + {\doifelsenothing{#1} + {\doifsomething{#2} + {\doifassignmentelse{#3} + {\setvalue{\??ef\??ef#2}{\doplaceexternalfigure[#2][#2][#3][#4]}} + {\setvalue{\??ef\??ef#2}{\doplaceexternalfigure[#2][#2][][#4]}}}} + {\doifelsenothing{#2} + {\doifassignmentelse{#3} + {\setvalue{\??ef\??ef#1}{\doplaceexternalfigure[#1][#1][][#3]}} + {\setvalue{\??ef\??ef#1}{\doplaceexternalfigure[#1][#1][#3][#4]}}} + {\doifassignmentelse{#3} + {\setvalue{\??ef\??ef#1}{\doplaceexternalfigure[#1][#2][][#3]}} + {\setvalue{\??ef\??ef#1}{\doplaceexternalfigure[#1][#2][#3][#4]}}}}} + +\def\dosetefparameters#1#2#3% parent_id use_settings current_settings + {\doifelsenothing{#1} % inherit from parent + {\getparameters[\??ef][#2,#3]} + {\doifdefinedelse{\??ef\??ef#1} + {\pushmacro\doplaceexternalfigure + \def\doplaceexternalfigure[##1][##2][##3][##4]{\getparameters[\??ef][##4,#2,#3]}% + \getvalue{\??ef\??ef#1}% + \popmacro\doplaceexternalfigure} + {\getparameters[\??ef][#2,#3]}}} + +\unexpanded\def\externalfigure + {\dotripleempty\doexternalfigure} + +\def\doexternalfigure[#1][#2][#3]% [label][file][settings] | [file][settings] | [file][parent][settings] + {\bgroup + \doifelsenothing{#1} + {\framed[\c!width=\defaultfigurewidth,\c!height=\defaultfigureheight]{external\\figure\\no name}} + {\doifundefinedelse{\??ef\??ef#1} + {\useexternalfigure[\s!dummy][#1][#2][#3]% + \getvalue{\??ef\??ef\s!dummy}[]} % [] is dummy arg 5 + {\doifassignmentelse{#2} + {\getvalue{\??ef\??ef#1}[#2]}% + {\getvalue{\??ef\??ef#1}[#3]}}}% + \globallet\currentresourcecomment\empty + \egroup} + +\long\def\resourcecomment#1% + {\long\gdef\currentresourcecomment{#1}} + +\long\def\startresourcecomment#1\stopresourcecomment + {\long\gdef\currentresourcecomment{#1}} + +\let\currentresourcecomment\empty + +\def\showexternalfigures % maybe run time command is better, but no core-run, unless figs-run ... + {%\writestatus\m!systems{for \string\showexternalfigures\space see \truefilename{x-res-20}.tex} + \usemodule[res-20]\showexternalfigures} % so for the moment we do it this way + +\def\overlayfigure#1% + {\externalfigure[#1][\c!width=\overlaywidth,\c!height=\overlayheight]} + +%D Still undocumented! No one uses it I think, better be done with layers. + +\newcount\efreference +\newdimen\efxsteps +\newdimen\efysteps + +\def\calculateefsteps + {\ifnum0\@@exxmax=\zerocount + \ifnum0\@@exymax=\zerocount + \def\@@exymax{24}% + \fi + \efysteps\figureheight \divide\efysteps \@@exymax + \efxsteps\efysteps + \dimen0=\figurewidth + \advance\dimen0 \efysteps + \divide \dimen0 \efysteps + \edef\@@exxmax{\number\dimen0}% + \else + \efxsteps\figurewidth \divide\efxsteps \@@exxmax + \efysteps\figureheight \divide\efysteps \@@exymax + \fi} + +\def\efcomment#1(#2,#3)#4(#5,#6)% {kader}(x,y)(h,b)[...]{tekst} + {\def\complexefdocomment[##1]##2% + {\position(#2,#3)% + {\setnostrut + \framed + [\c!width=#5\efxsteps, + \c!height=#6\exysteps, + \c!offset=\v!none, + \c!frame=#1, + ##1]% + {##2}}}% + \complexorsimpleempty\efdocomment} + +\def\efnocomment(#1,#2)#3(#4,#5)% (x,y)(h,b)[...]{tekst} + {\def\complexefdonocomment[##1]##2{}% + \complexorsimpleempty\efdonocomment} + +\def\efdomarker(#1,#2)#3#4% (h,b){kader}{tekst} + {\framed + [\c!width=#1\efxsteps, + \c!height=#2\efysteps, + \c!offset=\v!none, + \c!frame=#3]% + {#4}} + +\def\effigure#1% + {\position(0,0){\getvalue{#1}}} + +\def\efdoarea(#1,#2)#3#4% (h,b){kader}{tekst} + {\bgroup + \setnostrut + \framed + [\c!width=#1\efxsteps, + \c!height=#2\efysteps, + \c!offset=\!!zeropoint, + \c!frame=#3] + {#4}% + \egroup} + +\def\efgoto(#1,#2)#3[#4]% (h,b)kader[ref] + {\setbox0=\vbox{\efdoarea(#1,#2)#3{}}% + \gotobox{\copy0}[#4]} + +\def\efmark(#1,#2)#3(#4,#5)#6[#7]% + {\advance\efreference \plusone + \position(#1,#2) + {\hbox{\the\efreference}}% + \position(#1,#2) + {\gotosomeinternal\s!vwb{#7}\realfolio + {\efdomarker(#4,#5)\v!on{\thisissomeinternal\s!vwa{#7}}}}} + +\def\eftext#1(#2,#3)#4(#5,#6)#7[#8]% + {\advance\efreference \plusone + \hbox + {\quad + \thisissomeinternal\s!vwb{#8}% + \gotosomeinternal \s!vwa{#8}\realfolio + {\hbox to 1.5em{\the\efreference\presetgoto\hfill}}% + \quad#1 (#2,#3) (#5,#6) [#8]\hfill}% + \endgraf} + +\def\efthisis(#1,#2)#3[#4]% + {\efdoarea(#1,#2){#3}{\pagereference[#4]}} + +\newbox\colorbarbox + +\def\makecolorbar[#1]% + {\def\docommand##1% + {\color[##1] + {\blackrule + [\c!width=2em, + \c!height=1ex, + \c!depth=\!!zeropoint]}% + \endgraf}% + \global\setbox\colorbarbox\vbox + {\forgetall + \processcommalist[#1]\docommand}% + \global\setbox\colorbarbox\vbox + {\hskip2em\box\colorbarbox}% + \global\wd\colorbarbox\zeropoint} + +\def\placestartfigure[#1][#2][#3]#4\placestopfigure[#5]% + {\hbox + {\setbox0\hbox + {\useexternalfigure[\s!dummy][#2][#3,#5]% + \externalfigure[\s!dummy]}% + \calculateefsteps + \startpositioning + \def\referring(##1,##2)##3(##4,##5)##6[##7]% + {\position(##1,##2){\efgoto(##4,##5){\@@exframes}[##7]}}% + \def\marking(##1,##2)##3(##4,##5)##6[##7]% + {\position(##1,##2){\efthisis(##4,##5){\@@exframes}[##7]}}% + \def\remark{\efnocomment}% + \def\colorbar##1[##2]{}% + \position(0,0){\box0}% + \linewidth\onepoint + \setuppositioning + [\c!unit=pt, + \c!xscale=\withoutpt\the\efxsteps, + \c!yscale=\withoutpt\the\efysteps, + \c!factor=1]% + \ignorespaces#4% + \def\referring(##1,##2)##3(##4,##5)##6[##7]% + {}% + \let\marking\referring + \def\remark{\efcomment\v!no}% + \def\colorbar##1[##2]{\makecolorbar[##2]}% + \ignorespaces#4% + \stoppositioning + \box\colorbarbox}} + +\def\dodostartfigure[#1][#2][#3]#4\stopfigure + {\doifelse\v!test\@@exoption + {\teststartfigure[#1][#2][#3]#4\teststopfigure + \let\@@exframes\v!on} + {\let\@@exframes\v!off}% + \setvalue{\??ef\??ef#1}% + {\dosingleempty{\placestartfigure[#1][#2][#3]#4\placestopfigure}}% + }% no longer \doifundefined{#1}{\setvalue{#1}{\getexternalfigure{#1}}}} + +% De onderstaande macro mag niet zondermeer worden aangepast +% en is afgestemd op gebruik in de handleiding. + +\def\teststartfigure[#1][#2][#3]#4\teststopfigure% + {\begingroup + \setbox0\hbox + {\useexternalfigure[\s!dummy][#2][\c!wfactor=\v!max]% + \externalfigure[\s!dummy]}% + \def\referring{\efmark}% + \def\marking{\efmark}% + \def\remark{\efcomment\v!yes}% + \def\colorbar##1[##2]{}% + \efreference\zerocount + \setbox0\vbox + {\hsize240pt + \startpositioning + \calculateefsteps + \position(0,0) + {\box0}% + \position(0,0) + {\basegrid + [\c!nx=\@@exxmax, + \c!dx=\withoutpt\the\efxsteps, + \c!ny=\@@exymax, + \c!dy=\withoutpt\the\efysteps, + \c!xstep=1, + \c!ystep=1, + \c!scale=1, + \c!offset=\v!no, + \c!unit=pt]}% + \setuppositioning + [\c!unit=pt, + \c!xscale=\withoutpt\the\efxsteps, + \c!yscale=\withoutpt\the\efysteps, + \c!factor=1]% + \linewidth\onepoint + \ignorespaces#4\relax + \stoppositioning + \vfill}% + \efreference\zerocount + \def\referring{\eftext{$\rightarrow$}}% + \def\marking{\eftext{$\leftarrow$}}% + \def\remark{\efnocomment}% + \def\colorbar##1[##2]{}% + \setbox2\vbox + {{\tfa\doifelsenothing{#1}{#2}{#1}} + \blank + \tfxx#4 + \vfilll}% + \ifdim\ht0>\ht2 + \ht2\ht0 + \else + \ht0\ht2 + \fi + \hbox + {\hskip3em + \vtop{\vskip12pt\box0\vskip6pt}% + \vtop{\vskip12pt\box2\vskip6pt}}% + \endgroup} + +\def\dodostartfigure[#1][#2][#3]#4\stopfigure + {\doifelse\v!test\@@exoption + {\teststartfigure[#1][#2][#3]#4\teststopfigure + \let\@@exframe\v!on} + {\let\@@exframe\v!off}% + \setvalue{\??ef\??ef#1}% + {\def\next{\placestartfigure[#1][#2][#3]#4\placestopfigure}% + \dosingleempty\next}% + }% no longer: \doifundefined{#1}{\setvalue{#1}{\getexternalfigure{#1}}}} + +\long\def\dostartfigure#1% + {\dotripleargument\dodostartfigure#1\stopfigure} + +\def\startfigure + {\grabuntil{\e!stop\v!figure}\dostartfigure} + +%D defining sound tracks: +%D +%D \starttyping +%D \useexternalsoundtrack[label][file] +%D \stoptyping +%D +%D associated actions: StartSound StopSound PauseSound ResumeSound +%D +%D Todo: like external figures, also search on path, +%D although, they need to be present ar viewing time, so ... + +\def\useexternalsoundtrack + {\dodoubleargument\douseexternalsoundtrack} + +\def\douseexternalsoundtrack[#1][#2]% + {\setgvalue{\??sd:#1}{#2}} + +\def\checksoundtrack#1% + {\iflocation + \doifdefined{\??sd:#1}{\doifvaluesomething{\??sd:#1} + {\doinsertsoundtrack{\getvalue{\??sd:#1}}{#1}\@@sdoption + % brr, \..empty not really needed and maybe even wrong; + % also, not here but in driver + % well, no: sounds need to be reinitialize each time (i.e., be on page), so no + }}% \letgvalueempty{\??sd:#1}}}% + \fi} + +\setexecutecommandcheck {startsound} \checksoundtrack + +\def\setupexternalsoundtracks + {\dodoubleargument\getparameters[\??sd]} + +\setupexternalsoundtracks + [\c!option=] + +%D NEW: used in styledesign manual + +% \setbuffer[typeset-b]\endbuffer +% \setbuffer[typeset-a]\endbuffer +% +% todo: +% +% \appendtoks \setbuffer[typeset-b]\endbuffer\to \everystarttext +% \appendtoks \setbuffer[typeset-a]\endbuffer\to \everystarttext + +\def\typesetbuffer + {\dodoubleempty\dotypesetbuffer} + +\newcounter\noftypesetbuffers % all loaded at the end + +\defineexternalfigure + [typeset] + [\c!background=\v!color, + \c!backgroundcolor=\s!white] + +\def\dotypesetbuffer[#1][#2]% beware: this will mix up the mp graphics + {\bgroup + \def\TEXbufferfile##1{\bufferprefix##1.tex}% + \expanded{\setbuffer[typeset]% + \def\noexpand\bufferprefix{\ifprotectbuffers\jobname-\fi typeset-}}% + \starttext + \getbuffer[b,#1,a]% + \stoptext + \endbuffer + \doglobal\increment\noftypesetbuffers + % batch is needed + \executesystemcommand{texmfstart texexec --batch --pdf --result=\bufferprefix typeset-\noftypesetbuffers\space \bufferprefix typeset.tex}% + %\externalfigure[\bufferprefix typeset-\noftypesetbuffers.pdf][\c!object=\v!no,#2]% + \externalfigure[\bufferprefix typeset-\noftypesetbuffers.pdf][#2]% + \egroup} + +% for me only (manuals and such) + +\definesystemvariable{tz} + +\def\definetypesetting{\dotripleempty\dodefinetypesetting} +\def\typesetfile {\dotripleempty\dotypesetfile} + +\def\dodefinetypesetting[#1][#2][#3]% + {\doifsomething{#1}{\setvalue{\??tz#1}{\dodotypesetfile{#2}{#3}}}} + +\def\dotypesetfile[#1][#2][#3]% + {\executeifdefined{\??tz#1}\gobbletwoarguments{#2}{#3}} + +\def\dodotypesetfile#1#2#3#4% args settings file settings + {\doifmode{*\v!first}{\executesystemcommand{texmfstart texexec.pl --batch --pdf #1 #3}}% + \doglobal\beforesplitstring#3\at.\to\typesetfilename + \externalfigure[\typesetfilename.pdf][#2,#4]} + +\setupexternalfigures + [\c!option=, + \c!object=\v!yes, % we only check for no + \c!reset=\v!no, + \c!maxwidth=\@@efwidth, + \c!maxheight=\@@efheight, + \c!bodyfont=\bodyfontsize, + \c!directory=, + \c!file=\f!utilityfilename.\f!figureextension, + \c!radius=.5\bodyfontsize, + \c!corner=\v!rectangular, + \c!frame=\v!off, + \c!background=, % new + \c!splitcolor=\s!white, + \c!conversion=, + \c!prefix=, + \c!cache=, +% \c!grid=, + \c!equalwidth=, + \c!equalheight=, + \c!location={\v!local,\v!global}] + +\setupexternalfigures + [\c!frames=\v!off, + \c!ymax=24, + \c!xmax=] + +\useexternalfigure + [buffer] [\jobname] [\c!type=\v!buffer,\c!object=\v!no] + +\protect \endinput + +% alternative for positioning + +% \definelayer[figure][width=\overlaywidth,height=\overlayheight] +% \defineoverlay[figure][{\directsetup{figure}\tightlayer[figure]}] + +% \setupcolors[state=start] + +% \starttext + +% \startsetups figure +% \setlayerframed[figure][preset=rightbottom,x=.25\layerwidth,y=.25\layerheight]{HERE} +% \setlayerframed[figure][preset=leftbottom, x=.15\layerwidth,y=.35\layerheight]{THERE} +% \stopsetups + +% \externalfigure[cow][background={foreground,figure},width=4cm,height=8cm] + +% \startsetups figure +% \setlayerframed[figure][preset=righttop,x=.25\layerwidth,y=.25\layerheight]{MORE} +% \setlayerframed[figure][preset=middle,foregroundcolor=green]{EVEN MORE} +% \stopsetups + +% \externalfigure[cow][background={foreground,figure},width=14cm,height=2cm] + +% \defineexternalfigure[whatever][background={foreground,figure}] + +% \startsetups figure +% \setlayerframed[figure][preset=righttop,x=.25\layerwidth,y=.25\layerheight]{\red MORE} +% \setlayerframed[figure][preset=middle,foregroundcolor=green]{EVEN MORE} +% \stopsetups + +% \externalfigure[cow][whatever][width=14cm,height=4cm] + +% \stoptext + diff --git a/tex/context/base/grph-fig.mkiv b/tex/context/base/grph-fig.mkiv new file mode 100644 index 000000000..e10dc0a32 --- /dev/null +++ b/tex/context/base/grph-fig.mkiv @@ -0,0 +1,640 @@ +%D \module +%D [ file=grph-fig, +%D version=2006.08.26, % overhaul of 1997.03.31 +%D title=\CONTEXT\ Graphic Macros, +%D subtitle=Figure Inclusion, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Graphic Macros / Figure Handling} + +\unprotect + +\unexpanded\def\setupexternalfigures + {\dosingleempty\dosetupexternalfigures} + +\def\dosetupexternalfigures[#1]% + {\getparameters[\??ef][#1]% local settings + \getparameters[\??ex][#1]% global settings + \setfigurepathlist} % the path may be used elsewhere too (as in x-res-04) + +\presetlocalframed[\??ef] + +\newconditional\externalfigurelevel % true=background false=normal +\newconditional\externalfigureflush % true=place false=ignore + +\setfalse\externalfigurelevel +\settrue \externalfigureflush + +\def\doplaceexternalfigure[#1][#2][#3][#4][#5]% + {\doifsomething{#2}% catches \defineexternalfigure dummies + {\doifundefinedelse{\??ef\??ef#2} + {\dodoplaceexternalfigure[#1][#2][#3][#4][#5]} + {\doifelse{#1}{#2} + {\dodoplaceexternalfigure[#1][#2][#3][#4][#5]} + {\getvalue{\??ef\??ef#2}[#5]}}}} + +\def\dodoplaceexternalfigure[#1][#2][#3][#4][#5]% + {\bgroup + \pushmacro\textunderscore + \edef\textunderscore{\string_}% brrr, temp hack, still needed? + \calculateexternalfigure[][#1][#2][#3][#4][#5]% [] is dummy dwcomp + \popmacro\textunderscore + \box\foundexternalfigure + \egroup} + +\def\externalfigurereplacement#1#2#3% + {\setupcolors + [\c!state=\v!local]% + \expanded{\localframed + [\??ef] + [\c!width=\figurewidth, + \c!height=\figureheight, + \c!background=\v!color, + \c!backgroundcolor=missingfigurecolor, + \c!frame=\@@efframe]}% + {\tt\tfxx \nohyphens + name: \expanded{\verbatimstring{#1}}\\% + file: \expanded{\verbatimstring{#2}}\\% + state: \expanded{\verbatimstring{#3}}}} + +\definecolor[missingfigurecolor][s=.8] + +\def\externalfigureplaceholder#1#2#3% + {\localframed + [\??ef] + [\c!width=#2, + \c!height=#3, + \c!frame=\v!on]% + {\tt\tfxx \nohyphens + name: \expanded{\verbatimstring{#1}}\\% + state: \expanded{\verbatimstring{placeholder}}}} + +% new: more convenient/efficient than +% +% \use..[a][a][setting] \externalfigure[b][a] +% +% is equivalent to: +% +% \def..[a][setting] \externalfigure[b][a] +% +% see x-res modules for usage: +% +% \defineexternalfigure[name][settings] + +\unexpanded\def\defineexternalfigure + {\dodoubleargument\dodefineexternalfigure} + +\def\dodefineexternalfigure[#1][#2]% + {\setvalue{\??ef\??ef#1}{\doplaceexternalfigure[#1][][][#2]}} + +\def\getexternalfigure#1% efef has 4 args already and take an 5th + {\wait} % OBSOLETE + +% \useexternalfigure[alpha][koe] +% \useexternalfigure[beta] [koe] [breedte=1cm] +% \useexternalfigure[gamma][koe][alpha] +% \useexternalfigure[delta][koe][alpha][breedte=2cm] +% +% volle breedte: \externalfigure[koe] \par +% 3cm breed: \externalfigure[koe] [breedte=3cm] \par +% volle breedte: \externalfigure[alpha] \par +% 1cm breed: \externalfigure[beta] \par +% volle breedte: \externalfigure[gamma] \par +% 2cm breed: \externalfigure[delta] \par +% 4cm breed: \externalfigure[beta] [breedte=4cm] \par +% 5cm breed: \externalfigure[gamma][breedte=5cm] \par + +% \defineexternalfigure[a][width=10cm] +% \defineexternalfigure[b][width=5cm] +% \externalfigure[cow][a] +% \externalfigure[cow][b][height=8cm] + +% \useexternalfigure[x][cow][width=10cm,height=1cm] +% \externalfigure[x] +% \externalfigure[x][width=3cm] + +\def\useexternalfigure + {\doquadrupleempty\douseexternalfigure} + +% [label] [filename] +% [label] [filename] [parent] +% [label] [filename] [parent] [settings] +% [label] [filename] [settings] + +\def\useexternalfigure + {\doquadrupleempty\douseexternalfigure} + +% \def\douseexternalfigure[#1][#2][#3][#4]% +% {\doifelsenothing{#1} +% {\doifsomething{#2} +% {\doifassignmentelse{#3} +% {\setvalue{\??ef\??ef#2}{\doplaceexternalfigure[#2][#2][#3][#4]}} +% {\setvalue{\??ef\??ef#2}{\doplaceexternalfigure[#2][#2][][#4]}}}} +% {\doifelsenothing{#2} +% {\doifassignmentelse{#3} +% {\setvalue{\??ef\??ef#1}{\doplaceexternalfigure[#1][#1][][#3]}} +% {\setvalue{\??ef\??ef#1}{\doplaceexternalfigure[#1][#1][#3][#4]}}} +% {\doifassignmentelse{#3} +% {\setvalue{\??ef\??ef#1}{\doplaceexternalfigure[#1][#2][][#3]}} +% {\setvalue{\??ef\??ef#1}{\doplaceexternalfigure[#1][#2][#3][#4]}}}}} + +\def\douseexternalfigure[#1][#2][#3][#4]% + {\doifelsenothing{#1} + {\doifsomething{#2} + {\doifassignmentelse{#3} + {\dodouseexternalfigure{#2}{#2}{#3}{#4}} + {\dodouseexternalfigure{#2}{#2}{}{#4}}}} + {\doifelsenothing{#2} + {\doifassignmentelse{#3} + {\dodouseexternalfigure{#1}{#1}{}{#3}} + {\dodouseexternalfigure{#1}{#1}{#3}{#4}}} + {\doifassignmentelse{#3} + {\dodouseexternalfigure{#1}{#2}{}{#3}} + {\dodouseexternalfigure{#1}{#2}{#3}{#4}}}}} + +\def\dodouseexternalfigure#1#2#3#4% + {\setvalue{\??ef\??ef#1}{\doplaceexternalfigure[#1][#2][#3][#4]}% + \doanalyseexternalfigurecollection[#2][#4]} + +\newconditional\inexternalfigurecollection +\newdimen\xexternalfigurecollectionminwidth +\newdimen\xexternalfigurecollectionmaxwidth +\newdimen\xexternalfigurecollectionminheight +\newdimen\xexternalfigurecollectionmaxheight + +\def\doanalyseexternalfigurecollection[#1][#2]% + {\ifconditional\inexternalfigurecollection + \setfalse\inexternalfigurecollection + \getfiguredimensions[#1][#2]% + \settrue\inexternalfigurecollection + \scratchdimen\naturalfigurewidth + \ifdim\scratchdimen>\xexternalfigurecollectionmaxwidth \xexternalfigurecollectionmaxwidth \scratchdimen \fi + \ifdim\scratchdimen<\xexternalfigurecollectionminwidth \xexternalfigurecollectionminwidth \scratchdimen \fi + \scratchdimen\naturalfigureheight + \ifdim\scratchdimen>\xexternalfigurecollectionmaxheight \xexternalfigurecollectionmaxheight\scratchdimen \fi + \ifdim\scratchdimen<\xexternalfigurecollectionminheight \xexternalfigurecollectionminheight\scratchdimen \fi + \fi} + +\unexpanded\def\startexternalfigurecollection[#1]% + {\begingroup + \def\currentexternalfigurecollection{#1}% + \settrue\inexternalfigurecollection + \xexternalfigurecollectionminwidth \maxdimen + \xexternalfigurecollectionmaxwidth \zeropoint + \xexternalfigurecollectionminheight\maxdimen + \xexternalfigurecollectionmaxheight\zeropoint} + +\unexpanded\def\stopexternalfigurecollection + {\setxvalue{\??ef:c:\currentexternalfigurecollection:\c!minwidth }{\the\xexternalfigurecollectionminwidth }% + \setxvalue{\??ef:c:\currentexternalfigurecollection:\c!maxwidth }{\the\xexternalfigurecollectionmaxwidth }% + \setxvalue{\??ef:c:\currentexternalfigurecollection:\c!minheight}{\the\xexternalfigurecollectionminheight}% + \setxvalue{\??ef:c:\currentexternalfigurecollection:\c!maxheight}{\the\xexternalfigurecollectionmaxheight}% + \endgroup} + +\def\externalfigurecollectionparameter#1#2% + {\csname\ifcsname\??ef:c:#1:#2\endcsname\??ef:c:#1:#2\else\s!empty\fi\endcsname} + +\def\externalfigurecollectionminwidth #1{\externalfigurecollectionparameter{#1}\c!minwidth } +\def\externalfigurecollectionmaxwidth #1{\externalfigurecollectionparameter{#1}\c!maxwidth } +\def\externalfigurecollectionminheight#1{\externalfigurecollectionparameter{#1}\c!minheight} +\def\externalfigurecollectionmaxheight#1{\externalfigurecollectionparameter{#1}\c!maxheight} + +\let\efcparameter\externalfigurecollectionparameter +\let\efcminwidth \externalfigurecollectionminwidth +\let\efcmaxwidth \externalfigurecollectionmaxwidth +\let\efcminheight\externalfigurecollectionminheight +\let\efcmaxheight\externalfigurecollectionmaxheight + +% \startexternalfigurecollection[name] +% \useexternalfigure[cow] [cow.pdf] +% \useexternalfigure[mill][mill.png] +% \stopexternalfigurecollection +% \starttext +% \bTABLE +% \bTR +% \bTD \externalfigure[cow] [height=\externalfigurecollectionmaxheight{name}] \eTD +% \bTD \externalfigure[mill][height=\externalfigurecollectionmaxheight{name}] \eTD +% \eTR +% \eTABLE +% \stoptext + +\def\dosetefparameters#1#2#3% parent_id use_settings current_settings + {\doifelsenothing{#1} % inherit from parent + {\getparameters[\??ef][#2,#3]} + {\doifdefinedelse{\??ef\??ef#1} + {\pushmacro\doplaceexternalfigure + \def\doplaceexternalfigure[##1][##2][##3][##4]{\getparameters[\??ef][##4,#2,#3]}% + \getvalue{\??ef\??ef#1}% + \popmacro\doplaceexternalfigure} + {\getparameters[\??ef][#2,#3]}}} + +\unexpanded\def\externalfigure + {\dotripleempty\doexternalfigure} + +\def\doexternalfigure[#1][#2][#3]% [label][file][settings] | [file][settings] | [file][parent][settings] + {\bgroup + \doifelsenothing{#1} + {\framed[\c!width=\defaultfigurewidth,\c!height=\defaultfigureheight]{external\\figure\\no name}} + {\doifundefinedelse{\??ef\??ef#1} + {\useexternalfigure[\s!dummy][#1][#2][#3]% + \getvalue{\??ef\??ef\s!dummy}[]} % [] is dummy arg 5 + {\doifassignmentelse{#2} + {\getvalue{\??ef\??ef#1}[#2]}% + {\getvalue{\??ef\??ef#1}[#3]}}}% + \globallet\currentresourcecomment\empty + \egroup} + +\long\def\resourcecomment#1% + {\long\gdef\currentresourcecomment{#1}} + +\long\unexpanded\def\startresourcecomment#1\stopresourcecomment + {\long\gdef\currentresourcecomment{#1}} + +\let\currentresourcecomment\empty + +\def\showexternalfigures % maybe run time command is better, but no core-run, unless figs-run ... + {%\writestatus\m!systems{for \string\showexternalfigures\space see \truefilename{x-res-20}.tex} + \usemodule[res-20]\showexternalfigures} % so for the moment we do it this way + +\def\overlayfigure#1% + {\externalfigure[#1][\c!width=\overlaywidth,\c!height=\overlayheight]} + +%D Still undocumented! No one uses it I think, better be done with layers. + +% when there is need for this i'll reimplement it +% +% \newcount\efreference +% \newdimen\efxsteps +% \newdimen\efysteps +% +% \def\calculateefsteps +% {\ifnum0\@@exxmax=\zerocount +% \ifnum0\@@exymax=\zerocount +% \def\@@exymax{24}% +% \fi +% \efysteps\figureheight \divide\efysteps \@@exymax +% \efxsteps\efysteps +% \dimen0=\figurewidth +% \advance\dimen0 \efysteps +% \divide \dimen0 \efysteps +% \edef\@@exxmax{\number\dimen0}% +% \else +% \efxsteps\figurewidth \divide\efxsteps \@@exxmax +% \efysteps\figureheight \divide\efysteps \@@exymax +% \fi} +% +% \def\efcomment#1(#2,#3)#4(#5,#6)% {kader}(x,y)(h,b)[...]{tekst} +% {\def\complexefdocomment[##1]##2% +% {\position(#2,#3)% +% {\setnostrut +% \framed +% [\c!width=#5\efxsteps, +% \c!height=#6\exysteps, +% \c!offset=\v!none, +% \c!frame=#1, +% ##1]% +% {##2}}}% +% \complexorsimpleempty\efdocomment} +% +% \def\efnocomment(#1,#2)#3(#4,#5)% (x,y)(h,b)[...]{tekst} +% {\def\complexefdonocomment[##1]##2{}% +% \complexorsimpleempty\efdonocomment} +% +% \def\efdomarker(#1,#2)#3#4% (h,b){kader}{tekst} +% {\framed +% [\c!width=#1\efxsteps, +% \c!height=#2\efysteps, +% \c!offset=\v!none, +% \c!frame=#3]% +% {#4}} +% +% \def\effigure#1% +% {\position(0,0){\getvalue{#1}}} +% +% \def\efdoarea(#1,#2)#3#4% (h,b){kader}{tekst} +% {\bgroup +% \setnostrut +% \framed +% [\c!width=#1\efxsteps, +% \c!height=#2\efysteps, +% \c!offset=\!!zeropoint, +% \c!frame=#3] +% {#4}% +% \egroup} +% +% \def\efgoto(#1,#2)#3[#4]% (h,b)kader[ref] +% {\setbox0=\vbox{\efdoarea(#1,#2)#3{}}% +% \gotobox{\copy0}[#4]} +% +% \def\efmark(#1,#2)#3(#4,#5)#6[#7]% +% {\advance\efreference \plusone +% \position(#1,#2) +% {\hbox{\the\efreference}}% +% \position(#1,#2) +% {\gotosomeinternal\s!vwb{#7}\realfolio +% {\efdomarker(#4,#5)\v!on{\thisissomeinternal\s!vwa{#7}}}}} +% +% \def\eftext#1(#2,#3)#4(#5,#6)#7[#8]% +% {\advance\efreference \plusone +% \hbox +% {\quad +% \thisissomeinternal\s!vwb{#8}% +% \gotosomeinternal \s!vwa{#8}\realfolio +% {\hbox to 1.5em{\the\efreference\presetgoto\hfill}}% +% \quad#1 (#2,#3) (#5,#6) [#8]\hfill}% +% \endgraf} +% +% \def\efthisis(#1,#2)#3[#4]% +% {\efdoarea(#1,#2){#3}{\pagereference[#4]}} + +\newbox\colorbarbox + +\def\makecolorbar[#1]% + {\def\docommand##1% + {\color[##1] + {\blackrule + [\c!width=2em, + \c!height=1ex, + \c!depth=\!!zeropoint]}% + \endgraf}% + \global\setbox\colorbarbox\vbox + {\forgetall + \processcommalist[#1]\docommand}% + \global\setbox\colorbarbox\vbox + {\hskip2em\box\colorbarbox}% + \global\wd\colorbarbox\zeropoint} + +\unexpanded\def\placestartfigure[#1][#2][#3]#4\placestopfigure[#5]% + {\hbox + {\setbox0\hbox + {\useexternalfigure[\s!dummy][#2][#3,#5]% + \externalfigure[\s!dummy]}% + \calculateefsteps + \startpositioning + \def\referring(##1,##2)##3(##4,##5)##6[##7]% + {\position(##1,##2){\efgoto(##4,##5){\@@exframes}[##7]}}% + \def\marking(##1,##2)##3(##4,##5)##6[##7]% + {\position(##1,##2){\efthisis(##4,##5){\@@exframes}[##7]}}% + \def\remark{\efnocomment}% + \def\colorbar##1[##2]{}% + \position(0,0){\box0}% + \linewidth\onepoint + \setuppositioning + [\c!unit=pt, + \c!xscale=\withoutpt\the\efxsteps, + \c!yscale=\withoutpt\the\efysteps, + \c!factor=1]% + \ignorespaces#4% + \def\referring(##1,##2)##3(##4,##5)##6[##7]% + {}% + \let\marking\referring + \def\remark{\efcomment\v!no}% + \def\colorbar##1[##2]{\makecolorbar[##2]}% + \ignorespaces#4% + \stoppositioning + \box\colorbarbox}} + +\def\dodostartfigure[#1][#2][#3]#4\stopfigure + {\doifelse\v!test\@@exoption + {\teststartfigure[#1][#2][#3]#4\teststopfigure + \let\@@exframes\v!on} + {\let\@@exframes\v!off}% + \setvalue{\??ef\??ef#1}% + {\dosingleempty{\placestartfigure[#1][#2][#3]#4\placestopfigure}}% + }% no longer \doifundefined{#1}{\setvalue{#1}{\getexternalfigure{#1}}}} + +% De onderstaande macro mag niet zondermeer worden aangepast +% en is afgestemd op gebruik in de handleiding. + +\def\teststartfigure[#1][#2][#3]#4\teststopfigure% + {\begingroup + \setbox0\hbox + {\useexternalfigure[\s!dummy][#2][\c!wfactor=\v!max]% + \externalfigure[\s!dummy]}% + \def\referring{\efmark}% + \def\marking{\efmark}% + \def\remark{\efcomment\v!yes}% + \def\colorbar##1[##2]{}% + \efreference\zerocount + \setbox0\vbox + {\hsize240pt + \startpositioning + \calculateefsteps + \position(0,0) + {\box0}% + \position(0,0) + {\basegrid + [\c!nx=\@@exxmax, + \c!dx=\withoutpt\the\efxsteps, + \c!ny=\@@exymax, + \c!dy=\withoutpt\the\efysteps, + \c!xstep=1, + \c!ystep=1, + \c!scale=1, + \c!offset=\v!no, + \c!unit=pt]}% + \setuppositioning + [\c!unit=pt, + \c!xscale=\withoutpt\the\efxsteps, + \c!yscale=\withoutpt\the\efysteps, + \c!factor=1]% + \linewidth\onepoint + \ignorespaces#4\relax + \stoppositioning + \vfill}% + \efreference\zerocount + \def\referring{\eftext{$\rightarrow$}}% + \def\marking{\eftext{$\leftarrow$}}% + \def\remark{\efnocomment}% + \def\colorbar##1[##2]{}% + \setbox2\vbox + {{\tfa\doifelsenothing{#1}{#2}{#1}} + \blank + \tfxx#4 + \vfilll}% + \ifdim\ht0>\ht2 + \ht2\ht0 + \else + \ht0\ht2 + \fi + \hbox + {\hskip3em + \vtop{\vskip12pt\box0\vskip6pt}% + \vtop{\vskip12pt\box2\vskip6pt}}% + \endgroup} + +\def\dodostartfigure[#1][#2][#3]#4\stopfigure + {\doifelse\v!test\@@exoption + {\teststartfigure[#1][#2][#3]#4\teststopfigure + \let\@@exframe\v!on} + {\let\@@exframe\v!off}% + \setvalue{\??ef\??ef#1}% + {\def\next{\placestartfigure[#1][#2][#3]#4\placestopfigure}% + \dosingleempty\next}% + }% no longer: \doifundefined{#1}{\setvalue{#1}{\getexternalfigure{#1}}}} + +\long\def\dostartfigure#1% + {\dotripleargument\dodostartfigure#1\stopfigure} + +\unexpanded\def\startfigure + {\grabuntil{\e!stop\v!figure}\dostartfigure} + +%D NEW: used in styledesign manual + +% beware in mkiv we don't have the typeset- prefix +% +% \setbuffer[typeset-b]\endbuffer +% \setbuffer[typeset-a]\endbuffer +% +% todo: +% +% \appendtoks \setbuffer[typeset-b]\endbuffer\to \everystarttext +% \appendtoks \setbuffer[typeset-a]\endbuffer\to \everystarttext + +\newcount\noftypesetbuffers + +\def\typesetbuffer + {\dodoubleempty\dotypesetbuffer} + +\def\dotypesetbuffer[#1][#2]% beware: this will mix up the mp graphics + {\ifsecondargument + \dodotypesetbuffer[#1][#2]% + \else\iffirstargument + \doifassignmentelse{#1} + {\dodotypesetbuffer[\jobname][#1]}% + {\dodotypesetbuffer[#1][]}% + \else + \dodotypesetbuffer[\jobname][] + \fi\fi} + +% \def\dodotypesetbuffer[#1][#2]% +% {\bgroup +% \global\advance\noftypesetbuffers\plusone +% \edef\bufferfilename{\jobname-buffer-\the\noftypesetbuffers}% +% \doifmode{*\v!first} +% {\ctxlua{buffers.save("\bufferfilename.tmp","#1",true)}% +% \executesystemcommand{context \bufferfilename.tmp}}% +% \externalfigure[\bufferfilename.pdf][#2]% +% \egroup} + +\def\dodotypesetbuffer[#1][#2]% + {\bgroup + \global\advance\noftypesetbuffers\plusone + \edef\bufferfilename{\jobname-buffer-\the\noftypesetbuffers}% + \ctxlua{buffers.run("\bufferfilename.tmp","#1",true)}% + \externalfigure[\bufferfilename.pdf][#2]% + \egroup} + +\def\dodotypesetbufferindeed#1% + {} + +% for me only (manuals and such) +% +% \definetypesetting [name] [options] [settings-a] +% +% \typesetfile [name] [file] [settings-b] +% \typesetfile [file] [options] [settings-b] +% \typesetfile [file] [settings-b] +% \typesetfile [file] +% +% \enabletrackers[files.run] +% \starttext +% \typesetfile[oepsoeps.tex][width=10cm,frame=on] +% \stoptext + +\definesystemvariable{tz} + +\unexpanded\def\definetypesetting{\dotripleempty\dodefinetypesetting} +\def\typesetfile {\dotripleempty\dotypesetfile} + +\unexpanded\def\definetypesetting{\dotripleempty\dodefinetypesetting} +\def\typesetfile {\dotripleempty\dotypesetfile} + +\def\dodefinetypesetting[#1][#2][#3]%A conversion function that takes a number, unit (string) and optional +format (string) is implemented using this table.
+--ldx]]-- + +-- was: + +local function todimen(n,unit,fmt) + if type(n) == 'string' then + return n + else + unit = unit or 'pt' + return format(fmt or "%s%s",n*dimenfactors[unit],unit) + -- if fmt then + -- return format(fmt,n*dimenfactors[unit],unit) + -- else + -- return match(format("%.20f",n*dimenfactors[unit]),"(.-0?)0*$") .. unit + -- end + end +end + +--[[ldx-- +We collect a bunch of converters in the
More interesting it to implement a (sort of) dimen datatype, one
+that permits calculations too. First we define a function that
+converts a string to scaledpoints. We use
We use a metatable to intercept errors. When no key is found in +the table with factors, the metatable will be consulted for an +alternative index function.
+--ldx]]-- + +local mt = { } setmetatable(dimenfactors,mt) + +mt.__index = function(t,s) + -- error("wrong dimension: " .. (s or "?")) -- better a message + return false +end + +function string:todimen() + if type(self) == "number" then + return self + else + local value, unit = lpegmatch(dimenpair,self) + return value/unit + end +end + +local amount = S("+-")^0 * R("09")^0 * S(".,")^0 * R("09")^0 +local unit = P("pt") + P("cm") + P("mm") + P("sp") + P("bp") + P("in") + + P("pc") + P("dd") + P("cc") + P("nd") + P("nc") + +local validdimen = amount * unit + +lpeg.patterns.validdimen = pattern + +--[[ldx-- +This converter accepts calls like:
+ +And of course the often more efficient:
+ +With this in place, we can now implement a proper datatype for dimensions, one +that permits us to do this:
+ +We create a local metatable for this new type:
+--ldx]]-- + +local dimensions = { } + +--[[ldx-- +The main (and globally) visible representation of a dimen is defined next: it is +a one-element table. The unit that is returned from the match is normally a number +(one of the previously defined factors) but we also accept functions. Later we will +see why.
+--ldx]]-- + +function dimen(a) + if a then + local ta= type(a) + if ta == "string" then + local value, unit = lpegmatch(pattern,a) + if type(unit) == "function" then + k = value/unit() + else + k = value/unit + end + a = k + elseif ta == "table" then + a = a[1] + end + return setmetatable({ a }, dimensions) + else + return setmetatable({ 0 }, dimensions) + end +end + +--[[ldx-- +This function return a small hash with a metatable attached. It is +through this metatable that we can do the calculations. We could have +shared some of the code but for reasons of speed we don't.
+--ldx]]-- + +function dimensions.__add(a, b) + local ta, tb = type(a), type(b) + if ta == "string" then a = a:todimen() elseif ta == "table" then a = a[1] end + if tb == "string" then b = b:todimen() elseif tb == "table" then b = b[1] end + return setmetatable({ a + b }, dimensions) +end + +function dimensions.__sub(a, b) + local ta, tb = type(a), type(b) + if ta == "string" then a = a:todimen() elseif ta == "table" then a = a[1] end + if tb == "string" then b = b:todimen() elseif tb == "table" then b = b[1] end + return setmetatable({ a - b }, dimensions) +end + +function dimensions.__mul(a, b) + local ta, tb = type(a), type(b) + if ta == "string" then a = a:todimen() elseif ta == "table" then a = a[1] end + if tb == "string" then b = b:todimen() elseif tb == "table" then b = b[1] end + return setmetatable({ a * b }, dimensions) +end + +function dimensions.__div(a, b) + local ta, tb = type(a), type(b) + if ta == "string" then a = a:todimen() elseif ta == "table" then a = a[1] end + if tb == "string" then b = b:todimen() elseif tb == "table" then b = b[1] end + return setmetatable({ a / b }, dimensions) +end + +function dimensions.__unm(a) + local ta = type(a) + if ta == "string" then a = a:todimen() elseif ta == "table" then a = a[1] end + return setmetatable({ - a }, dimensions) +end + +--[[ldx-- +It makes no sense to implement the power and modulo function but +the next two do make sense because they permits is code like:
+ +We also need to provide a function for conversion to string (so that
+we can print dimensions). We print them as points, just like
Since it does not take much code, we also provide a way to access +a few accessors
+ +In the converter from string to dimension we support functions as
+factors. This is because in
In order to set the defaults we call this function now. At some point +the macro package needs to make sure the function is called again.
+--ldx]]-- + +dimensions.texify() + +--[[ldx-- +The previous code is rather efficient (also thanks to
When we cache converted strings this becomes 16.3 seconds. In order not +to waste too much memory on it, we tag the values of the cache as being +week which mean that the garbage collector will collect them in a next +sweep. This means that in most cases the speed up is mostly affecting the +current couple of calculations and as such the speed penalty is small.
+ +We redefine two previous defined functions that can benefit from +this:
+--ldx]]-- + +local known = { } setmetatable(known, { __mode = "v" }) + +function dimen(a) + if a then + local ta= type(a) + if ta == "string" then + local k = known[a] + if k then + a = k + else + local value, unit = lpegmatch(dimenpair,a) + if type(unit) == "function" then + k = value/unit() + else + k = value/unit + end + known[a] = k + a = k + end + elseif ta == "table" then + a = a[1] + end + return setmetatable({ a }, dimensions) + else + return setmetatable({ 0 }, dimensions) + end +end + +function string:todimen() + if type(self) == "number" then + return self + else + local k = known[self] + if not k then + local value, unit = lpegmatch(dimenpair,self) + if value and unit then + k = value/unit + else + k = 0 + end + -- print(self,value,unit) + known[self] = k + end + return k + end +end + +function number.toscaled(d) + return format("0.5f",d/2^16) +end + +--[[ldx-- +In a similar fashion we can define a glue datatype. In that case we +probably use a hash instead of a one-element table.
+--ldx]]-- + +--[[ldx-- +Goodie:s
+--ldx]]-- + +function number.percent(n) -- will be cleaned up once luatex 0.30 is out + local hsize = tex.hsize + if type(hsize) == "string" then + hsize = hsize:todimen() + end + return (n/100) * hsize +end + +number["%"] = number.percent diff --git a/tex/context/base/l-dir.lua b/tex/context/base/l-dir.lua new file mode 100644 index 000000000..2643f538b --- /dev/null +++ b/tex/context/base/l-dir.lua @@ -0,0 +1,363 @@ +if not modules then modules = { } end modules ['l-dir'] = { + version = 1.001, + comment = "companion to luat-lib.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- dir.expand_name will be merged with cleanpath and collapsepath + +local type = type +local find, gmatch, match, gsub = string.find, string.gmatch, string.match, string.gsub +local lpegmatch = lpeg.match + +dir = dir or { } + +-- handy + +function dir.current() + return (gsub(lfs.currentdir(),"\\","/")) +end + +-- optimizing for no string.find (*) does not save time + +local attributes = lfs.attributes +local walkdir = lfs.dir + +local function glob_pattern(path,patt,recurse,action) + local ok, scanner + if path == "/" then + ok, scanner = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe + else + ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe + end + if ok and type(scanner) == "function" then + if not find(path,"/$") then path = path .. '/' end + for name in scanner do + local full = path .. name + local mode = attributes(full,'mode') + if mode == 'file' then + if find(full,patt) then + action(full) + end + elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then + glob_pattern(full,patt,recurse,action) + end + end + end +end + +dir.glob_pattern = glob_pattern + +local function collect_pattern(path,patt,recurse,result) + local ok, scanner + result = result or { } + if path == "/" then + ok, scanner = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe + else + ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe + end + if ok and type(scanner) == "function" then + if not find(path,"/$") then path = path .. '/' end + for name in scanner do + local full = path .. name + local attr = attributes(full) + local mode = attr.mode + if mode == 'file' then + if find(full,patt) then + result[name] = attr + end + elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then + attr.list = collect_pattern(full,patt,recurse) + result[name] = attr + end + end + end + return result +end + +dir.collect_pattern = collect_pattern + +local P, S, R, C, Cc, Cs, Ct, Cv, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cv, lpeg.V + +local pattern = Ct { + [1] = (C(P(".") + P("/")^1) + C(R("az","AZ") * P(":") * P("/")^0) + Cc("./")) * V(2) * V(3), + [2] = C(((1-S("*?/"))^0 * P("/"))^0), + [3] = C(P(1)^0) +} + +local filter = Cs ( ( + P("**") / ".*" + + P("*") / "[^/]*" + + P("?") / "[^/]" + + P(".") / "%%." + + P("+") / "%%+" + + P("-") / "%%-" + + P(1) +)^0 ) + +local function glob(str,t) + if type(t) == "function" then + if type(str) == "table" then + for s=1,#str do + glob(str[s],t) + end + elseif lfs.isfile(str) then + t(str) + else + local split = lpegmatch(pattern,str) + if split then + local root, path, base = split[1], split[2], split[3] + local recurse = find(base,"%*%*") + local start = root .. path + local result = lpegmatch(filter,start .. base) + glob_pattern(start,result,recurse,t) + end + end + else + if type(str) == "table" then + local t = t or { } + for s=1,#str do + glob(str[s],t) + end + return t + elseif lfs.isfile(str) then + local t = t or { } + t[#t+1] = str + return t + else + local split = lpegmatch(pattern,str) + if split then + local t = t or { } + local action = action or function(name) t[#t+1] = name end + local root, path, base = split[1], split[2], split[3] + local recurse = find(base,"%*%*") + local start = root .. path + local result = lpegmatch(filter,start .. base) + glob_pattern(start,result,recurse,action) + return t + else + return { } + end + end + end +end + +dir.glob = glob + +--~ list = dir.glob("**/*.tif") +--~ list = dir.glob("/**/*.tif") +--~ list = dir.glob("./**/*.tif") +--~ list = dir.glob("oeps/**/*.tif") +--~ list = dir.glob("/oeps/**/*.tif") + +local function globfiles(path,recurse,func,files) -- func == pattern or function + if type(func) == "string" then + local s = func -- alas, we need this indirect way + func = function(name) return find(name,s) end + end + files = files or { } + for name in walkdir(path) do + if find(name,"^%.") then + --- skip + else + local mode = attributes(name,'mode') + if mode == "directory" then + if recurse then + globfiles(path .. "/" .. name,recurse,func,files) + end + elseif mode == "file" then + if func then + if func(name) then + files[#files+1] = path .. "/" .. name + end + else + files[#files+1] = path .. "/" .. name + end + end + end + end + return files +end + +dir.globfiles = globfiles + +-- t = dir.glob("c:/data/develop/context/sources/**/????-*.tex") +-- t = dir.glob("c:/data/develop/tex/texmf/**/*.tex") +-- t = dir.glob("c:/data/develop/context/texmf/**/*.tex") +-- t = dir.glob("f:/minimal/tex/**/*") +-- print(dir.ls("f:/minimal/tex/**/*")) +-- print(dir.ls("*.tex")) + +function dir.ls(pattern) + return table.concat(glob(pattern),"\n") +end + +--~ mkdirs("temp") +--~ mkdirs("a/b/c") +--~ mkdirs(".","/a/b/c") +--~ mkdirs("a","b","c") + +local make_indeed = true -- false + +if string.find(os.getenv("PATH"),";") then -- os.type == "windows" + + function dir.mkdirs(...) + local str, pth, t = "", "", { ... } + for i=1,#t do + local s = t[i] + if s ~= "" then + if str ~= "" then + str = str .. "/" .. s + else + str = s + end + end + end + local first, middle, last + local drive = false + first, middle, last = match(str,"^(//)(//*)(.*)$") + if first then + -- empty network path == local path + else + first, last = match(str,"^(//)/*(.-)$") + if first then + middle, last = match(str,"([^/]+)/+(.-)$") + if middle then + pth = "//" .. middle + else + pth = "//" .. last + last = "" + end + else + first, middle, last = match(str,"^([a-zA-Z]:)(/*)(.-)$") + if first then + pth, drive = first .. middle, true + else + middle, last = match(str,"^(/*)(.-)$") + if not middle then + last = str + end + end + end + end + for s in gmatch(last,"[^/]+") do + if pth == "" then + pth = s + elseif drive then + pth, drive = pth .. s, false + else + pth = pth .. "/" .. s + end + if make_indeed and not lfs.isdir(pth) then + lfs.mkdir(pth) + end + end + return pth, (lfs.isdir(pth) == true) + end + +--~ print(dir.mkdirs("","","a","c")) +--~ print(dir.mkdirs("a")) +--~ print(dir.mkdirs("a:")) +--~ print(dir.mkdirs("a:/b/c")) +--~ print(dir.mkdirs("a:b/c")) +--~ print(dir.mkdirs("a:/bbb/c")) +--~ print(dir.mkdirs("/a/b/c")) +--~ print(dir.mkdirs("/aaa/b/c")) +--~ print(dir.mkdirs("//a/b/c")) +--~ print(dir.mkdirs("///a/b/c")) +--~ print(dir.mkdirs("a/bbb//ccc/")) + + function dir.expand_name(str) -- will be merged with cleanpath and collapsepath + local first, nothing, last = match(str,"^(//)(//*)(.*)$") + if first then + first = dir.current() .. "/" + end + if not first then + first, last = match(str,"^(//)/*(.*)$") + end + if not first then + first, last = match(str,"^([a-zA-Z]:)(.*)$") + if first and not find(last,"^/") then + local d = lfs.currentdir() + if lfs.chdir(first) then + first = dir.current() + end + lfs.chdir(d) + end + end + if not first then + first, last = dir.current(), str + end + last = gsub(last,"//","/") + last = gsub(last,"/%./","/") + last = gsub(last,"^/*","") + first = gsub(first,"/*$","") + if last == "" then + return first + else + return first .. "/" .. last + end + end + +else + + function dir.mkdirs(...) + local str, pth, t = "", "", { ... } + for i=1,#t do + local s = t[i] + if s ~= "" then + if str ~= "" then + str = str .. "/" .. s + else + str = s + end + end + end + str = gsub(str,"/+","/") + if find(str,"^/") then + pth = "/" + for s in gmatch(str,"[^/]+") do + local first = (pth == "/") + if first then + pth = pth .. s + else + pth = pth .. "/" .. s + end + if make_indeed and not first and not lfs.isdir(pth) then + lfs.mkdir(pth) + end + end + else + pth = "." + for s in gmatch(str,"[^/]+") do + pth = pth .. "/" .. s + if make_indeed and not lfs.isdir(pth) then + lfs.mkdir(pth) + end + end + end + return pth, (lfs.isdir(pth) == true) + end + +--~ print(dir.mkdirs("","","a","c")) +--~ print(dir.mkdirs("a")) +--~ print(dir.mkdirs("/a/b/c")) +--~ print(dir.mkdirs("/aaa/b/c")) +--~ print(dir.mkdirs("//a/b/c")) +--~ print(dir.mkdirs("///a/b/c")) +--~ print(dir.mkdirs("a/bbb//ccc/")) + + function dir.expand_name(str) -- will be merged with cleanpath and collapsepath + if not find(str,"^/") then + str = lfs.currentdir() .. "/" .. str + end + str = gsub(str,"//","/") + str = gsub(str,"/%./","/") + return str + end + +end + +dir.makedirs = dir.mkdirs diff --git a/tex/context/base/l-file.lua b/tex/context/base/l-file.lua new file mode 100644 index 000000000..2bfc07090 --- /dev/null +++ b/tex/context/base/l-file.lua @@ -0,0 +1,314 @@ +if not modules then modules = { } end modules ['l-file'] = { + version = 1.001, + comment = "companion to luat-lib.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- needs a cleanup + +file = file or { } + +local concat = table.concat +local find, gmatch, match, gsub, sub, char = string.find, string.gmatch, string.match, string.gsub, string.sub, string.char +local lpegmatch = lpeg.match + +function file.removesuffix(filename) + return (gsub(filename,"%.[%a%d]+$","")) +end + +function file.addsuffix(filename, suffix) + if not suffix or suffix == "" then + return filename + elseif not find(filename,"%.[%a%d]+$") then + return filename .. "." .. suffix + else + return filename + end +end + +function file.replacesuffix(filename, suffix) + return (gsub(filename,"%.[%a%d]+$","")) .. "." .. suffix +end + +function file.dirname(name,default) + return match(name,"^(.+)[/\\].-$") or (default or "") +end + +function file.basename(name) + return match(name,"^.+[/\\](.-)$") or name +end + +function file.nameonly(name) + return (gsub(match(name,"^.+[/\\](.-)$") or name,"%..*$","")) +end + +function file.extname(name,default) + return match(name,"^.+%.([^/\\]-)$") or default or "" +end + +file.suffix = file.extname + +--~ function file.join(...) +--~ local pth = concat({...},"/") +--~ pth = gsub(pth,"\\","/") +--~ local a, b = match(pth,"^(.*://)(.*)$") +--~ if a and b then +--~ return a .. gsub(b,"//+","/") +--~ end +--~ a, b = match(pth,"^(//)(.*)$") +--~ if a and b then +--~ return a .. gsub(b,"//+","/") +--~ end +--~ return (gsub(pth,"//+","/")) +--~ end + +local trick_1 = char(1) +local trick_2 = "^" .. trick_1 .. "/+" + +function file.join(...) + local lst = { ... } + local a, b = lst[1], lst[2] + if a == "" then + lst[1] = trick_1 + elseif b and find(a,"^/+$") and find(b,"^/") then + lst[1] = "" + lst[2] = gsub(b,"^/+","") + end + local pth = concat(lst,"/") + pth = gsub(pth,"\\","/") + local a, b = match(pth,"^(.*://)(.*)$") + if a and b then + return a .. gsub(b,"//+","/") + end + a, b = match(pth,"^(//)(.*)$") + if a and b then + return a .. gsub(b,"//+","/") + end + pth = gsub(pth,trick_2,"") + return (gsub(pth,"//+","/")) +end + +--~ print(file.join("//","/y")) +--~ print(file.join("/","/y")) +--~ print(file.join("","/y")) +--~ print(file.join("/x/","/y")) +--~ print(file.join("x/","/y")) +--~ print(file.join("http://","/y")) +--~ print(file.join("http://a","/y")) +--~ print(file.join("http:///a","/y")) +--~ print(file.join("//nas-1","/y")) + +function file.iswritable(name) + local a = lfs.attributes(name) or lfs.attributes(file.dirname(name,".")) + return a and sub(a.permissions,2,2) == "w" +end + +function file.isreadable(name) + local a = lfs.attributes(name) + return a and sub(a.permissions,1,1) == "r" +end + +file.is_readable = file.isreadable +file.is_writable = file.iswritable + +-- todo: lpeg + +--~ function file.split_path(str) +--~ local t = { } +--~ str = gsub(str,"\\", "/") +--~ str = gsub(str,"(%a):([;/])", "%1\001%2") +--~ for name in gmatch(str,"([^;:]+)") do +--~ if name ~= "" then +--~ t[#t+1] = gsub(name,"\001",":") +--~ end +--~ end +--~ return t +--~ end + +local checkedsplit = string.checkedsplit + +function file.split_path(str,separator) + str = gsub(str,"\\","/") + return checkedsplit(str,separator or io.pathseparator) +end + +function file.join_path(tab) + return concat(tab,io.pathseparator) -- can have trailing // +end + +-- we can hash them weakly + +function file.collapse_path(str) + str = gsub(str,"\\","/") + if find(str,"/") then + str = gsub(str,"^%./",(gsub(lfs.currentdir(),"\\","/")) .. "/") -- ./xx in qualified + str = gsub(str,"/%./","/") + local n, m = 1, 1 + while n > 0 or m > 0 do + str, n = gsub(str,"[^/%.]+/%.%.$","") + str, m = gsub(str,"[^/%.]+/%.%./","") + end + str = gsub(str,"([^/])/$","%1") + -- str = gsub(str,"^%./","") -- ./xx in qualified + str = gsub(str,"/%.$","") + end + if str == "" then str = "." end + return str +end + +--~ print(file.collapse_path("/a")) +--~ print(file.collapse_path("a/./b/..")) +--~ print(file.collapse_path("a/aa/../b/bb")) +--~ print(file.collapse_path("a/../..")) +--~ print(file.collapse_path("a/.././././b/..")) +--~ print(file.collapse_path("a/./././b/..")) +--~ print(file.collapse_path("a/b/c/../..")) + +function file.robustname(str) + return (gsub(str,"[^%a%d%/%-%.\\]+","-")) +end + +file.readdata = io.loaddata +file.savedata = io.savedata + +function file.copy(oldname,newname) + file.savedata(newname,io.loaddata(oldname)) +end + +-- lpeg variants, slightly faster, not always + +--~ local period = lpeg.P(".") +--~ local slashes = lpeg.S("\\/") +--~ local noperiod = 1-period +--~ local noslashes = 1-slashes +--~ local name = noperiod^1 + +--~ local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * lpeg.C(noperiod^1) * -1 + +--~ function file.extname(name) +--~ return lpegmatch(pattern,name) or "" +--~ end + +--~ local pattern = lpeg.Cs(((period * noperiod^1 * -1)/"" + 1)^1) + +--~ function file.removesuffix(name) +--~ return lpegmatch(pattern,name) +--~ end + +--~ local pattern = (noslashes^0 * slashes)^1 * lpeg.C(noslashes^1) * -1 + +--~ function file.basename(name) +--~ return lpegmatch(pattern,name) or name +--~ end + +--~ local pattern = (noslashes^0 * slashes)^1 * lpeg.Cp() * noslashes^1 * -1 + +--~ function file.dirname(name) +--~ local p = lpegmatch(pattern,name) +--~ if p then +--~ return sub(name,1,p-2) +--~ else +--~ return "" +--~ end +--~ end + +--~ local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * lpeg.Cp() * noperiod^1 * -1 + +--~ function file.addsuffix(name, suffix) +--~ local p = lpegmatch(pattern,name) +--~ if p then +--~ return name +--~ else +--~ return name .. "." .. suffix +--~ end +--~ end + +--~ local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * lpeg.Cp() * noperiod^1 * -1 + +--~ function file.replacesuffix(name,suffix) +--~ local p = lpegmatch(pattern,name) +--~ if p then +--~ return sub(name,1,p-2) .. "." .. suffix +--~ else +--~ return name .. "." .. suffix +--~ end +--~ end + +--~ local pattern = (noslashes^0 * slashes)^0 * lpeg.Cp() * ((noperiod^1 * period)^1 * lpeg.Cp() + lpeg.P(true)) * noperiod^1 * -1 + +--~ function file.nameonly(name) +--~ local a, b = lpegmatch(pattern,name) +--~ if b then +--~ return sub(name,a,b-2) +--~ elseif a then +--~ return sub(name,a) +--~ else +--~ return name +--~ end +--~ end + +--~ local test = file.extname +--~ local test = file.basename +--~ local test = file.dirname +--~ local test = file.addsuffix +--~ local test = file.replacesuffix +--~ local test = file.nameonly + +--~ print(1,test("./a/b/c/abd.def.xxx","!!!")) +--~ print(2,test("./../b/c/abd.def.xxx","!!!")) +--~ print(3,test("a/b/c/abd.def.xxx","!!!")) +--~ print(4,test("a/b/c/def.xxx","!!!")) +--~ print(5,test("a/b/c/def","!!!")) +--~ print(6,test("def","!!!")) +--~ print(7,test("def.xxx","!!!")) + +--~ local tim = os.clock() for i=1,250000 do local ext = test("abd.def.xxx","!!!") end print(os.clock()-tim) + +-- also rewrite previous + +local letter = lpeg.R("az","AZ") + lpeg.S("_-+") +local separator = lpeg.P("://") + +local qualified = lpeg.P(".")^0 * lpeg.P("/") + letter*lpeg.P(":") + letter^1*separator + letter^1 * lpeg.P("/") +local rootbased = lpeg.P("/") + letter*lpeg.P(":") + +-- ./name ../name /name c: :// name/name + +function file.is_qualified_path(filename) + return lpegmatch(qualified,filename) ~= nil +end + +function file.is_rootbased_path(filename) + return lpegmatch(rootbased,filename) ~= nil +end + +local slash = lpeg.S("\\/") +local period = lpeg.P(".") +local drive = lpeg.C(lpeg.R("az","AZ")) * lpeg.P(":") +local path = lpeg.C(((1-slash)^0 * slash)^0) +local suffix = period * lpeg.C(lpeg.P(1-period)^0 * lpeg.P(-1)) +local base = lpeg.C((1-suffix)^0) + +local pattern = (drive + lpeg.Cc("")) * (path + lpeg.Cc("")) * (base + lpeg.Cc("")) * (suffix + lpeg.Cc("")) + +function file.splitname(str) -- returns drive, path, base, suffix + return lpegmatch(pattern,str) +end + +-- function test(t) for k, v in next, t do print(v, "=>", file.splitname(v)) end end +-- +-- test { "c:", "c:/aa", "c:/aa/bb", "c:/aa/bb/cc", "c:/aa/bb/cc.dd", "c:/aa/bb/cc.dd.ee" } +-- test { "c:", "c:aa", "c:aa/bb", "c:aa/bb/cc", "c:aa/bb/cc.dd", "c:aa/bb/cc.dd.ee" } +-- test { "/aa", "/aa/bb", "/aa/bb/cc", "/aa/bb/cc.dd", "/aa/bb/cc.dd.ee" } +-- test { "aa", "aa/bb", "aa/bb/cc", "aa/bb/cc.dd", "aa/bb/cc.dd.ee" } + +--~ -- todo: +--~ +--~ if os.type == "windows" then +--~ local currentdir = lfs.currentdir +--~ function lfs.currentdir() +--~ return (gsub(currentdir(),"\\","/")) +--~ end +--~ end diff --git a/tex/context/base/l-io.lua b/tex/context/base/l-io.lua new file mode 100644 index 000000000..66e279309 --- /dev/null +++ b/tex/context/base/l-io.lua @@ -0,0 +1,187 @@ +if not modules then modules = { } end modules ['l-io'] = { + version = 1.001, + comment = "companion to luat-lib.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local byte, find, gsub = string.byte, string.find, string.gsub + +if string.find(os.getenv("PATH"),";") then + io.fileseparator, io.pathseparator = "\\", ";" +else + io.fileseparator, io.pathseparator = "/" , ":" +end + +function io.loaddata(filename,textmode) + local f = io.open(filename,(textmode and 'r') or 'rb') + if f then + -- collectgarbage("step") -- sometimes makes a big difference in mem consumption + local data = f:read('*all') + -- garbagecollector.check(data) + f:close() + return data + else + return nil + end +end + +function io.savedata(filename,data,joiner) + local f = io.open(filename,"wb") + if f then + if type(data) == "table" then + f:write(table.join(data,joiner or "")) + elseif type(data) == "function" then + data(f) + else + f:write(data or "") + end + f:close() + return true + else + return false + end +end + +function io.exists(filename) + local f = io.open(filename) + if f == nil then + return false + else + assert(f:close()) + return true + end +end + +function io.size(filename) + local f = io.open(filename) + if f == nil then + return 0 + else + local s = f:seek("end") + assert(f:close()) + return s + end +end + +function io.noflines(f) + local n = 0 + for _ in f:lines() do + n = n + 1 + end + f:seek('set',0) + return n +end + +local nextchar = { + [ 4] = function(f) + return f:read(1,1,1,1) + end, + [ 2] = function(f) + return f:read(1,1) + end, + [ 1] = function(f) + return f:read(1) + end, + [-2] = function(f) + local a, b = f:read(1,1) + return b, a + end, + [-4] = function(f) + local a, b, c, d = f:read(1,1,1,1) + return d, c, b, a + end +} + +function io.characters(f,n) + if f then + return nextchar[n or 1], f + else + return nil, nil + end +end + +local nextbyte = { + [4] = function(f) + local a, b, c, d = f:read(1,1,1,1) + if d then + return byte(a), byte(b), byte(c), byte(d) + else + return nil, nil, nil, nil + end + end, + [2] = function(f) + local a, b = f:read(1,1) + if b then + return byte(a), byte(b) + else + return nil, nil + end + end, + [1] = function (f) + local a = f:read(1) + if a then + return byte(a) + else + return nil + end + end, + [-2] = function (f) + local a, b = f:read(1,1) + if b then + return byte(b), byte(a) + else + return nil, nil + end + end, + [-4] = function(f) + local a, b, c, d = f:read(1,1,1,1) + if d then + return byte(d), byte(c), byte(b), byte(a) + else + return nil, nil, nil, nil + end + end +} + +function io.bytes(f,n) + if f then + return nextbyte[n or 1], f + else + return nil, nil + end +end + +function io.ask(question,default,options) + while true do + io.write(question) + if options then + io.write(string.format(" [%s]",table.concat(options,"|"))) + end + if default then + io.write(string.format(" [%s]",default)) + end + io.write(string.format(" ")) + local answer = io.read() + answer = gsub(answer,"^%s*(.*)%s*$","%1") + if answer == "" and default then + return default + elseif not options then + return answer + else + for k=1,#options do + if options[k] == answer then + return answer + end + end + local pattern = "^" .. answer + for k=1,#options do + local v = options[k] + if find(v,pattern) then + return v + end + end + end + end +end diff --git a/tex/context/base/l-lpeg.lua b/tex/context/base/l-lpeg.lua new file mode 100644 index 000000000..b107a8e64 --- /dev/null +++ b/tex/context/base/l-lpeg.lua @@ -0,0 +1,165 @@ +if not modules then modules = { } end modules ['l-lpeg'] = { + version = 1.001, + comment = "companion to luat-lib.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local lpeg = require("lpeg") + +lpeg.patterns = lpeg.patterns or { } -- so that we can share +local patterns = lpeg.patterns + +local P, R, S, Ct, C, Cs, Cc, V = lpeg.P, lpeg.R, lpeg.S, lpeg.Ct, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.V +local match = lpeg.match + +local digit, sign = R('09'), S('+-') +local cr, lf, crlf = P("\r"), P("\n"), P("\r\n") +local utf8byte = R("\128\191") + +patterns.utf8byte = utf8byte +patterns.utf8one = R("\000\127") +patterns.utf8two = R("\194\223") * utf8byte +patterns.utf8three = R("\224\239") * utf8byte * utf8byte +patterns.utf8four = R("\240\244") * utf8byte * utf8byte * utf8byte + +patterns.digit = digit +patterns.sign = sign +patterns.cardinal = sign^0 * digit^1 +patterns.integer = sign^0 * digit^1 +patterns.float = sign^0 * digit^0 * P('.') * digit^1 +patterns.number = patterns.float + patterns.integer +patterns.oct = P("0") * R("07")^1 +patterns.octal = patterns.oct +patterns.HEX = P("0x") * R("09","AF")^1 +patterns.hex = P("0x") * R("09","af")^1 +patterns.hexadecimal = P("0x") * R("09","AF","af")^1 +patterns.lowercase = R("az") +patterns.uppercase = R("AZ") +patterns.letter = patterns.lowercase + patterns.uppercase +patterns.space = S(" ") +patterns.eol = S("\n\r") +patterns.spacer = S(" \t\f\v") -- + string.char(0xc2, 0xa0) if we want utf (cf mail roberto) +patterns.newline = crlf + cr + lf +patterns.nonspace = 1 - patterns.space +patterns.nonspacer = 1 - patterns.spacer +patterns.whitespace = patterns.eol + patterns.spacer +patterns.nonwhitespace = 1 - patterns.whitespace +patterns.utf8 = patterns.utf8one + patterns.utf8two + patterns.utf8three + patterns.utf8four +patterns.utfbom = P('\000\000\254\255') + P('\255\254\000\000') + P('\255\254') + P('\254\255') + P('\239\187\191') + +function lpeg.anywhere(pattern) --slightly adapted from website + return P { P(pattern) + 1 * V(1) } -- why so complex? +end + +function lpeg.splitter(pattern, action) + return (((1-P(pattern))^1)/action+1)^0 +end + +local spacing = patterns.spacer^0 * patterns.newline -- sort of strip +local empty = spacing * Cc("") +local nonempty = Cs((1-spacing)^1) * spacing^-1 +local content = (empty + nonempty)^1 + +local capture = Ct(content^0) + +function string:splitlines() + return match(capture,self) +end + +patterns.textline = content + +--~ local p = lpeg.splitat("->",false) print(match(p,"oeps->what->more")) -- oeps what more +--~ local p = lpeg.splitat("->",true) print(match(p,"oeps->what->more")) -- oeps what->more +--~ local p = lpeg.splitat("->",false) print(match(p,"oeps")) -- oeps +--~ local p = lpeg.splitat("->",true) print(match(p,"oeps")) -- oeps + +local splitters_s, splitters_m = { }, { } + +local function splitat(separator,single) + local splitter = (single and splitters_s[separator]) or splitters_m[separator] + if not splitter then + separator = P(separator) + if single then + local other, any = C((1 - separator)^0), P(1) + splitter = other * (separator * C(any^0) + "") -- ? + splitters_s[separator] = splitter + else + local other = C((1 - separator)^0) + splitter = other * (separator * other)^0 + splitters_m[separator] = splitter + end + end + return splitter +end + +lpeg.splitat = splitat + +local cache = { } + +function lpeg.split(separator,str) + local c = cache[separator] + if not c then + c = Ct(splitat(separator)) + cache[separator] = c + end + return match(c,str) +end + +function string:split(separator) + local c = cache[separator] + if not c then + c = Ct(splitat(separator)) + cache[separator] = c + end + return match(c,self) +end + +lpeg.splitters = cache + +local cache = { } + +function lpeg.checkedsplit(separator,str) + local c = cache[separator] + if not c then + separator = P(separator) + local other = C((1 - separator)^0) + c = Ct(separator^0 * other * (separator^1 * other)^0) + cache[separator] = c + end + return match(c,str) +end + +function string:checkedsplit(separator) + local c = cache[separator] + if not c then + separator = P(separator) + local other = C((1 - separator)^0) + c = Ct(separator^0 * other * (separator^1 * other)^0) + cache[separator] = c + end + return match(c,self) +end + +--~ function lpeg.append(list,pp) +--~ local p = pp +--~ for l=1,#list do +--~ if p then +--~ p = p + P(list[l]) +--~ else +--~ p = P(list[l]) +--~ end +--~ end +--~ return p +--~ end + +--~ from roberto's site: + +local f1 = string.byte + +local function f2(s) local c1, c2 = f1(s,1,2) return c1 * 64 + c2 - 12416 end +local function f3(s) local c1, c2, c3 = f1(s,1,3) return (c1 * 64 + c2) * 64 + c3 - 925824 end +local function f4(s) local c1, c2, c3, c4 = f1(s,1,4) return ((c1 * 64 + c2) * 64 + c3) * 64 + c4 - 63447168 end + +patterns.utf8byte = patterns.utf8one/f1 + patterns.utf8two/f2 + patterns.utf8three/f3 + patterns.utf8four/f4 diff --git a/tex/context/base/l-math.lua b/tex/context/base/l-math.lua new file mode 100644 index 000000000..fc8db4721 --- /dev/null +++ b/tex/context/base/l-math.lua @@ -0,0 +1,41 @@ +if not modules then modules = { } end modules ['l-math'] = { + version = 1.001, + comment = "companion to luat-lib.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local floor, sin, cos, tan = math.floor, math.sin, math.cos, math.tan + +if not math.round then + function math.round(x) + return floor(x + 0.5) + end +end + +if not math.div then + function math.div(n,m) + return floor(n/m) + end +end + +if not math.mod then + function math.mod(n,m) + return n % m + end +end + +local pipi = 2*math.pi/360 + +function math.sind(d) + return sin(d*pipi) +end + +function math.cosd(d) + return cos(d*pipi) +end + +function math.tand(d) + return tan(d*pipi) +end diff --git a/tex/context/base/l-md5.lua b/tex/context/base/l-md5.lua new file mode 100644 index 000000000..27955ef9b --- /dev/null +++ b/tex/context/base/l-md5.lua @@ -0,0 +1,72 @@ +if not modules then modules = { } end modules ['l-md5'] = { + version = 1.001, + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- This also provides file checksums and checkers. + +local gsub, format, byte = string.gsub, string.format, string.byte + +local function convert(str,fmt) + return (gsub(md5.sum(str),".",function(chr) return format(fmt,byte(chr)) end)) +end + +if not md5.HEX then function md5.HEX(str) return convert(str,"%02X") end end +if not md5.hex then function md5.hex(str) return convert(str,"%02x") end end +if not md5.dec then function md5.dec(str) return convert(str,"%03i") end end + +--~ if not md5.HEX then +--~ local function remap(chr) return format("%02X",byte(chr)) end +--~ function md5.HEX(str) return (gsub(md5.sum(str),".",remap)) end +--~ end +--~ if not md5.hex then +--~ local function remap(chr) return format("%02x",byte(chr)) end +--~ function md5.hex(str) return (gsub(md5.sum(str),".",remap)) end +--~ end +--~ if not md5.dec then +--~ local function remap(chr) return format("%03i",byte(chr)) end +--~ function md5.dec(str) return (gsub(md5.sum(str),".",remap)) end +--~ end + +file.needs_updating_threshold = 1 + +function file.needs_updating(oldname,newname) -- size modification access change + local oldtime = lfs.attributes(oldname, modification) + local newtime = lfs.attributes(newname, modification) + if newtime >= oldtime then + return false + elseif oldtime - newtime < file.needs_updating_threshold then + return false + else + return true + end +end + +function file.checksum(name) + if md5 then + local data = io.loaddata(name) + if data then + return md5.HEX(data) + end + end + return nil +end + +function file.loadchecksum(name) + if md5 then + local data = io.loaddata(name .. ".md5") + return data and (gsub(data,"%s","")) + end + return nil +end + +function file.savechecksum(name, checksum) + if not checksum then checksum = file.checksum(name) end + if checksum then + io.savedata(name .. ".md5",checksum) + return checksum + end + return nil +end diff --git a/tex/context/base/l-number.lua b/tex/context/base/l-number.lua new file mode 100644 index 000000000..a1249f055 --- /dev/null +++ b/tex/context/base/l-number.lua @@ -0,0 +1,58 @@ +if not modules then modules = { } end modules ['l-number'] = { + version = 1.001, + comment = "companion to luat-lib.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local tostring = tostring +local format, floor, insert, match = string.format, math.floor, table.insert, string.match +local lpegmatch = lpeg.match + +number = number or { } + +-- a,b,c,d,e,f = number.toset(100101) + +function number.toset(n) + return match(tostring(n),"(.?)(.?)(.?)(.?)(.?)(.?)(.?)(.?)") +end + +function number.toevenhex(n) + local s = format("%X",n) + if #s % 2 == 0 then + return s + else + return "0" .. s + end +end + +-- the lpeg way is slower on 8 digits, but faster on 4 digits, some 7.5% +-- on +-- +-- for i=1,1000000 do +-- local a,b,c,d,e,f,g,h = number.toset(12345678) +-- local a,b,c,d = number.toset(1234) +-- local a,b,c = number.toset(123) +-- end +-- +-- of course dedicated "(.)(.)(.)(.)" matches are even faster + +local one = lpeg.C(1-lpeg.S(''))^1 + +function number.toset(n) + return lpegmatch(one,tostring(n)) +end + +function number.bits(n,zero) + local t, i = { }, (zero and 0) or 1 + while n > 0 do + local m = n % 2 + if m > 0 then + insert(t,1,i) + end + n = floor(n/2) + i = i + 1 + end + return t +end diff --git a/tex/context/base/l-os.lua b/tex/context/base/l-os.lua new file mode 100644 index 000000000..fba2cd317 --- /dev/null +++ b/tex/context/base/l-os.lua @@ -0,0 +1,301 @@ +if not modules then modules = { } end modules ['l-os'] = { + version = 1.001, + comment = "companion to luat-lib.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- maybe build io.flush in os.execute + +local find, format, gsub = string.find, string.format, string.gsub +local random, ceil = math.random, math.ceil + +local execute, spawn, exec, ioflush = os.execute, os.spawn or os.execute, os.exec or os.execute, io.flush + +function os.execute(...) ioflush() return execute(...) end +function os.spawn (...) ioflush() return spawn (...) end +function os.exec (...) ioflush() return exec (...) end + +function os.resultof(command) + ioflush() -- else messed up logging + local handle = io.popen(command,"r") + if not handle then + -- print("unknown command '".. command .. "' in os.resultof") + return "" + else + return handle:read("*all") or "" + end +end + +--~ os.type : windows | unix (new, we already guessed os.platform) +--~ os.name : windows | msdos | linux | macosx | solaris | .. | generic (new) +--~ os.platform : extended os.name with architecture + +if not io.fileseparator then + if find(os.getenv("PATH"),";") then + io.fileseparator, io.pathseparator, os.type = "\\", ";", os.type or "mswin" + else + io.fileseparator, io.pathseparator, os.type = "/" , ":", os.type or "unix" + end +end + +os.type = os.type or (io.pathseparator == ";" and "windows") or "unix" +os.name = os.name or (os.type == "windows" and "mswin" ) or "linux" + +if os.type == "windows" then + os.libsuffix, os.binsuffix = 'dll', 'exe' +else + os.libsuffix, os.binsuffix = 'so', '' +end + +function os.launch(str) + if os.type == "windows" then + os.execute("start " .. str) -- os.spawn ? + else + os.execute(str .. " &") -- os.spawn ? + end +end + +if not os.times then + -- utime = user time + -- stime = system time + -- cutime = children user time + -- cstime = children system time + function os.times() + return { + utime = os.gettimeofday(), -- user + stime = 0, -- system + cutime = 0, -- children user + cstime = 0, -- children system + } + end +end + +os.gettimeofday = os.gettimeofday or os.clock + +local startuptime = os.gettimeofday() + +function os.runtime() + return os.gettimeofday() - startuptime +end + +--~ print(os.gettimeofday()-os.time()) +--~ os.sleep(1.234) +--~ print (">>",os.runtime()) +--~ print(os.date("%H:%M:%S",os.gettimeofday())) +--~ print(os.date("%H:%M:%S",os.time())) + +-- no need for function anymore as we have more clever code and helpers now +-- this metatable trickery might as well disappear + +os.resolvers = os.resolvers or { } + +local resolvers = os.resolvers + +local osmt = getmetatable(os) or { __index = function(t,k) t[k] = "unset" return "unset" end } -- maybe nil +local osix = osmt.__index + +osmt.__index = function(t,k) + return (resolvers[k] or osix)(t,k) +end + +setmetatable(os,osmt) + +if not os.setenv then + + -- we still store them but they won't be seen in + -- child processes although we might pass them some day + -- using command concatination + + local env, getenv = { }, os.getenv + + function os.setenv(k,v) + env[k] = v + end + + function os.getenv(k) + return env[k] or getenv(k) + end + +end + +-- we can use HOSTTYPE on some platforms + +local name, platform = os.name or "linux", os.getenv("MTX_PLATFORM") or "" + +local function guess() + local architecture = os.resultof("uname -m") or "" + if architecture ~= "" then + return architecture + end + architecture = os.getenv("HOSTTYPE") or "" + if architecture ~= "" then + return architecture + end + return os.resultof("echo $HOSTTYPE") or "" +end + +if platform ~= "" then + + os.platform = platform + +elseif os.type == "windows" then + + -- we could set the variable directly, no function needed here + + function os.resolvers.platform(t,k) + local platform, architecture = "", os.getenv("PROCESSOR_ARCHITECTURE") or "" + if find(architecture,"AMD64") then + platform = "mswin-64" + else + platform = "mswin" + end + os.setenv("MTX_PLATFORM",platform) + os.platform = platform + return platform + end + +elseif name == "linux" then + + function os.resolvers.platform(t,k) + -- we sometims have HOSTTYPE set so let's check that first + local platform, architecture = "", os.getenv("HOSTTYPE") or os.resultof("uname -m") or "" + if find(architecture,"x86_64") then + platform = "linux-64" + elseif find(architecture,"ppc") then + platform = "linux-ppc" + else + platform = "linux" + end + os.setenv("MTX_PLATFORM",platform) + os.platform = platform + return platform + end + +elseif name == "macosx" then + + --[[ + Identifying the architecture of OSX is quite a mess and this + is the best we can come up with. For some reason $HOSTTYPE is + a kind of pseudo environment variable, not known to the current + environment. And yes, uname cannot be trusted either, so there + is a change that you end up with a 32 bit run on a 64 bit system. + Also, some proper 64 bit intel macs are too cheap (low-end) and + therefore not permitted to run the 64 bit kernel. + ]]-- + + function os.resolvers.platform(t,k) + -- local platform, architecture = "", os.getenv("HOSTTYPE") or "" + -- if architecture == "" then + -- architecture = os.resultof("echo $HOSTTYPE") or "" + -- end + local platform, architecture = "", os.resultof("echo $HOSTTYPE") or "" + if architecture == "" then + -- print("\nI have no clue what kind of OSX you're running so let's assume an 32 bit intel.\n") + platform = "osx-intel" + elseif find(architecture,"i386") then + platform = "osx-intel" + elseif find(architecture,"x86_64") then + platform = "osx-64" + else + platform = "osx-ppc" + end + os.setenv("MTX_PLATFORM",platform) + os.platform = platform + return platform + end + +elseif name == "sunos" then + + function os.resolvers.platform(t,k) + local platform, architecture = "", os.resultof("uname -m") or "" + if find(architecture,"sparc") then + platform = "solaris-sparc" + else -- if architecture == 'i86pc' + platform = "solaris-intel" + end + os.setenv("MTX_PLATFORM",platform) + os.platform = platform + return platform + end + +elseif name == "freebsd" then + + function os.resolvers.platform(t,k) + local platform, architecture = "", os.resultof("uname -m") or "" + if find(architecture,"amd64") then + platform = "freebsd-amd64" + else + platform = "freebsd" + end + os.setenv("MTX_PLATFORM",platform) + os.platform = platform + return platform + end + +elseif name == "kfreebsd" then + + function os.resolvers.platform(t,k) + -- we sometims have HOSTTYPE set so let's check that first + local platform, architecture = "", os.getenv("HOSTTYPE") or os.resultof("uname -m") or "" + if find(architecture,"x86_64") then + platform = "kfreebsd-64" + else + platform = "kfreebsd-i386" + end + os.setenv("MTX_PLATFORM",platform) + os.platform = platform + return platform + end + +else + + -- platform = "linux" + -- os.setenv("MTX_PLATFORM",platform) + -- os.platform = platform + + function os.resolvers.platform(t,k) + local platform = "linux" + os.setenv("MTX_PLATFORM",platform) + os.platform = platform + return platform + end + +end + +-- beware, we set the randomseed + +-- from wikipedia: Version 4 UUIDs use a scheme relying only on random numbers. This algorithm sets the +-- version number as well as two reserved bits. All other bits are set using a random or pseudorandom +-- data source. Version 4 UUIDs have the form xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx with hexadecimal +-- digits x and hexadecimal digits 8, 9, A, or B for y. e.g. f47ac10b-58cc-4372-a567-0e02b2c3d479. +-- +-- as we don't call this function too often there is not so much risk on repetition + +local t = { 8, 9, "a", "b" } + +function os.uuid() + return format("%04x%04x-4%03x-%s%03x-%04x-%04x%04x%04x", + random(0xFFFF),random(0xFFFF), + random(0x0FFF), + t[ceil(random(4))] or 8,random(0x0FFF), + random(0xFFFF), + random(0xFFFF),random(0xFFFF),random(0xFFFF) + ) +end + +local d + +function os.timezone(delta) + d = d or tonumber(tonumber(os.date("%H")-os.date("!%H"))) + if delta then + if d > 0 then + return format("+%02i:00",d) + else + return format("-%02i:00",-d) + end + else + return 1 + end +end diff --git a/tex/context/base/l-pdfview.lua b/tex/context/base/l-pdfview.lua new file mode 100644 index 000000000..627477ee8 --- /dev/null +++ b/tex/context/base/l-pdfview.lua @@ -0,0 +1,95 @@ +if not modules then modules = { } end modules ['l-pdfview'] = { + version = 1.001, + comment = "companion to mtx-context.lua", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local format, getenv = string.format, os.getenv + +pdfview = pdfview or { } + +local opencalls = { + ['default'] = "pdfopen --file", -- "pdfopen --back --file" + ['xpdf'] = "xpdfopen", +} + +local closecalls= { + ['default'] = "pdfclose --file", + ['xpdf'] = nil, +} + +local allcalls = { + ['default'] = "pdfclose --all", + ['xpdf'] = nil, +} + +if os.type == "windows" then + opencalls['okular'] = 'start "test" "c:/program files/kde/bin/okular.exe" --unique' -- todo: get focus +else + opencalls['okular'] = 'okular --unique' +end + +pdfview.METHOD = "MTX_PDFVIEW_METHOD" +pdfview.method = getenv(pdfview.METHOD) or 'default' +pdfview.method = (opencalls[pdfview.method] and pdfview.method) or 'default' + +function pdfview.methods() + return table.concat(table.sortedkeys(opencalls), " ") +end + +function pdfview.status() + return format("pdfview methods: %s, current method: %s, MTX_PDFVIEW_METHOD=%s",pdfview.methods(),pdfview.method,getenv(pdfview.METHOD) or "Hyphenating
Callbacks are the real asset of
When you (temporarily) want to install a callback function, and after a +while wants to revert to the original one, you can use the following two +functions.
+--ldx]]-- + +local trace_callbacks = false trackers.register("system.callbacks", function(v) trace_callbacks = v end) + +local register_callback, find_callback = callback.register, callback.find +local frozen, stack = { }, { } + +callback.original_register_callback = register_callback + +local function frozen_message(what,name) + logs.report("callbacks","not %s frozen '%s' (%s)",what,name,frozen[name]) +end + +local function frozen_callback(name) + return nil, format("callback '%s' is frozen (%s)",name,frozen[name]) +end + +local function state(name) + local f = find_callback(name) + if f == false then + return "disabled" + elseif f then + return "enabled" + else + return "undefined" + end +end + +function callbacks.report() + local list = callback.list() + for name, func in table.sortedhash(list) do + local str = frozen[name] + if str then + logs.report("callbacks","%s: %s -> %s",state(name),name,str) + else + logs.report("callbacks","%s: %s",state(name),name) + end + end +end + +function callbacks.table() + local NC, NR, verbatim = context.NC, context.NR, context.type + context.starttabulate { "|l|l|p|" } + for name, func in table.sortedhash(callback.list()) do + NC() verbatim(name) NC() verbatim(state(name)) NC() context(frozen[name] or "") NC() NR() + end + context.stoptabulate() +end + +function callbacks.freeze(name,freeze) + freeze = type(freeze) == "string" and freeze +--~ print(name) + if find(name,"%*") then + local pattern = name -- string.simpleesc(name) + local list = callback.list() + for name, func in next, list do + if find(name,pattern) then + frozen[name] = freeze or frozen[name] or "frozen" + end + end + else + frozen[name] = freeze or frozen[name] or "frozen" + end +end + +function callbacks.register(name,func,freeze) + if frozen[name] then + if trace_callbacks then + frozen_message("registering",name) + end + return frozen_callback(name) + elseif freeze then + frozen[name] = (type(freeze) == "string" and freeze) or "registered" + end + return register_callback(name,func) +end + +function callback.register(name,func) -- original + if not frozen[name] then + return register_callback(name,func) + elseif trace_callbacks then + frozen_message("registering",name) + end + return frozen_callback(name) +end + +function callbacks.push(name, func) + if not frozen[name] then + local sn = stack[name] + if not sn then + sn = { } + stack[name] = sn + end + insert(sn,find_callback(name)) + register_callback(name, func) + elseif trace_callbacks then + frozen_message("pushing",name) + end +end + +function callbacks.pop(name) + if not frozen[name] then + local sn = stack[name] + if not sn or #sn == 0 then + -- some error + register_callback(name, nil) -- ! really needed + else + -- this fails: register_callback(name, remove(stack[name])) + local func = remove(sn) + register_callback(name, func) + end + end +end + +--~ -- somehow crashes later on +--~ +--~ callbacks.freeze("find_.*_file","finding file") +--~ callbacks.freeze("read_.*_file","reading file") +--~ callbacks.freeze("open_.*_file","opening file") + +--[[ldx-- +The simple case is to remove the callback:
+ +
+callbacks.push('linebreak_filter')
+... some actions ...
+callbacks.pop('linebreak_filter')
+
+
+Often, in such case, another callback or a macro call will pop +the original.
+ +In practice one will install a new handler, like in:
+ +
+callbacks.push('linebreak_filter', function(...)
+ return something_done(...)
+end)
+
+
+Even more interesting is:
+ +
+callbacks.push('linebreak_filter', function(...)
+ callbacks.pop('linebreak_filter')
+ return something_done(...)
+end)
+
+
+This does a one-shot.
+--ldx]]-- + +--[[ldx-- +Callbacks may result in
At some point in the development we did some tests with counting +nodes (in this case 121049).
+ +setstepmul | seconds | megabytes |
200 | 24.0 | 80.5 |
175 | 21.0 | 78.2 |
150 | 22.0 | 74.6 |
160 | 22.0 | 74.6 |
165 | 21.0 | 77.6 |
125 | 21.5 | 89.2 |
100 | 21.5 | 88.4 |
The following code is kind of experimental. In the documents
+that describe the development of
We cannot load anything yet. However what we will do us reserve a fewtables. +These can be used for runtime user data or third party modules and will not be +cluttered by macro package code.
+--ldx]]-- + +userdata = userdata or { } -- might be used +thirddata = thirddata or { } -- might be used +moduledata = moduledata or { } -- might be used +document = document or { } +parametersets = parametersets or { } -- experimental + +--[[ldx-- +These can be used/set by the caller program;
Please create a namespace within these tables before using them!
+ +We could cook up a readonly model for global tables but it +makes more sense to invite users to use one of the predefined +namespaces. One can redefine the protector. After all, it's +just a lightweight suggestive system, not a watertight +one.
+--ldx]]-- + +local debug = require "debug" + +local string, table, lpeg, math, io, system = string, table, lpeg, math, io, system +local next, setfenv = next, setfenv or debug.setfenv +local format = string.format + +local global = _G + +global.global = global + +local dummy = function() end + +local protected = { + -- global table + global = global, + -- user tables + userdata = userdata, + moduledata = moduledata, + thirddata = thirddata, + document = document, + -- reserved + protect = dummy, + unprotect = dummy, + -- luatex + tex = tex, + -- lua + string = string, + table = table, + lpeg = lpeg, + math = math, + io = io, + system = system, +} + +userdata, thirddata, moduledata = nil, nil, nil + +if not setfenv then + texio.write_nl("warning: we need to fix setfenv by using 'load in' or '_ENV'") +end + +function protect(name) + if name == "isolateddata" then + local t = { } + for k, v in next, protected do + t[k] = v + end + setfenv(2,t) + else + if not name then + name = "shareddata" + end + local t = global[name] + if not t then + t = { } + for k, v in next, protected do + t[k] = v + end + global[name] = t + end + setfenv(2,t) + end +end + +lua.numbers = { } +lua.messages = { } + +function lua.registername(name,message) + local lnn = lua.numbers[name] + if not lnn then + lnn = #lua.messages + 1 + lua.messages[lnn] = message + lua.numbers[name] = lnn + end + lua.name[lnn] = message + tex.write(lnn) +end + +--~ function lua.checknames() +--~ lua.name[0] = "ctx" +--~ for k, v in next, lua.messages do +--~ lua.name[k] = v +--~ end +--~ end + +storage.register("lua/numbers", lua.numbers, "lua.numbers") +storage.register("lua/messages", lua.messages, "lua.messages") + +--~ local arguments, files = document.arguments, document.files -- set later + +function document.setargument(key,value) + document.arguments[key] = value +end + +function document.setdefaultargument(key,default) + local v = document.arguments[key] + if v == nil or v == "" then + document.arguments[key] = default + end +end + +function document.getargument(key,default) + local v = document.arguments[key] + if type(v) == "boolean" then + v = (v and "yes") or "no" + document.arguments[key] = v + end + tex.sprint(tex.ctxcatcodes,v or default or "") +end + +function document.setfilename(i,name) + document.files[tonumber(i)] = name +end + +function document.getfilename(i) + tex.sprint(tex.ctxcatcodes,document.files[i] or "") +end diff --git a/tex/context/base/luat-ini.mkiv b/tex/context/base/luat-ini.mkiv new file mode 100644 index 000000000..c9d88bf4f --- /dev/null +++ b/tex/context/base/luat-ini.mkiv @@ -0,0 +1,243 @@ +%D \module +%D [ file=luat-ini, +%D version=2005.08.11, +%D title=\CONTEXT\ Lua Macros, +%D subtitle=Initialization, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Lua Macros / Initialization} + +\unprotect + +%D Loading lua code can be done using \type {startup.lua}. The following +%D method uses the \TEX\ input file locator of kpse. At least we need to +%D use that way of loading when we haven't yet define our own code, which +%D we keep outside the format. We will keep code outside \TEX\ files as +%D much as possible. + +\ifx\setnaturalcatcodes\undefined \let\setnaturalcatcodes\relax \fi +\ifx\obeylualines \undefined \let\obeylualines \relax \fi +\ifx\obeyluatokens \undefined \let\obeyluatokens \relax \fi + +%D A few more goodies: + +\long\def\dostartlua + {\begingroup + \obeylualines + \dodostartlua} + +\long\def\dodostartlua#1\stoplua + {\normalexpanded{\endgroup\noexpand\directlua\zerocount{#1}}} + +\long\def\dostartluacode + {\begingroup + \obeylualines + \obeyluatokens + \dodostartluacode} + +\long\def\dodostartluacode#1\stopluacode + {\normalexpanded{\endgroup\noexpand\directlua\zerocount{#1}}} + +\unexpanded\def\startlua {\dostartlua } % tex catcodes +\unexpanded\def\startluacode{\dostartluacode} % lua catcodes + +%D Some delayed definitions: + +\ifx\obeylines \undefined \let\obeylines \relax \fi +\ifx\obeyedline \undefined \let\obeyedline \relax \fi +\ifx\obeyspaces \undefined \let\obeyspaces \relax \fi +\ifx\obeyedspace \undefined \let\obeyedspace \relax \fi +\ifx\outputnewlinechar\undefined \let\outputnewlinechar\relax \fi + +%D A previous version used a bit less code and no catcode table, +%D simply becaus ethey were not around at the time of writing. +% +% we keep it around for archival purposes +% +% \def\obeylualines +% {\obeylines \let\obeyedline \outputnewlinechar +% \obeyspaces \let\obeyedspace\space} +% +% \def\obeyluatokens % todo: make this a proper catcode table, use let's +% {\catcode`\%=12 \catcode`\#=12 +% \catcode`\_=12 \catcode`\^=12 +% \catcode`\&=12 \catcode`\|=12 +% \catcode`\{=12 \catcode`\}=12 +% \catcode`\~=12 \catcode`\$=12 +% \def\\{\string\\}\def\|{\string\|}\def\-{\string\-}% +% \def\({\string\(}\def\){\string\)}\def\{{\string\{}\def\}{\string\}}% +% \def\'{\string\'}\def\"{\string\"}% +% \def\n{\string\n}\def\r{\string\r}\def\f{\string\f}\def\t{\string\t}% +% \def\a{\string\a}\def\b{\string\b}\def\v{\string\v}\def\s{\string\s}% +% \def\1{\string\1}\def\2{\string\2}\def\3{\string\3}\def\4{\string\4}\def\5{\string\5}% +% \def\6{\string\6}\def\7{\string\7}\def\8{\string\8}\def\9{\string\9}\def\0{\string\0}} + +\let\obeylualines\relax + +\newtoks\everyluacode + +\edef\lualetterbackslash{\string\\} +\edef\lualetterbar {\string\|} \edef\lualetterdash {\string\-} +\edef\lualetterlparent {\string\(} \edef\lualetterrparent {\string\)} +\edef\lualetterlbrace {\string\{} \edef\lualetterrbrace {\string\}} +\edef\lualettersquote {\string\'} \edef\lualetterdquote {\string\"} +\edef\lualettern {\string\n} \edef\lualetterr {\string\r} +\edef\lualetterf {\string\f} \edef\lualettert {\string\t} +\edef\lualettera {\string\a} \edef\lualetterb {\string\b} +\edef\lualetterv {\string\v} \edef\lualetters {\string\s} +\edef\lualetterone {\string\1} \edef\lualettertwo {\string\2} +\edef\lualetterthree {\string\3} \edef\lualetterfour {\string\4} +\edef\lualetterfive {\string\5} \edef\lualettersix {\string\6} +\edef\lualetterseven {\string\7} \edef\lualettereight {\string\8} +\edef\lualetternine {\string\9} \edef\lualetterzero {\string\0} + +\appendtoks + \let\\\lualetterbackslash + \let\|\lualetterbar \let\-\lualetterdash + \let\(\lualetterlparent \let\)\lualetterrparent + \let\{\lualetterlbrace \let\}\lualetterrbrace + \let\'\lualettersquote \let\"\lualetterdquote + \let\n\lualettern \let\r\lualetterr + \let\f\lualetterf \let\t\lualettert + \let\a\lualettera \let\b\lualetterb + \let\v\lualetterv \let\s\lualetters + \let\1\lualetterone \let\2\lualettertwo + \let\3\lualetterthree \let\4\lualetterfour + \let\5\lualetterfive \let\6\lualettersix + \let\7\lualetterseven \let\8\lualettereight + \let\9\lualetternine \let\0\lualetterzero +\to \everyluacode + +\def\obeyluatokens + {\setcatcodetable \luacatcodes + \the\everyluacode} + +%D \macros +%D {definenamedlua} +%D +%D We provide an interface for defining instances: + +\def\s!lua{lua} \def\v!code{code} \def\!!name{name} \def\s!data{data} + +%D Beware: because \type {\expanded} is een convert command, the error +%D message will show \type{The following functions collect elements and texts.
+--ldx]]-- + +-- are these still needed -> lxml-cmp.lua + +function xml.collect_elements(root, pattern) + return xmlparseapply({ root },pattern) +end + +function xml.collect_texts(root, pattern, flatten) -- todo: variant with handle + local collected = xmlparseapply({ root },pattern) + if collected and flatten then + local xmltostring = xml.tostring + for c=1,#collected do + collected[c] = xmltostring(collected[c].dt) + end + end + return collected or { } +end + +function xml.collect_tags(root, pattern, nonamespace) + local collected = xmlparseapply({ root },pattern) + if collected then + local t = { } + for c=1,#collected do + local e = collected[c] + local ns, tg = e.ns, e.tg + if nonamespace then + t[#t+1] = tg + elseif ns == "" then + t[#t+1] = tg + else + t[#t+1] = ns .. ":" .. tg + end + end + return t + end +end + +--[[ldx-- +We've now arrived at the functions that manipulate the tree.
+--ldx]]-- + +local no_root = { no_root = true } + +function xml.redo_ni(d) + for k=1,#d do + local dk = d[k] + if type(dk) == "table" then + dk.ni = k + end + end +end + +local function xmltoelement(whatever,root) + if not whatever then + return nil + end + local element + if type(whatever) == "string" then + element = xmlinheritedconvert(whatever,root) + else + element = whatever -- we assume a table + end + if element.error then + return whatever -- string + end + if element then + --~ if element.ri then + --~ element = element.dt[element.ri].dt + --~ else + --~ element = element.dt + --~ end + end + return element +end + +xml.toelement = xmltoelement + +local function copiedelement(element,newparent) + if type(element) == "string" then + return element + else + element = xmlcopy(element).dt + if newparent and type(element) == "table" then + element.__p__ = newparent + end + return element + end +end + +function xml.delete_element(root,pattern) + local collected = xmlparseapply({ root },pattern) + if collected then + for c=1,#collected do + local e = collected[c] + local p = e.__p__ + if p then + if trace_manipulations then + report('deleting',pattern,c,e) + end + local d = p.dt + remove(d,e.ni) + xml.redo_ni(d) -- can be made faster and inlined + end + end + end +end + +function xml.replace_element(root,pattern,whatever) + local element = root and xmltoelement(whatever,root) + local collected = element and xmlparseapply({ root },pattern) + if collected then + for c=1,#collected do + local e = collected[c] + local p = e.__p__ + if p then + if trace_manipulations then + report('replacing',pattern,c,e) + end + local d = p.dt + d[e.ni] = copiedelement(element,p) + xml.redo_ni(d) -- probably not needed + end + end + end +end + +local function inject_element(root,pattern,whatever,prepend) + local element = root and xmltoelement(whatever,root) + local collected = element and xmlparseapply({ root },pattern) + if collected then + for c=1,#collected do + local e = collected[c] + local r = e.__p__ + local d, k, rri = r.dt, e.ni, r.ri + local edt = (rri and d[rri].dt) or (d and d[k] and d[k].dt) + if edt then + local be, af + local cp = copiedelement(element,e) + if prepend then + be, af = cp, edt + else + be, af = edt, cp + end + for i=1,#af do + be[#be+1] = af[i] + end + if rri then + r.dt[rri].dt = be + else + d[k].dt = be + end + xml.redo_ni(d) + end + end + end +end + +local function insert_element(root,pattern,whatever,before) -- todo: element als functie + local element = root and xmltoelement(whatever,root) + local collected = element and xmlparseapply({ root },pattern) + if collected then + for c=1,#collected do + local e = collected[c] + local r = e.__p__ + local d, k = r.dt, e.ni + if not before then + k = k + 1 + end + insert(d,k,copiedelement(element,r)) + xml.redo_ni(d) + end + end +end + +xml.insert_element = insert_element +xml.insert_element_after = insert_element +xml.insert_element_before = function(r,p,e) insert_element(r,p,e,true) end +xml.inject_element = inject_element +xml.inject_element_after = inject_element +xml.inject_element_before = function(r,p,e) inject_element(r,p,e,true) end + +local function include(xmldata,pattern,attribute,recursive,loaddata) + -- parse="text" (default: xml), encoding="" (todo) + -- attribute = attribute or 'href' + pattern = pattern or 'include' + loaddata = loaddata or io.loaddata + local collected = xmlparseapply({ xmldata },pattern) + if collected then + for c=1,#collected do + local ek = collected[c] + local name = nil + local ekdt = ek.dt + local ekat = ek.at + local epdt = ek.__p__.dt + if not attribute or attribute == "" then + name = (type(ekdt) == "table" and ekdt[1]) or ekdt -- ckeck, probably always tab or str + end + if not name then + for a in gmatch(attribute or "href","([^|]+)") do + name = ekat[a] + if name then break end + end + end + local data = (name and name ~= "" and loaddata(name)) or "" + if data == "" then + epdt[ek.ni] = "" -- xml.empty(d,k) + elseif ekat["parse"] == "text" then + -- for the moment hard coded + epdt[ek.ni] = xml.escaped(data) -- d[k] = xml.escaped(data) + else +--~ local settings = xmldata.settings +--~ settings.parent_root = xmldata -- to be tested +--~ local xi = xmlconvert(data,settings) + local xi = xmlinheritedconvert(data,xmldata) + if not xi then + epdt[ek.ni] = "" -- xml.empty(d,k) + else + if recursive then + include(xi,pattern,attribute,recursive,loaddata) + end + epdt[ek.ni] = xml.body(xi) -- xml.assign(d,k,xi) + end + end + end + end +end + +xml.include = include + +--~ local function manipulate(xmldata,pattern,manipulator) -- untested and might go away +--~ local collected = xmlparseapply({ xmldata },pattern) +--~ if collected then +--~ local xmltostring = xml.tostring +--~ for c=1,#collected do +--~ local e = collected[c] +--~ local data = manipulator(xmltostring(e)) +--~ if data == "" then +--~ epdt[e.ni] = "" +--~ else +--~ local xi = xmlinheritedconvert(data,xmldata) +--~ if not xi then +--~ epdt[e.ni] = "" +--~ else +--~ epdt[e.ni] = xml.body(xi) -- xml.assign(d,k,xi) +--~ end +--~ end +--~ end +--~ end +--~ end + +--~ xml.manipulate = manipulate + +function xml.strip_whitespace(root, pattern, nolines) -- strips all leading and trailing space ! + local collected = xmlparseapply({ root },pattern) + if collected then + for i=1,#collected do + local e = collected[i] + local edt = e.dt + if edt then + local t = { } + for i=1,#edt do + local str = edt[i] + if type(str) == "string" then + if str == "" then + -- stripped + else + if nolines then + str = gsub(str,"[ \n\r\t]+"," ") + end + if str == "" then + -- stripped + else + t[#t+1] = str + end + end + else + --~ str.ni = i + t[#t+1] = str + end + end + e.dt = t + end + end + end +end + +function xml.strip_whitespace(root, pattern, nolines, anywhere) -- strips all leading and trailing spacing + local collected = xmlparseapply({ root },pattern) -- beware, indices no longer are valid now + if collected then + for i=1,#collected do + local e = collected[i] + local edt = e.dt + if edt then + if anywhere then + local t = { } + for e=1,#edt do + local str = edt[e] + if type(str) ~= "string" then + t[#t+1] = str + elseif str ~= "" then + -- todo: lpeg for each case + if nolines then + str = gsub(str,"%s+"," ") + end + str = gsub(str,"^%s*(.-)%s*$","%1") + if str ~= "" then + t[#t+1] = str + end + end + end + e.dt = t + else + -- we can assume a regular sparse xml table with no successive strings + -- otherwise we should use a while loop + if #edt > 0 then + -- strip front + local str = edt[1] + if type(str) ~= "string" then + -- nothing + elseif str == "" then + remove(edt,1) + else + if nolines then + str = gsub(str,"%s+"," ") + end + str = gsub(str,"^%s+","") + if str == "" then + remove(edt,1) + else + edt[1] = str + end + end + end + if #edt > 1 then + -- strip end + local str = edt[#edt] + if type(str) ~= "string" then + -- nothing + elseif str == "" then + remove(edt) + else + if nolines then + str = gsub(str,"%s+"," ") + end + str = gsub(str,"%s+$","") + if str == "" then + remove(edt) + else + edt[#edt] = str + end + end + end + end + end + end + end +end + +local function rename_space(root, oldspace, newspace) -- fast variant + local ndt = #root.dt + for i=1,ndt or 0 do + local e = root[i] + if type(e) == "table" then + if e.ns == oldspace then + e.ns = newspace + if e.rn then + e.rn = newspace + end + end + local edt = e.dt + if edt then + rename_space(edt, oldspace, newspace) + end + end + end +end + +xml.rename_space = rename_space + +function xml.remap_tag(root, pattern, newtg) + local collected = xmlparseapply({ root },pattern) + if collected then + for c=1,#collected do + collected[c].tg = newtg + end + end +end + +function xml.remap_namespace(root, pattern, newns) + local collected = xmlparseapply({ root },pattern) + if collected then + for c=1,#collected do + collected[c].ns = newns + end + end +end + +function xml.check_namespace(root, pattern, newns) + local collected = xmlparseapply({ root },pattern) + if collected then + for c=1,#collected do + local e = collected[c] + if (not e.rn or e.rn == "") and e.ns == "" then + e.rn = newns + end + end + end +end + +function xml.remap_name(root, pattern, newtg, newns, newrn) + local collected = xmlparseapply({ root },pattern) + if collected then + for c=1,#collected do + local e = collected[c] + e.tg, e.ns, e.rn = newtg, newns, newrn + end + end +end + +--[[ldx-- +Here are a few synonyms.
+--ldx]]-- + +xml.each = xml.each_element +xml.process = xml.process_element +xml.strip = xml.strip_whitespace +xml.collect = xml.collect_elements +xml.all = xml.collect_elements + +xml.insert = xml.insert_element_after +xml.inject = xml.inject_element_after +xml.after = xml.insert_element_after +xml.before = xml.insert_element_before +xml.delete = xml.delete_element +xml.replace = xml.replace_element diff --git a/tex/context/base/lxml-ctx.lua b/tex/context/base/lxml-ctx.lua new file mode 100644 index 000000000..765754953 --- /dev/null +++ b/tex/context/base/lxml-ctx.lua @@ -0,0 +1,131 @@ +if not modules then modules = { } end modules ['lxml-ctx'] = { + version = 1.001, + comment = "companion to lxml-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- is this still used? + +xml.ctx = { } +xml.ctx.enhancers = { } + +-- hashen + +function xml.ctx.enhancers.compound(root,lpath,before,tokens,after) -- todo lpeg + local before = before or "[%a%d][%a%d][%a%d]" + local tokens = tokens or "[%/%-]" + local after = after or "[%a%d][%a%d][%a%d]" + local pattern = "(" .. before .. ")(" .. tokens .. ")(" .. after .. ")" + local action = function(a,b,c) + return a .. "We provide (at least here) two entity handlers. The more extensive
+resolver consults a hash first, tries to convert to
We do things different now but it's still somewhat experimental
+--ldx]]-- + +local trace_entities = false trackers.register("xml.entities", function(v) trace_entities = v end) + +xml.entities = xml.entities or { } -- xml.entity_handler == function + +storage.register("xml/entities",xml.entities,"xml.entities") -- this will move to lxml + +local entities = xml.entities -- this is a shared hash + +xml.unknown_any_entity_format = nil -- has to be per xml + +local parsedentity = xml.parsedentitylpeg + +function xml.register_entity(key,value) + entities[key] = value + if trace_entities then + logs.report("xml","registering entity '%s' as: %s",key,value) + end +end + +function xml.resolved_entity(str) + local e = entities[str] + if e then + local te = type(e) + if te == "function" then + e(str) + elseif e then + texsprint(ctxcatcodes,e) + end + else + -- resolve hex and dec, todo: escape # & etc for ctxcatcodes + -- normally this is already solved while loading the file + local chr, err = lpegmatch(parsedentity,str) + if chr then + texsprint(ctxcatcodes,chr) + elseif err then + texsprint(ctxcatcodes,err) + else + texsprint(ctxcatcodes,"\\xmle{",str,"}{",utfupper(str),"}") -- we need to use our own upper + end + end +end + +entities.amp = function() tex.write("&") end +entities.lt = function() tex.write("<") end +entities.gt = function() tex.write(">") end diff --git a/tex/context/base/lxml-inf.lua b/tex/context/base/lxml-inf.lua new file mode 100644 index 000000000..629c869ec --- /dev/null +++ b/tex/context/base/lxml-inf.lua @@ -0,0 +1,53 @@ +if not modules then modules = { } end modules ['lxml-inf'] = { + version = 1.001, + comment = "this module is the basis for the lxml-* ones", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- This file will be loaded runtime by x-pending.tex. + +local status, stack + +local function get(e,d) + local ns, tg = e.ns, e.tg + local name = tg + if ns ~= "" then name = ns .. ":" .. tg end + stack[d] = name + local ec = e.command + if ec == true then + ec = "system: text" + elseif ec == false then + ec = "system: skip" + elseif ec == nil then + ec = "system: not set" + elseif type(ec) == "string" then + ec = "setup: " .. ec + else -- function + ec = tostring(ec) + end + local tag = concat(stack," => ",1,d) + local s = status[tag] + if not s then + s = { } + status[tag] = s + end + s[ec] = (s[ec] or 0) + 1 +end + +local function get_command_status(id) + status, stack = {}, {} + if id then + xmlwithelements(get_id(id),get) + return status + else + local t = { } + for id, _ in next, loaded do + t[id] = get_command_status(id) + end + return t + end +end + +lxml.get_command_status = get_command_status diff --git a/tex/context/base/lxml-ini.mkiv b/tex/context/base/lxml-ini.mkiv new file mode 100644 index 000000000..0f04d0488 --- /dev/null +++ b/tex/context/base/lxml-ini.mkiv @@ -0,0 +1,414 @@ +%D \module +%D [ file=lxml-ini, +%D version=2007.08.17, +%D title=\CONTEXT\ \XML\ Support, +%D subtitle=Initialization, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D Todo: auto apply setups (manage at lua end) + +\writestatus{loading}{ConTeXt XML Support / Initialization} + +%registerctxluafile{lxml-tab}{1.001} % loader +%registerctxluafile{lxml-lpt}{1.001} % parser +%registerctxluafile{lxml-xml}{1.001} % xml finalizers +%registerctxluafile{lxml-aux}{1.001} % extras using parser +%registerctxluafile{lxml-mis}{1.001} % extras independent of parser +\registerctxluafile{lxml-ent}{1.001} % entity hacks +\registerctxluafile{lxml-tex}{1.001} % tex finalizers +\registerctxluafile{lxml-dir}{1.001} % ctx hacks + +\unprotect + +\def\c!entities{entities} % to be internationalized + +\def\xmlmain #1{\ctxlua{lxml.main("#1")}} +\def\xmlmatch #1{\ctxlua{lxml.match("#1")}} +\def\xmlall #1#2{\ctxlua{lxml.all("#1","#2")}} +\def\xmlatt #1#2{\ctxlua{lxml.att("#1","#2")}} +\def\xmlattdef #1#2#3{\ctxlua{lxml.att("#1","#2","#3")}} +\def\xmlchainatt #1#2{\ctxlua{lxml.chainattribute("#1","/","#2")}} +\def\xmlchainattdef #1#2#3{\ctxlua{lxml.chainattribute("#1","/","#2","#3")}} +\def\xmlattribute #1#2#3{\ctxlua{lxml.attribute("#1","#2","#3")}} +\def\xmlattributedef #1#2#3#4{\ctxlua{lxml.attribute("#1","#2","#3","#4")}} +\def\xmlcommand #1#2#3{\ctxlua{lxml.command("#1","#2","#3")}} +\def\xmlconcat #1#2#3{\ctxlua{lxml.concat("#1","#2",[[\detokenize{#3}]])}} +\def\xmlconcatrange#1#2#3#4#5{\ctxlua{lxml.concatrange("#1","#2","#3","#4",[[\detokenize{#5}]])}} +\def\xmlcount #1#2{\ctxlua{lxml.count("#1","#2")}} +\def\xmldelete #1#2{\ctxlua{lxml.delete("#1","#2")}} +\def\xmldirectives #1{\ctxlua{lxml.directives.setup("#1")}} +\def\xmldirectivesbefore #1{\ctxlua{lxml.directives.before("#1")}} +\def\xmldirectivesafter #1{\ctxlua{lxml.directives.after("#1")}} +\def\xmlfilter #1#2{\ctxlua{lxml.filter("#1",\!!bs#2\!!es)}} +\def\xmlfilterlist #1#2{\ctxlua{lxml.filterlist("#1",\!!bs#2\!!es)}} +\def\xmlfunction #1#2{\ctxlua{lxml["function"]("#1",\!!bs#2\!!es)}} +\def\xmlfirst #1#2{\ctxlua{lxml.first("#1","#2")}} +\def\xmlflush #1{\ctxlua{lxml.flush("#1")}} +%def\xmlcontent #1{\ctxlua{lxml.content("#1")}} +%def\xmlflushstripped #1{\ctxlua{lxml.strip("#1",true)}} +\def\xmldirect #1{\ctxlua{lxml.direct("#1")}} % in loops, not dt but root +\def\xmlidx #1#2#3{\ctxlua{lxml.idx("#1","#2",\number#3)}} +\def\xmlinclude #1#2#3{\ctxlua{lxml.include("#1","#2","#3",true)}} +\def\xmlindex #1#2#3{\ctxlua{lxml.index("#1","#2",\number#3)}} +\def\xmlinfo #1{\hbox{\ttxx[\ctxlua{lxml.info("#1")}]}} +\def\xmlshow #1{\startpacked\ttx\xmlverbatim{#1}\stoppacked} +\def\xmllast #1#2{\ctxlua{lxml.last("#1","#2")}} +\def\xmlname #1{\ctxlua{lxml.name("#1")}} +\def\xmlnamespace #1{\ctxlua{lxml.namespace("#1")}} +\def\xmlnonspace #1#2{\ctxlua{lxml.nonspace("#1","#2")}} +\def\xmlraw #1#2{\ctxlua{lxml.raw("#1","#2")}} +\def\xmlcontext #1#2{\ctxlua{lxml.context("#1","#2")}} +\def\xmlflushcontext #1{\ctxlua{lxml.context("#1")}} +\def\xmlsnippet #1#2{\ctxlua{lxml.snippet("#1",#2)}} +\def\xmlelement #1#2{\ctxlua{lxml.element("#1",#2)}} +\def\xmlregisterns #1#2{\ctxlua{xml.registerns("#1","#2")}} % document +\def\xmlremapname #1#2#3#4{\ctxlua{xml.remapname(lxml.id("#1"),"#2","#3","#4")}} % element +\def\xmlremapnamespace #1#2#3{\ctxlua{xml.rename_space(lxml.id("#1"),"#2","#3")}} % document +\def\xmlchecknamespace #1#2#3{\ctxlua{xml.check_namespace(lxml.id("#1"),"#2","#3")}} % element +\def\xmlsetfunction #1#2#3{\ctxlua{lxml.setaction("#1","#2",#3)}} +\def\xmlsetsetup #1#2#3{\ctxlua{lxml.setsetup("#1","#2","#3")}} +\def\xmlstrip #1#2{\ctxlua{lxml.strip("#1","#2")}} +\def\xmlstripnolines #1#2{\ctxlua{lxml.strip("#1","#2",true)}} +\def\xmlstripanywhere #1#2{\ctxlua{lxml.strip("#1","#2",true,true)}} +\def\xmlstripped #1#2{\ctxlua{lxml.stripped("#1","#2")}} +\def\xmlstrippednolines #1#2{\ctxlua{lxml.stripped("#1","#2",true)}} +\def\xmltag #1{\ctxlua{lxml.tag("#1")}} +\def\xmltext #1#2{\ctxlua{lxml.text("#1","#2")}} +\def\xmlverbatim #1{\ctxlua{lxml.verbatim("#1")}} +\def\xmldisplayverbatim #1{\ctxlua{lxml.displayverbatim("#1")}} +\def\xmlinlineverbatim #1{\ctxlua{lxml.inlineverbatim("#1")}} + +\def\xmlload #1#2{\ctxlua{lxml.load("#1","#2","\@@xmentities","\@@xmcompress")}} +\def\xmlloadbuffer #1#2{\ctxlua{lxml.loadbuffer("#1","#2","\@@xmentities","\@@xmcompress")}} +\def\xmlloaddata #1#2{\ctxlua{lxml.loaddata("#1",\!!bs#2\!!es,"\@@xmentities","\@@xmcompress")}} +\def\xmlloadregistered #1#2{\ctxlua{lxml.loadregistered("#1","\@@xmentities","\@@xmcompress")}} +\def\xmlloaddirectives #1{\ctxlua{lxml.directives.load("any:///#1")}} +\def\xmlpos #1{\ctxlua{lxml.pos("#1")}} + +%def\xmldoifelse #1#2{\ctxlua{cs.testcase(xml.found(lxml.id("#1"),"#2",false))}} +%def\xmldoifelsetext #1#2{\ctxlua{cs.testcase(xml.found(lxml.id("#1"),"#2",true ))}} + +% kind of special: + +\def\xmlstartraw{\ctxlua{lxml.startraw()}} +\def\xmlstopraw {\ctxlua{lxml.stopraw()}} + +% todo: \xmldoifelseattribute + +\def\xmldoif #1#2{\ctxlua{lxml.doif (\!!bs#1\!!es,\!!bs#2\!!es)}} +\def\xmldoifnot #1#2{\ctxlua{lxml.doifnot (\!!bs#1\!!es,\!!bs#2\!!es)}} +\def\xmldoifelse #1#2{\ctxlua{lxml.doifelse (\!!bs#1\!!es,\!!bs#2\!!es)}} +\def\xmldoiftext #1#2{\ctxlua{lxml.doiftext (\!!bs#1\!!es,\!!bs#2\!!es)}} +\def\xmldoifnottext #1#2{\ctxlua{lxml.doifnottext (\!!bs#1\!!es,\!!bs#2\!!es)}} +\def\xmldoifelsetext #1#2{\ctxlua{lxml.doifelsetext(\!!bs#1\!!es,\!!bs#2\!!es)}} + +%def\xmldoifelseempty #1#2{\ctxlua{lxml.doifelseempty("#1","#2")}} % #2, "*" or "" == self not yet implemented +%def\xmldoifelseselfempty #1{\ctxlua{lxml.doifelseempty("#1")}} + +% \startxmlsetups xml:include +% \xmlinclude{main}{include}{filename|href} +% \stopxmlsetups +% +% \xmlprependsetup{xml:include} + +\let\xmlgrab\xmlsetsetup % obsolete +\let\xmlself\s!unknown % obsolete + +\def\xmlsetup#1#2{\setupwithargument{#2}{#1}} + +\let\xmls\xmlsetup +\let\xmlw\setupwithargument + +\newtoks \registeredxmlsetups + +% todo: 1:xml:whatever always before 3:xml:something + +\def\xmlprependsetup #1{\ctxlua{lxml.installsetup(1,"*","#1")}} +\def\xmlappendsetup #1{\ctxlua{lxml.installsetup(2,"*","#1")}} +\def\xmlbeforesetup #1#2{\ctxlua{lxml.installsetup(3,"*","#1","#2"))}} +\def\xmlaftersetup #1#2{\ctxlua{lxml.installsetup(4,"*","#1","#2"))}} + +\def\xmlprependdocumentsetup #1#2{\ctxlua{lxml.installsetup(1,"#1","#2")}} +\def\xmlappenddocumentsetup #1#2{\ctxlua{lxml.installsetup(2,"#1","#2")}} +\def\xmlbeforedocumentsetup#1#2#3{\ctxlua{lxml.installsetup(3,"#1","#2","#3"))}} +\def\xmlafterdocumentsetup #1#2#3{\ctxlua{lxml.installsetup(4,"#1","#2","#3"))}} + +\def\xmlremovesetup #1{\ctxlua{lxml.removesetup("*","#1")}} +\def\xmlremovedocumentsetup #1#2{\ctxlua{lxml.removesetup("#1","#2")}} + +\def\xmlflushdocumentsetups #1#2{\ctxlua{lxml.flushsetups("#1","*","#2")}} % #1 == id where to apply * and #2 +\def\xmlresetdocumentsetups #1{\ctxlua{lxml.resetsetups("#1")}} + +\let\xmlregistersetup \xmlappendsetup +\let\xmlregisterdocumentsetup\xmlappenddocumentsetup + +\def\xmldocument{main} + +\def\xmlregisteredsetups + {\xmlstarttiming + \xmlflushsetups + \xmldefaulttotext\xmldocument % after include + \xmlstoptiming} + +\def\xmlregistereddocumentsetups#1#2% id setups + {\xmlstarttiming + % todo: test for duplicates ! + \xmlflushdocumentsetups{#1}{#2}% + \xmldefaulttotext{#1}% after include + \xmlstoptiming} + +\def\xmlstarttiming{\ctxlua{statistics.starttiming(lxml)}} +\def\xmlstoptiming {\ctxlua{statistics.stoptiming (lxml)}} + +\def\doxmlprocess#1#2#3#4#5% flag \loader id name what initializersetup + {\begingroup + \edef\xmldocument{#3}% #2 can be \xmldocument and set as such + %xmlpushdocument{#3}% + #2{#3}{#4}% + \setcatcodetable\notcatcodes + \doifelsenothing{#5} + {\xmlsetup{#3}{xml:process}} + {\xmlsetup{#3}{#5}}% + %xmlpopdocument + \endgroup} + +\def\xmlprocessfile {\doxmlprocess\plusone \xmlload} +\def\xmlprocessdata {\doxmlprocess\zerocount\xmlloaddata} +\def\xmlprocessbuffer {\doxmlprocess\zerocount\xmlloadbuffer} +\def\xmlprocessregistered{\doxmlprocess\zerocount\xmlloadregistered} +\let\xmlprocess \xmlprocessfile + +\startxmlsetups xml:flush + \xmlflush{#1} +\stopxmlsetups + +\startxmlsetups xml:process + \xmlregistereddocumentsetups{#1}{#1} + \xmlmain{#1} +\stopxmlsetups + +\def\xmlloadonly#1#2#3% + {\xmlload{#1}{#2}% + \xmlregistereddocumentsetups{#1}{#3}} + +% beware: \xmlmain takes the real root, so also processing +% instructions preceding the root element; well, in some +% sense that is the root + +\long\def\xmlconnect#1#2#3% inefficient + {\scratchcounter\xmlcount{#1}{#2}\relax + \ifcase\scratchcounter \or + \xmlall{#1}{#2}% + \else + \dorecurse \scratchcounter + {\ifnum\recurselevel>\plusone#3\fi + \xmlidx{#1}{#2}\recurselevel}% + \fi} + +\def\xmlcdataobeyedline {\obeyedline} +\def\xmlcdataobeyedspace{\strut\obeyedspace} +\def\xmlcdatabefore {\bgroup\tt} +\def\xmlcdataafter {\egroup} + +% verbatim (dodo:pre/post whitespace, maybe splot verbatim and +% cdata commands), experimental: +% +% \xmlsetfunction{main}{verbatim}{lxml.displayverbatim} +% \xmlsetfunction{main}{verb} {lxml.inlineverbatim} + +% \unexpanded\def\startxmldisplayverbatim[#1]{} +% \unexpanded\def\stopxmldisplayverbatim {} +% \unexpanded\def\startxmlinlineverbatim [#1]{} +% \unexpanded\def\stopxmlinlineverbatim {} + +% we use an xml: namespace so one has to define a suitable verbatim, say +% +% \definetyping[xml:verbatim][typing] +% +% this is experimental! + +\unexpanded\def\startxmldisplayverbatim[#1]% + {\startpacked % \begingroup + \let\currenttypingclass\??tp + \edef\currenttyping{xml:#1}% + \unexpanded\def\stopxmldisplayverbatim + {\endofverbatimlines + \stoppacked} % \endgroup + \doinitializeverbatim + \beginofverbatimlines} + +\unexpanded\def\startxmlinlineverbatim[#1]% + {\begingroup + \let\currenttypingclass\??ty + \edef\currenttyping{xml:#1}% + \let\stopxmldisplayverbatim\endgroup + \doinitializeverbatim} + +% will move but is developed for xml + +\newtoks \collectingtoks + +\unexpanded\def\startcollect#1\stopcollect + {\collectingtoks\@EA{\the\collectingtoks#1}} + +\unexpanded\def\startexpandedcollect#1\stopexpandedcollect + {\normalexpanded{\collectingtoks{\the\collectingtoks#1}}} + +\unexpanded\def\startcollecting{\collectingtoks\emptytoks} +\unexpanded\def\stopcollecting {\the\collectingtoks} + +\def\inlinemessage #1{\dontleavehmode{\tttf#1}} +\def\displaymessage#1{\blank\inlinemessage{#1}\blank} + +\def\xmltraceentities + {\ctxlua{xml.set_text_cleanup(lxml.trace_text_entities)}% + \appendtoks\ctxlua{lxml.show_text_entities()}\to\everygoodbye} + +% processing instructions + +\def\xmlcontextdirective#1% kind class key value + {\executeifdefined{xml#1directive}\gobblethreearguments} + +% setting up xml: +% +% \setupxml[\c!default=] % mkiv only == text +% \setupxml[\c!default=\v!none] % mkiv only, undefined -> hidden +% \setupxml[\c!default=\v!text] % mkiv only, undefined -> text + +% \def\xmlctxdirective#1#2#3{\doif{#1}{clue}{\doif{#2}{page}}{\page[#3]}} + +\chardef\xmlprocessingmode=0 % 0=unset, 1=text, 2=hidden + +\newtoks\everysetupxml + +\unexpanded\def\setupxml[#1]{\getparameters[\??xm][#1]\the\everysetupxml} + + +\letvalue{\??xm:\s!default:\v!normal}\zerocount +\letvalue{\??xm:\s!default:\v!none }\zerocount +\letvalue{\??xm:\s!default:\v!text }\plusone +\letvalue{\??xm:\s!default:\v!hidden}\plustwo + +\def\xmldefaulttotext#1% + {\ifcase\xmlprocessingmode + % unset + \or + \ctxlua{lxml.set_command_to_text("#1")}% 1 + \or + \ctxlua{lxml.set_command_to_none("#1")}% 2 + \else + % unset + \fi} + +\appendtoks + \chardef\xmlprocessingmode\executeifdefined{\??xm:\s!default:\@@xmdefault}\plusone +\to \everysetupxml + +\def\xmlinitialize{\the\everysetupxml} + +\setupxml + [\c!default=, % flush all + \c!compress=\v!no, % strip comment + \c!entities=\v!yes] % replace entities + +% \defineXMLenvironment[y]{(}{)} +% +% \startxmlsetups x +% /\xmlflush{#1}/ +% \stopxmlsetups +% +% \startxmlsetups xx +% \xmlsetsetup{main}{x}{*} +% \stopxmlsetups +% +% \xmlregistersetup{xx} +% +% \startbuffer +%This module can be used stand alone but also inside
If I can get in the mood I will make a variant that is XSLT compliant +but I wonder if it makes sense.
+--ldx]]-- + +--[[ldx-- +Expecially the lpath code is experimental, we will support some of xpath, but
+only things that make sense for us; as compensation it is possible to hook in your
+own functions. Apart from preprocessing content for
We've now arrived at an interesting part: accessing the tree using a subset
+of
This is the main filter function. It returns whatever is asked for.
+--ldx]]-- + +function xml.filter(root,pattern) -- no longer funny attribute handling here + return parse_apply({ root },pattern) +end + +--[[ldx-- +Often using an iterators looks nicer in the code than passing handler
+functions. The
The following helper functions best belong to the
The parser used here is inspired by the variant discussed in the lua book, but
+handles comment and processing instructions, has a different structure, provides
+parent access; a first version used different trickery but was less optimized to we
+went this route. First we had a find based parser, now we have an
Beware, the interface may change. For instance at, ns, tg, dt may get more +verbose names. Once the code is stable we will also remove some tracing and +optimize the code.
+--ldx]]-- + +xml = xml or { } + +--~ local xml = xml + +local concat, remove, insert = table.concat, table.remove, table.insert +local type, next, setmetatable, getmetatable, tonumber = type, next, setmetatable, getmetatable, tonumber +local format, lower, find, match, gsub = string.format, string.lower, string.find, string.match, string.gsub +local utfchar = unicode.utf8.char +local lpegmatch = lpeg.match +local P, S, R, C, V, C, Cs = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.C, lpeg.Cs + +--[[ldx-- +First a hack to enable namespace resolving. A namespace is characterized by
+a
The next function associates a namespace prefix with an
The next function also registers a namespace, but this time we map a
+given namespace prefix onto a registered one, using the given
+
Next we provide a way to turn an
A namespace in an element can be remapped onto the registered
+one efficiently by using the
This version uses
Next comes the parser. The rather messy doctype definition comes in many
+disguises so it is no surprice that later on have to dedicate quite some
+
The code may look a bit complex but this is mostly due to the fact that we +resolve namespaces and attach metatables. There is only one public function:
+ +An optional second boolean argument tells this function not to create a root +element.
+ +Valid entities are:
+ +Packaging data in an xml like table is done with the following +function. Maybe it will go away (when not used).
+--ldx]]-- + +function xml.is_valid(root) + return root and root.dt and root.dt[1] and type(root.dt[1]) == "table" and not root.dt[1].er +end + +function xml.package(tag,attributes,data) + local ns, tg = match(tag,"^(.-):?([^:]+)$") + local t = { ns = ns, tg = tg, dt = data or "", at = attributes or {} } + setmetatable(t, mt) + return t +end + +function xml.is_valid(root) + return root and not root.error +end + +xml.error_handler = (logs and logs.report) or (input and logs.report) or print + +--[[ldx-- +We cannot load an
When we inject new elements, we need to convert strings to +valid trees, which is what the next function does.
+--ldx]]-- + +local no_root = { no_root = true } + +function xml.toxml(data) + if type(data) == "string" then + local root = { xmlconvert(data,no_root) } + return (#root > 1 and root) or root[1] + else + return data + end +end + +--[[ldx-- +For copying a tree we use a dedicated function instead of the +generic table copier. Since we know what we're dealing with we +can speed up things a bit. The second argument is not to be used!
+--ldx]]-- + +local function copy(old,tables) + if old then + tables = tables or { } + local new = { } + if not tables[old] then + tables[old] = new + end + for k,v in next, old do + new[k] = (type(v) == "table" and (tables[v] or copy(v, tables))) or v + end + local mt = getmetatable(old) + if mt then + setmetatable(new,mt) + end + return new + else + return { } + end +end + +xml.copy = copy + +--[[ldx-- +In
At the cost of some 25% runtime overhead you can first convert the tree to a string +and then handle the lot.
+--ldx]]-- + +-- new experimental reorganized serialize + +local function verbose_element(e,handlers) + local handle = handlers.handle + local serialize = handlers.serialize + local ens, etg, eat, edt, ern = e.ns, e.tg, e.at, e.dt, e.rn + local ats = eat and next(eat) and { } + if ats then + for k,v in next, eat do + ats[#ats+1] = format('%s=%q',k,v) + end + end + if ern and trace_remap and ern ~= ens then + ens = ern + end + if ens ~= "" then + if edt and #edt > 0 then + if ats then + handle("<",ens,":",etg," ",concat(ats," "),">") + else + handle("<",ens,":",etg,">") + end + for i=1,#edt do + local e = edt[i] + if type(e) == "string" then + handle(e) + else + serialize(e,handlers) + end + end + handle("",ens,":",etg,">") + else + if ats then + handle("<",ens,":",etg," ",concat(ats," "),"/>") + else + handle("<",ens,":",etg,"/>") + end + end + else + if edt and #edt > 0 then + if ats then + handle("<",etg," ",concat(ats," "),">") + else + handle("<",etg,">") + end + for i=1,#edt do + local ei = edt[i] + if type(ei) == "string" then + handle(ei) + else + serialize(ei,handlers) + end + end + handle("",etg,">") + else + if ats then + handle("<",etg," ",concat(ats," "),"/>") + else + handle("<",etg,"/>") + end + end + end +end + +local function verbose_pi(e,handlers) + handlers.handle("",e.dt[1],"?>") +end + +local function verbose_comment(e,handlers) + handlers.handle("") +end + +local function verbose_cdata(e,handlers) + handlers.handle("") +end + +local function verbose_doctype(e,handlers) + handlers.handle("") +end + +local function verbose_root(e,handlers) + handlers.serialize(e.dt,handlers) +end + +local function verbose_text(e,handlers) + handlers.handle(e) +end + +local function verbose_document(e,handlers) + local serialize = handlers.serialize + local functions = handlers.functions + for i=1,#e do + local ei = e[i] + if type(ei) == "string" then + functions["@tx@"](ei,handlers) + else + serialize(ei,handlers) + end + end +end + +local function serialize(e,handlers,...) + local initialize = handlers.initialize + local finalize = handlers.finalize + local functions = handlers.functions + if initialize then + local state = initialize(...) + if not state == true then + return state + end + end + local etg = e.tg + if etg then + (functions[etg] or functions["@el@"])(e,handlers) + -- elseif type(e) == "string" then + -- functions["@tx@"](e,handlers) + else + functions["@dc@"](e,handlers) + end + if finalize then + return finalize() + end +end + +local function xserialize(e,handlers) + local functions = handlers.functions + local etg = e.tg + if etg then + (functions[etg] or functions["@el@"])(e,handlers) + -- elseif type(e) == "string" then + -- functions["@tx@"](e,handlers) + else + functions["@dc@"](e,handlers) + end +end + +local handlers = { } + +local function newhandlers(settings) + local t = table.copy(handlers.verbose or { }) -- merge + if settings then + for k,v in next, settings do + if type(v) == "table" then + tk = t[k] if not tk then tk = { } t[k] = tk end + for kk,vv in next, v do + tk[kk] = vv + end + else + t[k] = v + end + end + if settings.name then + handlers[settings.name] = t + end + end + return t +end + +local nofunction = function() end + +function xml.sethandlersfunction(handler,name,fnc) + handler.functions[name] = fnc or nofunction +end + +function xml.gethandlersfunction(handler,name) + return handler.functions[name] +end + +function xml.gethandlers(name) + return handlers[name] +end + +newhandlers { + name = "verbose", + initialize = false, -- faster than nil and mt lookup + finalize = false, -- faster than nil and mt lookup + serialize = xserialize, + handle = print, + functions = { + ["@dc@"] = verbose_document, + ["@dt@"] = verbose_doctype, + ["@rt@"] = verbose_root, + ["@el@"] = verbose_element, + ["@pi@"] = verbose_pi, + ["@cm@"] = verbose_comment, + ["@cd@"] = verbose_cdata, + ["@tx@"] = verbose_text, + } +} + +--[[ldx-- +How you deal with saving data depends on your preferences. For a 40 MB database +file the timing on a 2.3 Core Duo are as follows (time in seconds):
+ +Beware, these were timing with the old routine but measurements will not be that +much different I guess.
+--ldx]]-- + +-- maybe this will move to lxml-xml + +local result + +local xmlfilehandler = newhandlers { + name = "file", + initialize = function(name) result = io.open(name,"wb") return result end, + finalize = function() result:close() return true end, + handle = function(...) result:write(...) end, +} + +-- no checking on writeability here but not faster either +-- +-- local xmlfilehandler = newhandlers { +-- initialize = function(name) io.output(name,"wb") return true end, +-- finalize = function() io.close() return true end, +-- handle = io.write, +-- } + + +function xml.save(root,name) + serialize(root,xmlfilehandler,name) +end + +local result + +local xmlstringhandler = newhandlers { + name = "string", + initialize = function() result = { } return result end, + finalize = function() return concat(result) end, + handle = function(...) result[#result+1] = concat { ... } end +} + +local function xmltostring(root) -- 25% overhead due to collecting + if root then + if type(root) == 'string' then + return root + else -- if next(root) then -- next is faster than type (and >0 test) + return serialize(root,xmlstringhandler) or "" + end + end + return "" +end + +local function xmltext(root) -- inline + return (root and xmltostring(root)) or "" +end + +function initialize_mt(root) + mt = { __tostring = xmltext, __index = root } +end + +xml.defaulthandlers = handlers +xml.newhandlers = newhandlers +xml.serialize = serialize +xml.tostring = xmltostring + +--[[ldx-- +The next function operated on the content only and needs a handle function +that accepts a string.
+--ldx]]-- + +local function xmlstring(e,handle) + if not handle or (e.special and e.tg ~= "@rt@") then + -- nothing + elseif e.tg then + local edt = e.dt + if edt then + for i=1,#edt do + xmlstring(edt[i],handle) + end + end + else + handle(e) + end +end + +xml.string = xmlstring + +--[[ldx-- +A few helpers:
+--ldx]]-- + +--~ xmlsetproperty(root,"settings",settings) + +function xml.settings(e) + while e do + local s = e.settings + if s then + return s + else + e = e.__p__ + end + end + return nil +end + +function xml.root(e) + local r = e + while e do + e = e.__p__ + if e then + r = e + end + end + return r +end + +function xml.parent(root) + return root.__p__ +end + +function xml.body(root) + return (root.ri and root.dt[root.ri]) or root -- not ok yet +end + +function xml.name(root) + if not root then + return "" + elseif root.ns == "" then + return root.tg + else + return root.ns .. ":" .. root.tg + end +end + +--[[ldx-- +The next helper erases an element but keeps the table as it is, +and since empty strings are not serialized (effectively) it does +not harm. Copying the table would take more time. Usage:
+--ldx]]-- + +function xml.erase(dt,k) + if dt then + if k then + dt[k] = "" + else for k=1,#dt do + dt[1] = { "" } + end end + end +end + +--[[ldx-- +The next helper assigns a tree (or string). Usage:
+ +The next helper assigns a tree (or string). Usage:
+Remapping mathematics alphabets.
+--ldx]]-- + +-- oldstyle: not really mathematics but happened to be part of +-- the mathematics fonts in cmr +-- +-- persian: we will also provide mappers for other +-- scripts + +-- todo: alphabets namespace +-- maybe: script/scriptscript dynamic, + +local type, next = type, next +local floor = math.floor + +local texattribute = tex.attribute + +local trace_greek = false trackers.register("math.greek", function(v) trace_greek = v end) + +mathematics = mathematics or { } + +-- we could use one level less and have tf etc be tables directly but the +-- following approach permits easier remapping of a-a, A-Z and 0-9 to +-- fallbacks; symbols is currently mostly greek + +mathematics.alphabets = { + regular = { + tf = { + digits = 0x00030, + ucletters = 0x00041, + lcletters = 0x00061, + ucgreek = { + [0x0391]=0x0391, [0x0392]=0x0392, [0x0393]=0x0393, [0x0394]=0x0394, [0x0395]=0x0395, + [0x0396]=0x0396, [0x0397]=0x0397, [0x0398]=0x0398, [0x0399]=0x0399, [0x039A]=0x039A, + [0x039B]=0x039B, [0x039C]=0x039C, [0x039D]=0x039D, [0x039E]=0x039E, [0x039F]=0x039F, + [0x03A0]=0x03A0, [0x03A1]=0x03A1, [0x03A3]=0x03A3, [0x03A4]=0x03A4, [0x03A5]=0x03A5, + [0x03A6]=0x03A6, [0x03A7]=0x03A7, [0x03A8]=0x03A8, [0x03A9]=0x03A9, + }, + lcgreek = { + [0x03B1]=0x03B1, [0x03B2]=0x03B2, [0x03B3]=0x03B3, [0x03B4]=0x03B4, [0x03B5]=0x03B5, + [0x03B6]=0x03B6, [0x03B7]=0x03B7, [0x03B8]=0x03B8, [0x03B9]=0x03B9, [0x03BA]=0x03BA, + [0x03BB]=0x03BB, [0x03BC]=0x03BC, [0x03BD]=0x03BD, [0x03BE]=0x03BE, [0x03BF]=0x03BF, + [0x03C0]=0x03C0, [0x03C1]=0x03C1, [0x03C2]=0x03C2, [0x03C3]=0x03C3, [0x03C4]=0x03C4, + [0x03C5]=0x03C5, [0x03C6]=0x03C6, [0x03C7]=0x03C7, [0x03C8]=0x03C8, [0x03C9]=0x03C9, + [0x03D1]=0x03D1, [0x03D5]=0x03D5, [0x03D6]=0x03D6, [0x03F0]=0x03F0, [0x03F1]=0x03F1, + [0x03F4]=0x03F4, [0x03F5]=0x03F5, + }, + symbols = { + [0x2202]=0x2202, [0x2207]=0x2207, + }, + }, + it = { + ucletters = 0x1D434, + lcletters = { -- H + [0x00061]=0x1D44E, [0x00062]=0x1D44F, [0x00063]=0x1D450, [0x00064]=0x1D451, [0x00065]=0x1D452, + [0x00066]=0x1D453, [0x00067]=0x1D454, [0x00068]=0x0210E, [0x00069]=0x1D456, [0x0006A]=0x1D457, + [0x0006B]=0x1D458, [0x0006C]=0x1D459, [0x0006D]=0x1D45A, [0x0006E]=0x1D45B, [0x0006F]=0x1D45C, + [0x00070]=0x1D45D, [0x00071]=0x1D45E, [0x00072]=0x1D45F, [0x00073]=0x1D460, [0x00074]=0x1D461, + [0x00075]=0x1D462, [0x00076]=0x1D463, [0x00077]=0x1D464, [0x00078]=0x1D465, [0x00079]=0x1D466, + [0x0007A]=0x1D467, + }, + ucgreek = { + [0x0391]=0x1D6E2, [0x0392]=0x1D6E3, [0x0393]=0x1D6E4, [0x0394]=0x1D6E5, [0x0395]=0x1D6E6, + [0x0396]=0x1D6E7, [0x0397]=0x1D6E8, [0x0398]=0x1D6E9, [0x0399]=0x1D6EA, [0x039A]=0x1D6EB, + [0x039B]=0x1D6EC, [0x039C]=0x1D6ED, [0x039D]=0x1D6EE, [0x039E]=0x1D6EF, [0x039F]=0x1D6F0, + [0x03A0]=0x1D6F1, [0x03A1]=0x1D6F2, [0x03A3]=0x1D6F4, [0x03A4]=0x1D6F5, [0x03A5]=0x1D6F6, + [0x03A6]=0x1D6F7, [0x03A7]=0x1D6F8, [0x03A8]=0x1D6F9, [0x03A9]=0x1D6FA, + }, + lcgreek = { + [0x03B1]=0x1D6FC, [0x03B2]=0x1D6FD, [0x03B3]=0x1D6FE, [0x03B4]=0x1D6FF, [0x03B5]=0x1D700, + [0x03B6]=0x1D701, [0x03B7]=0x1D702, [0x03B8]=0x1D703, [0x03B9]=0x1D704, [0x03BA]=0x1D705, + [0x03BB]=0x1D706, [0x03BC]=0x1D707, [0x03BD]=0x1D708, [0x03BE]=0x1D709, [0x03BF]=0x1D70A, + [0x03C0]=0x1D70B, [0x03C1]=0x1D70C, [0x03C2]=0x1D70D, [0x03C3]=0x1D70E, [0x03C4]=0x1D70F, + [0x03C5]=0x1D710, [0x03C6]=0x1D711, [0x03C7]=0x1D712, [0x03C8]=0x1D713, [0x03C9]=0x1D714, + [0x03D1]=0x1D717, [0x03D5]=0x1D719, [0x03D6]=0x1D71B, [0x03F0]=0x1D718, [0x03F1]=0x1D71A, + [0x03F4]=0x1D6F3, [0x03F5]=0x1D716, + }, + symbols = { + [0x2202]=0x1D715, [0x2207]=0x1D6FB, + }, + }, + bf= { + digits = 0x1D7CE, + ucletters = 0x1D400, + lcletters = 0x1D41A, + ucgreek = { + [0x0391]=0x1D6A8, [0x0392]=0x1D6A9, [0x0393]=0x1D6AA, [0x0394]=0x1D6AB, [0x0395]=0x1D6AC, + [0x0396]=0x1D6AD, [0x0397]=0x1D6AE, [0x0398]=0x1D6AF, [0x0399]=0x1D6B0, [0x039A]=0x1D6B1, + [0x039B]=0x1D6B2, [0x039C]=0x1D6B3, [0x039D]=0x1D6B4, [0x039E]=0x1D6B5, [0x039F]=0x1D6B6, + [0x03A0]=0x1D6B7, [0x03A1]=0x1D6B8, [0x03A3]=0x1D6BA, [0x03A4]=0x1D6BB, [0x03A5]=0x1D6BC, + [0x03A6]=0x1D6BD, [0x03A7]=0x1D6BE, [0x03A8]=0x1D6BF, [0x03A9]=0x1D6C0, + }, + lcgreek = { + [0x03B1]=0x1D6C2, [0x03B2]=0x1D6C3, [0x03B3]=0x1D6C4, [0x03B4]=0x1D6C5, [0x03B5]=0x1D6C6, + [0x03B6]=0x1D6C7, [0x03B7]=0x1D6C8, [0x03B8]=0x1D6C9, [0x03B9]=0x1D6CA, [0x03BA]=0x1D6CB, + [0x03BB]=0x1D6CC, [0x03BC]=0x1D6CD, [0x03BD]=0x1D6CE, [0x03BE]=0x1D6CF, [0x03BF]=0x1D6D0, + [0x03C0]=0x1D6D1, [0x03C1]=0x1D6D2, [0x03C2]=0x1D6D3, [0x03C3]=0x1D6D4, [0x03C4]=0x1D6D5, + [0x03C5]=0x1D6D6, [0x03C6]=0x1D6D7, [0x03C7]=0x1D6D8, [0x03C8]=0x1D6D9, [0x03C9]=0x1D6DA, + [0x03D1]=0x1D6DD, [0x03D5]=0x1D6DF, [0x03D6]=0x1D6E1, [0x03F0]=0x1D6DE, [0x03F1]=0x1D6E0, + [0x03F4]=0x1D6B9, [0x03F5]=0x1D6DC, + }, + symbols = { + [0x2202]=0x1D6DB, [0x2207]=0x1D6C1, + }, + }, + bi = { + ucletters = 0x1D468, + lcletters = 0x1D482, + ucgreek = { + [0x0391]=0x1D71C, [0x0392]=0x1D71D, [0x0393]=0x1D71E, [0x0394]=0x1D71F, [0x0395]=0x1D720, + [0x0396]=0x1D721, [0x0397]=0x1D722, [0x0398]=0x1D723, [0x0399]=0x1D724, [0x039A]=0x1D725, + [0x039B]=0x1D726, [0x039C]=0x1D727, [0x039D]=0x1D728, [0x039E]=0x1D729, [0x039F]=0x1D72A, + [0x03A0]=0x1D72B, [0x03A1]=0x1D72C, [0x03A3]=0x1D72E, [0x03A4]=0x1D72F, [0x03A5]=0x1D730, + [0x03A6]=0x1D731, [0x03A7]=0x1D732, [0x03A8]=0x1D733, [0x03A9]=0x1D734, + }, + lcgreek = { + [0x03B1]=0x1D736, [0x03B2]=0x1D737, [0x03B3]=0x1D738, [0x03B4]=0x1D739, [0x03B5]=0x1D73A, + [0x03B6]=0x1D73B, [0x03B7]=0x1D73C, [0x03B8]=0x1D73D, [0x03B9]=0x1D73E, [0x03BA]=0x1D73F, + [0x03BB]=0x1D740, [0x03BC]=0x1D741, [0x03BD]=0x1D742, [0x03BE]=0x1D743, [0x03BF]=0x1D744, + [0x03C0]=0x1D745, [0x03C1]=0x1D746, [0x03C2]=0x1D747, [0x03C3]=0x1D748, [0x03C4]=0x1D749, + [0x03C5]=0x1D74A, [0x03C6]=0x1D74B, [0x03C7]=0x1D74C, [0x03C8]=0x1D74D, [0x03C9]=0x1D74E, + [0x03D1]=0x1D751, [0x03D5]=0x1D753, [0x03D6]=0x1D755, [0x03F0]=0x1D752, [0x03F1]=0x1D754, + [0x03F4]=0x1D72D, [0x03F5]=0x1D750, + }, + symbols = { + [0x2202]=0x1D74F, [0x2207]=0x1D735, + }, + }, + }, + sansserif = { + tf = { + digits = 0x1D7E2, + ucletters = 0x1D5A0, + lcletters = 0x1D5BA, + }, + it = { + ucletters = 0x1D608, + lcletters = 0x1D622, + }, + bf = { + digits = 0x1D7EC, + ucletters = 0x1D5D4, + lcletters = 0x1D5EE, + ucgreek = { + [0x0391]=0x1D756, [0x0392]=0x1D757, [0x0393]=0x1D758, [0x0394]=0x1D759, [0x0395]=0x1D75A, + [0x0396]=0x1D75B, [0x0397]=0x1D75C, [0x0398]=0x1D75D, [0x0399]=0x1D75E, [0x039A]=0x1D75F, + [0x039B]=0x1D760, [0x039C]=0x1D761, [0x039D]=0x1D762, [0x039E]=0x1D763, [0x039F]=0x1D764, + [0x03A0]=0x1D765, [0x03A1]=0x1D766, [0x03A3]=0x1D768, [0x03A4]=0x1D769, [0x03A5]=0x1D76A, + [0x03A6]=0x1D76B, [0x03A7]=0x1D76C, [0x03A8]=0x1D76D, [0x03A9]=0x1D76E, + }, + lcgreek = { + [0x03B1]=0x1D770, [0x03B2]=0x1D771, [0x03B3]=0x1D772, [0x03B4]=0x1D773, [0x03B5]=0x1D774, + [0x03B6]=0x1D775, [0x03B7]=0x1D776, [0x03B8]=0x1D777, [0x03B9]=0x1D778, [0x03BA]=0x1D779, + [0x03BB]=0x1D77A, [0x03BC]=0x1D77B, [0x03BD]=0x1D77C, [0x03BE]=0x1D77D, [0x03BF]=0x1D77E, + [0x03C0]=0x1D77F, [0x03C1]=0x1D780, [0x03C2]=0x1D781, [0x03C3]=0x1D782, [0x03C4]=0x1D783, + [0x03C5]=0x1D784, [0x03C6]=0x1D785, [0x03C7]=0x1D786, [0x03C8]=0x1D787, [0x03C9]=0x1D788, + [0x03D1]=0x1D78B, [0x03D5]=0x1D78D, [0x03D6]=0x1D78F, [0x03F0]=0x1D78C, [0x03F1]=0x1D78E, + [0x03F4]=0x1D767, [0x03F5]=0x1D78A, + }, + symbols = { + [0x2202]=0x1D789, [0x2207]=0x1D76F, + }, + }, + bi = { + ucletters = 0x1D63C, + lcletters = 0x1D656, + ucgreek = { + [0x0391]=0x1D790, [0x0392]=0x1D791, [0x0393]=0x1D792, [0x0394]=0x1D793, [0x0395]=0x1D794, + [0x0396]=0x1D795, [0x0397]=0x1D796, [0x0398]=0x1D797, [0x0399]=0x1D798, [0x039A]=0x1D799, + [0x039B]=0x1D79A, [0x039C]=0x1D79B, [0x039D]=0x1D79C, [0x039E]=0x1D79D, [0x039F]=0x1D79E, + [0x03A0]=0x1D79F, [0x03A1]=0x1D7A0, [0x03A3]=0x1D7A2, [0x03A4]=0x1D7A3, [0x03A5]=0x1D7A4, + [0x03A6]=0x1D7A5, [0x03A7]=0x1D7A6, [0x03A8]=0x1D7A7, [0x03A9]=0x1D7A8, + }, + lcgreek = { + [0x03B1]=0x1D7AA, [0x03B2]=0x1D7AB, [0x03B3]=0x1D7AC, [0x03B4]=0x1D7AD, [0x03B5]=0x1D7AE, + [0x03B6]=0x1D7AF, [0x03B7]=0x1D7B0, [0x03B8]=0x1D7B1, [0x03B9]=0x1D7B2, [0x03BA]=0x1D7B3, + [0x03BB]=0x1D7B4, [0x03BC]=0x1D7B5, [0x03BD]=0x1D7B6, [0x03BE]=0x1D7B7, [0x03BF]=0x1D7B8, + [0x03C0]=0x1D7B9, [0x03C1]=0x1D7BA, [0x03C2]=0x1D7BB, [0x03C3]=0x1D7BC, [0x03C4]=0x1D7BD, + [0x03C5]=0x1D7BE, [0x03C6]=0x1D7BF, [0x03C7]=0x1D7C0, [0x03C8]=0x1D7C1, [0x03C9]=0x1D7C2, + [0x03D1]=0x1D7C5, [0x03D5]=0x1D7C7, [0x03D6]=0x1D7C9, [0x03F0]=0x1D7C6, [0x03F1]=0x1D7C8, + [0x03F4]=0x1D7A1, [0x03F5]=0x1D7C4, + }, + symbols = { + [0x2202]=0x1D7C3, [0x2207]=0x1D7A9, + }, + }, + }, + monospaced = { + tf = { + digits = 0x1D7F6, + ucletters = 0x1D670, + lcletters = 0x1D68A, + }, + }, + blackboard = { -- ok + tf = { + digits = 0x1D7D8, + ucletters = { -- C H N P Q R Z + [0x00041]=0x1D538, [0x00042]=0x1D539, [0x00043]=0x02102, [0x00044]=0x1D53B, [0x00045]=0x1D53C, + [0x00046]=0x1D53D, [0x00047]=0x1D53E, [0x00048]=0x0210D, [0x00049]=0x1D540, [0x0004A]=0x1D541, + [0x0004B]=0x1D542, [0x0004C]=0x1D543, [0x0004D]=0x1D544, [0x0004E]=0x02115, [0x0004F]=0x1D546, + [0x00050]=0x02119, [0x00051]=0x0211A, [0x00052]=0x0211D, [0x00053]=0x1D54A, [0x00054]=0x1D54B, + [0x00055]=0x1D54C, [0x00056]=0x1D54D, [0x00057]=0x1D54E, [0x00058]=0x1D54F, [0x00059]=0x1D550, + [0x0005A]=0x02124, + }, + lcletters = 0x1D552, + lcgreek = { -- gamma pi + [0x03B3]=0x0213C, [0x03C0]=0x0213D, + }, + ucgreek = { -- Gamma pi + [0x0393]=0x0213E, [0x03A0]=0x0213F, + }, + symbols = { -- sum + [0x2211]=0x02140, + }, + }, + }, + fraktur = { -- ok + tf= { + ucletters = { -- C H I R Z + [0x00041]=0x1D504, [0x00042]=0x1D505, [0x00043]=0x0212D, [0x00044]=0x1D507, [0x00045]=0x1D508, + [0x00046]=0x1D509, [0x00047]=0x1D50A, [0x00048]=0x0210C, [0x00049]=0x02111, [0x0004A]=0x1D50D, + [0x0004B]=0x1D50E, [0x0004C]=0x1D50F, [0x0004D]=0x1D510, [0x0004E]=0x1D511, [0x0004F]=0x1D512, + [0x00050]=0x1D513, [0x00051]=0x1D514, [0x00052]=0x0211C, [0x00053]=0x1D516, [0x00054]=0x1D517, + [0x00055]=0x1D518, [0x00056]=0x1D519, [0x00057]=0x1D51A, [0x00058]=0x1D51B, [0x00059]=0x1D51C, + [0x0005A]=0x02128, + }, + lcletters = 0x1D51E, + }, + bf = { + ucletters = 0x1D56C, + lcletters = 0x1D586, + }, + }, + script = { + tf= { + ucletters = { -- B E F H I L M R -- P 2118 + [0x00041]=0x1D49C, [0x00042]=0x0212C, [0x00043]=0x1D49E, [0x00044]=0x1D49F, [0x00045]=0x02130, + [0x00046]=0x02131, [0x00047]=0x1D4A2, [0x00048]=0x0210B, [0x00049]=0x02110, [0x0004A]=0x1D4A5, + [0x0004B]=0x1D4A6, [0x0004C]=0x02112, [0x0004D]=0x02133, [0x0004E]=0x1D4A9, [0x0004F]=0x1D4AA, + [0x00050]=0x1D4AB, [0x00051]=0x1D4AC, [0x00052]=0x0211B, [0x00053]=0x1D4AE, [0x00054]=0x1D4AF, + [0x00055]=0x1D4B0, [0x00056]=0x1D4B1, [0x00057]=0x1D4B2, [0x00058]=0x1D4B3, [0x00059]=0x1D4B4, + [0x0005A]=0x1D4B5, + }, + lcletters = { -- E G O -- L 2113 + [0x00061]=0x1D4B6, [0x00062]=0x1D4B7, [0x00063]=0x1D4B8, [0x00064]=0x1D4B9, [0x00065]=0x0212F, + [0x00066]=0x1D4BB, [0x00067]=0x0210A, [0x00068]=0x1D4BD, [0x00069]=0x1D4BE, [0x0006A]=0x1D4BF, + [0x0006B]=0x1D4C0, [0x0006C]=0x1D4C1, [0x0006D]=0x1D4C2, [0x0006E]=0x1D4C3, [0x0006F]=0x02134, + [0x00070]=0x1D4C5, [0x00071]=0x1D4C6, [0x00072]=0x1D4C7, [0x00073]=0x1D4C8, [0x00074]=0x1D4C9, + [0x00075]=0x1D4CA, [0x00076]=0x1D4CB, [0x00077]=0x1D4CC, [0x00078]=0x1D4CD, [0x00079]=0x1D4CE, + [0x0007A]=0x1D4CF, + } + }, + bf = { + ucletters = 0x1D4D0, + lcletters = 0x1D4EA, + }, + }, +} + +local alphabets = mathematics.alphabets +local mathremap = { } + +for alphabet, styles in next, alphabets do + for style, data in next, styles do + -- let's keep the long names (for tracing) + local n = #mathremap + 1 + data.attribute = n + data.alphabet = alphabet + data.style = style + mathremap[n] = data + end +end + +-- beware, these are shared tables (no problem since they're not +-- in unicode) + +alphabets.regular.it.digits = alphabets.regular.tf.digits +alphabets.regular.bi.digits = alphabets.regular.bf.digits + +alphabets.sansserif.tf.symbols = alphabets.regular.tf.symbols +alphabets.sansserif.tf.lcgreek = alphabets.regular.tf.lcgreek +alphabets.sansserif.tf.ucgreek = alphabets.regular.tf.ucgreek +alphabets.sansserif.tf.digits = alphabets.regular.tf.digits +alphabets.sansserif.it.symbols = alphabets.regular.tf.symbols +alphabets.sansserif.it.lcgreek = alphabets.regular.tf.lcgreek +alphabets.sansserif.it.ucgreek = alphabets.regular.tf.ucgreek +alphabets.sansserif.bi.digits = alphabets.regular.bf.digits + +alphabets.monospaced.tf.symbols = alphabets.sansserif.tf.symbols +alphabets.monospaced.tf.lcgreek = alphabets.sansserif.tf.lcgreek +alphabets.monospaced.tf.ucgreek = alphabets.sansserif.tf.ucgreek +alphabets.monospaced.it = alphabets.sansserif.tf +alphabets.monospaced.bf = alphabets.sansserif.tf +alphabets.monospaced.bi = alphabets.sansserif.bf + +alphabets.blackboard.tf.symbols = table.merge(alphabets.regular.tf.symbols, alphabets.blackboard.tf.symbols) +alphabets.blackboard.tf.lcgreek = table.merge(alphabets.regular.tf.lcgreek, alphabets.blackboard.tf.lcgreek) +alphabets.blackboard.tf.ucgreek = table.merge(alphabets.regular.tf.ucgreek, alphabets.blackboard.tf.ucgreek) + +alphabets.blackboard.it = alphabets.blackboard.tf +alphabets.blackboard.bf = alphabets.blackboard.tf +alphabets.blackboard.bi = alphabets.blackboard.bf + +alphabets.fraktur.tf.digits = alphabets.regular.tf.digits +alphabets.fraktur.tf.symbols = alphabets.regular.tf.symbols +alphabets.fraktur.tf.lcgreek = alphabets.regular.tf.lcgreek +alphabets.fraktur.tf.ucgreek = alphabets.regular.tf.ucgreek +alphabets.fraktur.bf.digits = alphabets.regular.bf.digits +alphabets.fraktur.bf.symbols = alphabets.regular.bf.symbols +alphabets.fraktur.bf.lcgreek = alphabets.regular.bf.lcgreek +alphabets.fraktur.bf.ucgreek = alphabets.regular.bf.ucgreek +alphabets.fraktur.it = alphabets.fraktur.tf +alphabets.fraktur.bi = alphabets.fraktur.bf + +alphabets.script.tf.digits = alphabets.regular.tf.digits +alphabets.script.tf.symbols = alphabets.regular.tf.symbols +alphabets.script.tf.lcgreek = alphabets.regular.tf.lcgreek +alphabets.script.tf.ucgreek = alphabets.regular.tf.ucgreek +alphabets.script.bf.digits = alphabets.regular.bf.digits +alphabets.script.bf.symbols = alphabets.regular.bf.symbols +alphabets.script.bf.lcgreek = alphabets.regular.bf.lcgreek +alphabets.script.bf.ucgreek = alphabets.regular.bf.ucgreek +alphabets.script.it = alphabets.script.tf +alphabets.script.bi = alphabets.script.bf + +alphabets.tt = alphabets.monospaced +alphabets.ss = alphabets.sansserif +alphabets.rm = alphabets.regular +alphabets.bb = alphabets.blackboard +alphabets.fr = alphabets.fraktur +alphabets.sr = alphabets.script + +alphabets.serif = alphabets.regular +alphabets.type = alphabets.monospaced +alphabets.teletype = alphabets.monospaced + +function mathematics.to_a_style(attribute) + local r = mathremap[attribute] + return r and r.style or "tf" +end + +function mathematics.to_a_name(attribute) + local r = mathremap[attribute] + return r and r.alphabet or "regular" +end + +-- of course we could do some div/mod trickery instead + +local mathalphabet = attributes.private("mathalphabet") + +function mathematics.sync_a_both(alphabet,style) + local data = alphabets[alphabet or "regular"] or alphabets.regular + data = data[style or "tf"] or data.tf + texattribute[mathalphabet] = data and data.attribute or texattribute[mathalphabet] +end + +function mathematics.sync_a_style(style) +--~ local r = mathremap[mathalphabet] + local r = mathremap[texattribute[mathalphabet]] + local alphabet = r and r.alphabet or "regular" + local data = alphabets[alphabet][style] + texattribute[mathalphabet] = data and data.attribute or texattribute[mathalphabet] +end + +function mathematics.sync_a_name(alphabet) +--~ local r = mathremap[mathalphabet] + local r = mathremap[texattribute[mathalphabet]] + local style = r and r.style or "tf" + local data = alphabets[alphabet][style] + texattribute[mathalphabet] = data and data.attribute or texattribute[mathalphabet] +end + +local issymbol = mathematics.alphabets.regular.tf.symbols +local islcgreek = mathematics.alphabets.regular.tf.lcgreek +local isucgreek = mathematics.alphabets.regular.tf.ucgreek + +local remapping = { + [1] = { what = "unchanged" }, -- upright + [2] = { what = "upright", it = "tf", bi = "bf" }, -- upright + [3] = { what = "italic", tf = "it", bf = "bi" }, -- italic +} + +function mathematics.remap_alphabets(char,mathalphabet,mathgreek) + if mathgreek > 0 then + local lc, uc = floor(mathgreek/10), mathgreek % 10 -- 2 == upright 3 == italic + if lc > 1 or uc > 1 then + local islc, isuc = islcgreek[char] and lc, isucgreek[char] and uc + if islc or isuc then + local r = mathremap[mathalphabet] -- what if 0 + local alphabet = r and r.alphabet or "regular" + local style = r and r.style or "tf" + if trace_greek then + logs.report("math","before: char: %05X, alphabet: %s %s, lcgreek: %s, ucgreek: %s",char,alphabet,style,remapping[lc].what,remapping[uc].what) + end + local s = remapping[islc or isuc][style] + if s then + local data = alphabets[alphabet][s] + mathalphabet, style = data and data.attribute or mathalphabet, s + end + if trace_greek then + logs.report("math","after : char: %05X, alphabet: %s %s, lcgreek: %s, ucgreek: %s",char,alphabet,style,remapping[lc].what,remapping[uc].what) + end + end + end + end + if mathalphabet > 0 then + local newchar + local offset = mathremap[mathalphabet] + if not offset then + -- nothing to remap + elseif char >= 0x030 and char <= 0x039 then + local o = offset.digits + newchar = (type(o) == "table" and (o[char] or char)) or (char - 0x030 + o) + elseif char >= 0x041 and char <= 0x05A then + local o = offset.ucletters + newchar = (type(o) == "table" and (o[char] or char)) or (char - 0x041 + o) + elseif char >= 0x061 and char <= 0x07A then + local o = offset.lcletters + newchar = (type(o) == "table" and (o[char] or char)) or (char - 0x061 + o) + elseif islcgreek[char] then + newchar = offset.lcgreek[char] + elseif isucgreek[char] then + newchar = offset.ucgreek[char] + elseif issymbol[char] then + newchar = offset.symbols[char] + end + return newchar ~= char and newchar + end + return nil +end diff --git a/tex/context/base/math-noa.lua b/tex/context/base/math-noa.lua new file mode 100644 index 000000000..02bbe0a62 --- /dev/null +++ b/tex/context/base/math-noa.lua @@ -0,0 +1,369 @@ +if not modules then modules = { } end modules ['math-noa'] = { + version = 1.001, + comment = "companion to math-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- beware: this is experimental code and there will be a more +-- generic (attribute value driven) interface too but for the +-- moment this is ok + +local utf = unicode.utf8 + +local set_attribute = node.set_attribute +local has_attribute = node.has_attribute +local mlist_to_hlist = node.mlist_to_hlist +local font_of_family = node.family_font +local fontdata = fonts.identifiers + +local format, rep = string.format, string.rep +local utfchar, utfbyte = utf.char, utf.byte + +noads = noads or { } + +local trace_remapping = false trackers.register("math.remapping", function(v) trace_remapping = v end) +local trace_processing = false trackers.register("math.processing", function(v) trace_processing = v end) +local trace_analyzing = false trackers.register("math.analyzing", function(v) trace_analyzing = v end) + +local noad_ord = 0 +local noad_op_displaylimits = 1 +local noad_op_limits = 2 +local noad_op_nolimits = 3 +local noad_bin = 4 +local noad_rel = 5 +local noad_open = 6 +local noad_close = 7 +local noad_punct = 8 +local noad_inner = 9 +local noad_under = 10 +local noad_over = 11 +local noad_vcenter = 12 + +-- obsolete: +-- +-- math_ord = node.id("ord") -- attr nucleus sub sup +-- math_op = node.id("op") -- attr nucleus sub sup subtype +-- math_bin = node.id("bin") -- attr nucleus sub sup +-- math_rel = node.id("rel") -- attr nucleus sub sup +-- math_punct = node.id("punct") -- attr nucleus sub sup +-- +-- math_open = node.id("open") -- attr nucleus sub sup +-- math_close = node.id("close") -- attr nucleus sub sup +-- +-- math_inner = node.id("inner") -- attr nucleus sub sup +-- math_vcenter = node.id("vcenter") -- attr nucleus sub sup +-- math_under = node.id("under") -- attr nucleus sub sup +-- math_over = node.id("over") -- attr nucleus sub sup + +local math_noad = node.id("noad") -- attr nucleus sub sup + +local math_accent = node.id("accent") -- attr nucleus sub sup accent +local math_radical = node.id("radical") -- attr nucleus sub sup left degree +local math_fraction = node.id("fraction") -- attr nucleus sub sup left right + +local math_box = node.id("sub_box") -- attr list +local math_sub = node.id("sub_mlist") -- attr list +local math_char = node.id("math_char") -- attr fam char +local math_text_char = node.id("math_text_char") -- attr fam char +local math_delim = node.id("delim") -- attr small_fam small_char large_fam large_char +local math_style = node.id("style") -- attr style +local math_choice = node.id("choice") -- attr display text script scriptscript +local math_fence = node.id("fence") -- attr subtype + +local simple_noads = table.tohash { + math_noad, +} + +local all_noads = { + math_noad, + math_box, math_sub, + math_char, math_text_char, math_delim, math_style, + math_accent, math_radical, math_fraction, math_choice, math_fence, +} + +noads.processors = noads.processors or { } + +local function process(start,what,n) + if n then n = n + 1 else n = 0 end + while start do + if trace_processing then + logs.report("math","%s%s",rep(" ",n or 0),tostring(start)) + end + local id = start.id + local proc = what[id] + if proc then + proc(start,what,n) + elseif id == math_char or id == math_text_char or id == math_delim then + break + elseif id == math_style then + -- has a next + elseif id == math_noad then + local noad = start.nucleus if noad then process(noad,what,n) end -- list + noad = start.sup if noad then process(noad,what,n) end -- list + noad = start.sub if noad then process(noad,what,n) end -- list + elseif id == math_box or id == math_sub then + local noad = start.list if noad then process(noad,what,n) end -- list + elseif id == math_fraction then + local noad = start.num if noad then process(noad,what,n) end -- list + noad = start.denom if noad then process(noad,what,n) end -- list + noad = start.left if noad then process(noad,what,n) end -- delimiter + noad = start.right if noad then process(noad,what,n) end -- delimiter + elseif id == math_choice then + local noad = start.display if noad then process(noad,what,n) end -- list + noad = start.text if noad then process(noad,what,n) end -- list + noad = start.script if noad then process(noad,what,n) end -- list + noad = start.scriptscript if noad then process(noad,what,n) end -- list + elseif id == math_fence then + local noad = start.delim if noad then process(noad,what,n) end -- delimiter + elseif id == math_radical then + local noad = start.nucleus if noad then process(noad,what,n) end -- list + noad = start.sup if noad then process(noad,what,n) end -- list + noad = start.sub if noad then process(noad,what,n) end -- list + noad = start.left if noad then process(noad,what,n) end -- delimiter + noad = start.degree if noad then process(noad,what,n) end -- list + elseif id == math_accent then + local noad = start.nucleus if noad then process(noad,what,n) end -- list + noad = start.sup if noad then process(noad,what,n) end -- list + noad = start.sub if noad then process(noad,what,n) end -- list + noad = start.accent if noad then process(noad,what,n) end -- list + noad = start.bot_accent if noad then process(noad,what,n) end -- list + else + -- glue, penalty, etc + end + start = start.next + end +end + +noads.process = process + +-- character remapping + +local mathalphabet = attributes.private("mathalphabet") +local mathgreek = attributes.private("mathgreek") + +noads.processors.relocate = { } + +local function report_remap(tag,id,old,new,extra) + logs.report("math","remapping %s in font %s from U+%04X (%s) to U+%04X (%s)%s",tag,id,old,utfchar(old),new,utfchar(new),extra or "") +end + +local remap_alphabets = mathematics.remap_alphabets +local fcs = fonts.color.set + +-- we can have a global famdata == fonts.famdata and chrdata == fonts.chrdata + +--~ This does not work out well, as there are no fallbacks. Ok, we could +--~ define a poor mans simplify mechanism. +--~ +--~ local function checked(pointer) +--~ local char = pointer.char +--~ local fam = pointer.fam +--~ local id = font_of_family(fam) +--~ local tfmdata = fontdata[id] +--~ local tc = tfmdata and tfmdata.characters +--~ if not tc[char] then +--~ local specials = characters.data[char].specials +--~ if specials and (specials[1] == "char" or specials[1] == "font") then +--~ newchar = specials[#specials] +--~ if trace_remapping then +--~ report_remap("fallback",id,char,newchar) +--~ end +--~ if trace_analyzing then +--~ fcs(pointer,"font:isol") +--~ end +--~ pointer.char = newchar +--~ return true +--~ end +--~ end +--~ end + +noads.processors.relocate[math_char] = function(pointer) + local g = has_attribute(pointer,mathgreek) or 0 + local a = has_attribute(pointer,mathalphabet) or 0 + if a > 0 or g > 0 then + if a > 0 then + set_attribute(pointer,mathgreek,0) + end + if g > 0 then + set_attribute(pointer,mathalphabet,0) + end + local char = pointer.char + local newchar = remap_alphabets(char,a,g) + if newchar then + local fam = pointer.fam + local id = font_of_family(fam) + local tfmdata = fontdata[id] + if tfmdata and tfmdata.characters[newchar] then -- we could probably speed this up + if trace_remapping then + report_remap("char",id,char,newchar) + end + if trace_analyzing then + fcs(pointer,"font:isol") + end + pointer.char = newchar + return true + elseif trace_remapping then + report_remap("char",id,char,newchar," fails") + end + else + -- return checked(pointer) + end + else + -- return checked(pointer) + end + if trace_analyzing then + fcs(pointer,"font:medi") + end +end + +noads.processors.relocate[math_text_char] = function(pointer) + if trace_analyzing then + fcs(pointer,"font:init") + end +end + +noads.processors.relocate[math_delim] = function(pointer) + if trace_analyzing then + fcs(pointer,"font:fina") + end +end + +function noads.relocate_characters(head,style,penalties) + process(head,noads.processors.relocate) + return true +end + +-- some resize options (this works ok because the content is +-- empty and no larger next will be forced) +-- +-- beware: we don't use \delcode but \Udelcode and as such have +-- no large_fam; also, we need to check for subtype and/or +-- small_fam not being 0 because \. sits in 0,0 by default +-- +-- todo: just replace the character by an ord noad +-- and remove the right delimiter as well + +local mathsize = attributes.private("mathsize") + +noads.processors.resize = { } + +noads.processors.resize[math_fence] = function(pointer) + if pointer.subtype == 1 then -- left + local a = has_attribute(pointer,mathsize) + if a and a > 0 then + set_attribute(pointer,mathsize,0) + local d = pointer.delim + local df = d.small_fam + local id = font_of_family(df) + if id > 0 then + local ch = d.small_char + d.small_char = mathematics.big(fontdata[id],ch,a) + end + end + end +end + +function noads.resize_characters(head,style,penalties) + process(head,noads.processors.resize) + return true +end + +-- respacing + +local mathpunctuation = attributes.private("mathpunctuation") + +noads.processors.respace = { } + +local chardata = characters.data + +-- only [nd,ll,ul][po][nd,ll,ul] + +noads.processors.respace[math_noad] = function(pointer) + if pointer.subtype == noad_ord then + local a = has_attribute(pointer,mathpunctuation) + if a and a > 0 then + set_attribute(pointer,mathpunctuation,0) + local current_nucleus = pointer.nucleus + if current_nucleus.id == math_char then + local current_char = current_nucleus.char + local fc = chardata[current_char] + fc = fc and fc.category + if fc == "nd" or fc == "ll" or fc == "lu" then + local next_noad = pointer.next + if next_noad and next_noad.id == math_noad and next_noad.subtype == noad_punct then + local next_nucleus = next_noad.nucleus + if next_nucleus.id == math_char then + local next_char = next_nucleus.char + local nc = chardata[next_char] + nc = nc and nc.category + if nc == "po" then + local last_noad = next_noad.next + if last_noad and last_noad.id == math_noad and last_noad.subtype == noad_ord then + local last_nucleus = last_noad.nucleus + if last_nucleus.id == math_char then + local last_char = last_nucleus.char + local lc = chardata[last_char] + lc = lc and lc.category + if lc == "nd" or lc == "ll" or lc == "lu" then + local ord = node.new(math_noad) -- todo: pool + ord.subtype, ord.nucleus, ord.sub, ord.sup, ord.attr = noad_ord, next_noad.nucleus, next_noad.sub, next_noad.sup, next_noad.attr + -- next_noad.nucleus, next_noad.sub, next_noad.sup, next_noad.attr = nil, nil, nil, nil + next_noad.nucleus, next_noad.sub, next_noad.sup = nil, nil, nil -- else crash with attributes ref count + --~ next_noad.attr = nil + ord.next = last_noad + pointer.next = ord + node.free(next_noad) + end + end + end + end + end + end + end + end + end + end +end + + +function noads.respace_characters(head,style,penalties) + noads.process(head,noads.processors.respace) + return true +end + +-- the normal builder + +function noads.mlist_to_hlist(head,style,penalties) + return mlist_to_hlist(head,style,penalties), true +end + +tasks.new ( + "math", + { + "before", + "normalizers", + "builders", + "after", + } +) + +local actions = tasks.actions("math",2) -- head, style, penalties + +local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming + +function nodes.processors.mlist_to_hlist(head,style,penalties) + starttiming(noads) + local head, done = actions(head,style,penalties) + stoptiming(noads) + return head, done +end + +callbacks.register('mlist_to_hlist',nodes.processors.mlist_to_hlist,"preprocessing math list") + +-- tracing + +statistics.register("math processing time", function() + return statistics.elapsedseconds(noads) +end) diff --git a/tex/context/base/math-pln.mkii b/tex/context/base/math-pln.mkii new file mode 100644 index 000000000..f2f3ff183 --- /dev/null +++ b/tex/context/base/math-pln.mkii @@ -0,0 +1,360 @@ +%D \module +%D [ file=math-pln, +%D version=2001.11.16, +%D title=\CONTEXT\ Math Macros, +%D subtitle=Plain Helpers, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +% \points should become \bodyfontsize + +%D This is a temporary module, some of this code will move to +%D the other math modules. + +\writestatus{loading}{ConTeXt Math Macros / Plain Helpers} + +\unprotect + +\ifx\displ@y\undefined \let\displ@y\relax\fi + +\newbox\rootbox + +\def\root#1\of + {\setbox\rootbox\hbox{$\mathsurround\zeropoint\scriptscriptstyle{#1}$}% + \mathpalette\r@@t} + +\def\r@@t#1#2% will be overloaded + {\setbox\zerocount\hbox{$\mathsurround\zeropoint#1\sqrt{#2}$}\dimen@\ht\zerocount + \advance\dimen@-\dp\zerocount + \mkern5mu\raise.6\dimen@\copy\rootbox + \mkern-10mu\box\zerocount} + +\def\mathhexbox#1#2#3% + {\leavevmode + \hbox{$\mathsurround\zeropoint\mathchar"#1#2#3$}} + +\def\oalign#1% + {\leavevmode + \vtop + {\baselineskip\zeroskip \lineskip.25ex% + \ialign{##\crcr#1\crcr}}} + +\def\o@lign + {\lineskiplimit\zeropoint \oalign} + +\def\ooalign % chars over each other + {\lineskiplimit-\maxdimen + \oalign} + +\def\sh@ft#1% kern by #1 times the current slant + {\dimen@#1% + \kern\expandafter\withoutpt\the\slantperpoint + \dimen@} + +\def\dots + {\relax\ifmmode\ldots\else$\mathsurround\zeropoint\ldots\,$\fi} + +\def\hrulefill + {\leaders\hrule\hfill} + +\def\dotfill + {\cleaders\hbox{$\mathsurround\zeropoint \mkern1.5mu.\mkern1.5mu$}\hfill} + +\def\rightarrowfill + {$\mathsurround\zeropoint\smash-\mkern-7mu% + \cleaders\hbox{$\mkern-2mu\smash-\mkern-2mu$}\hfill + \mkern-7mu\mathord\rightarrow$} + +\def\leftarrowfill + {$\mathsurround\zeropoint\mathord\leftarrow\mkern-7mu% + \cleaders\hbox{$\mkern-2mu\smash-\mkern-2mu$}\hfill + \mkern-7mu\smash-$} + +% must go to math-tex + +\ifx\braceld\undefined + % mkii values + \mathchardef\braceld="37A + \mathchardef\bracerd="37B + \mathchardef\bracelu="37C + \mathchardef\braceru="37D +\fi + +\def\downbracefill + {$\mathsurround\zeropoint\setbox\zerocount\hbox{$\braceld$}% + \braceld\leaders\vrule\!!height\ht\zerocount\!!depth\zeropoint\hfill\braceru + \bracelu\leaders\vrule\!!height\ht\zerocount\!!depth\zeropoint\hfill\bracerd$} + +\def\upbracefill + {$\mathsurround\zeropoint\setbox\zerocount\hbox{$\braceld$}% + \bracelu\leaders\vrule\!!height\ht\zerocount\!!depth\zeropoint\hfill\bracerd + \braceld\leaders\vrule\!!height\ht\zerocount\!!depth\zeropoint\hfill\braceru$} + +% hm, shouldn't that be \kern3\bodyfontsize + +\def\overbrace#1% + {\mathop{\vbox{\mathsurround\zeropoint\ialign{##\crcr\noalign{\kern3\points} + \downbracefill\crcr\noalign{\kern3\points\nointerlineskip} + $\hfil\displaystyle{#1}\hfil$\crcr}}}\limits} + +\def\underbrace#1% + {\mathop{\vtop{\mathsurround\zeropoint\ialign{##\crcr + $\hfil\displaystyle{#1}\hfil$\crcr\noalign{\kern3\points\nointerlineskip} + \upbracefill\crcr\noalign{\kern3\points}}}}\limits} + +\let\sp=^ % will become obsolete +\let\sb=_ % will become obsolete + +\ifx\,\undefined \def\,{\mskip \thinmuskip } \fi +\ifx\>\undefined \def\>{\mskip \medmuskip } \fi +\ifx\;\undefined \def\;{\mskip \thickmuskip} \fi +\ifx\!\undefined \def\!{\mskip-\thinmuskip } \fi +\ifx\*\undefined \def\*{\discretionary{\thinspace\the\textfont2\char2}{}{}} \fi + +% {\catcode`\'=\active \gdef'{^\bgroup\prim@s}} + +\def\activemathquote{^\bgroup\prim@s} + +\def\prim@s + {\prime\futurelet\next\pr@m@s} + +\def\pr@m@s + {\ifx'\next + \@EA\pr@@@s + \else\ifx^\next + \@EAEAEA\pr@@@t + \else + \@EAEAEA\egroup + \fi\fi} + +\def\pr@@@s#1% + {\prim@s} + +\def\pr@@@t#1#2% + {#2\egroup} + +% {\catcode`\_=\active \global\let_=\_} % _ in math is either subscript or \_ + +\let\activemathunderscore\_ + +\def\relbar {\mathrel{\smash-}} % - has the same height as + +\def\Relbar {\mathrel=} + +\def\Longrightarrow {\Relbar\joinrel\Rightarrow} +\def\longrightarrow {\relbar\joinrel\rightarrow} +\def\longleftarrow {\leftarrow\joinrel\relbar} +\def\Longleftarrow {\Leftarrow\joinrel\Relbar} +\def\longmapsto {\mapstochar\longrightarrow} +\def\longleftrightarrow{\leftarrow\joinrel\rightarrow} +\def\Longleftrightarrow{\Leftarrow\joinrel\Rightarrow} + +\def\overrightarrow#1% + {\vbox{\mathsurround\zeropoint\ialign{##\crcr + \rightarrowfill\crcr\noalign{\kern-\onepoint\nointerlineskip} + $\hfil\displaystyle{#1}\hfil$\crcr}}} + +\def\overleftarrow#1% + {\vbox{\mathsurround\zeropoint\ialign{##\crcr + \leftarrowfill\crcr\noalign{\kern-\onepoint\nointerlineskip} + $\hfil\displaystyle{#1}\hfil$\crcr}}} + +\def\skew#1#2#3% + {{\muskip\zerocount#1mu\divide\muskip\zerocount\plustwo \mkern\muskip\zerocount + #2{\mkern-\muskip\zerocount{#3}\mkern\muskip\zerocount}\mkern-\muskip\zerocount}{}} + +\def\choose{\atopwithdelims()} +\def\brack {\atopwithdelims[]} +\def\brace {\atopwithdelims\{\}} + +\def\mathpalette#1#2% + {\mathchoice + {#1\displaystyle {#2}}% + {#1\textstyle {#2}}% + {#1\scriptstyle {#2}}% + {#1\scriptscriptstyle{#2}}} + +\def\cong + {\mathrel{\mathpalette\@vereq\sim}} % congruence sign + +\def\@vereq#1#2% + {\lower.5\points\vbox{\lineskiplimit\maxdimen\lineskip-.5\points + \ialign{$\mathsurround\zeropoint#1\hfil##\hfil$\crcr#2\crcr=\crcr}}} + +\def\notin% can be mkiv'd + {\mathrel{\mathpalette\c@ncel\in}} + +\def\c@ncel#1#2% + {\mathsurround\zeropoint\ooalign{$\hfil#1\mkern1mu/\hfil$\crcr$#1#2$}} + +\def\rightleftharpoons% + {\mathrel{\mathpalette\rlh@{}}} + +\def\rlh@#1% + {\vcenter + {\mathsurround\zeropoint + \hbox + {\ooalign + {\raise2pt\hbox{$#1\rightharpoonup$}\crcr + $#1\leftharpoondown$}}}} + +\def\buildrel#1\over#2% + {\mathrel{\mathop{\kern\zeropoint#2}\limits^{#1}}} + +\def\doteq + {\buildrel\textstyle.\over=} + +\ifx\mfunction\undefined \def\mfunction#1{\mathbin{\rm#1}} \fi + +\def\bmod + {\nonscript + \mskip-\medmuskip + \mkern5mu + \mfunction{mod}% + \penalty900 + \mkern5mu + \nonscript + \mskip-\medmuskip} + +\def\pmod#1% + {\allowbreak + \mkern18mu + (\mfunction{mod}\,\,#1)} + +\def\cases#1% + {\left\{% + \,% + \vcenter + {\normalbaselines + \mathsurround\zeropoint + \ialign{$##\hfil$&\quad##\hfil\crcr#1\crcr}}% + \right.} + +\def\matrix#1% + {\null + \,% + \vcenter + {\normalbaselines\mathsurround\zeropoint + \ialign{\hfil$##$\hfil&&\quad\hfil$##$\hfil\crcr + \mathstrut\crcr\noalign{\kern-\baselineskip} + #1\crcr\mathstrut\crcr\noalign{\kern-\baselineskip}}}% + \,} + +\def\pmatrix#1% + {\left(\matrix{#1}\right)} + +\newdimen\mathparentwd + +% \setbox0=\hbox{\tenex B} \mathparentwd=\wd0 % width of the big left ( + +\def\bordermatrix#1% + {\begingroup + \mathsurround\zeropoint + \setbox\zerocount\vbox + {\def\cr{\crcr\noalign{\kern2\points\global\let\cr\endline}}% + \ialign{$##$\hfil\kern2\points\kern\mathparentwd&\thinspace\hfil$##$\hfil + &&\quad\hfil$##$\hfil\crcr + \omit\strut\hfil\crcr\noalign{\kern-\baselineskip}% + #1\crcr\omit\strut\cr}}% + \setbox\plustwo\vbox + {\unvcopy\zerocount\global\setbox\plusone\lastbox}% + \setbox\plustwo\hbox + {\unhbox\plusone\unskip\global\setbox\plusone\lastbox}% + \setbox\plustwo\hbox + {$\kern\wd\plusone\kern-\mathparentwd\left(\kern-\wd\plusone + \global\setbox\plusone\vbox{\box\plusone\kern2\points}% + \vcenter{\kern-\ht\plusone\unvbox\zerocount\kern-\baselineskip}\,\right)$}% + \null + \;% + \vbox{\kern\ht\plusone\box\plustwo}% + \endgroup} + +% \def\openup{\afterassignment\@penup\dimen@=} +% +% \def\@penup{\advance\lineskip\dimen@ +% \advance\baselineskip\dimen@ +% \advance\lineskiplimit\dimen@} + +\def\openup + {\afterassignment\doopenup\scratchdimen=} + +\def\doopenup + {\advance\lineskip \scratchdimen + \advance\baselineskip \scratchdimen + \advance\lineskiplimit\scratchdimen} + +% \def\jot{.25\bodyfontsize} % plain tex: 3 pt (todo: better name and configurable) + +\def\displayopenupvalue{.25\bodyfontsize} + +\def\eqalign#1% + {\null + \,% + \vcenter + {\openup\displayopenupvalue % was \openup\jot + \mathsurround\zeropoint + \ialign + {\strut\hfil$\displaystyle{##}$&$\displaystyle{{}##}$\hfil\crcr + #1\crcr}}% + \,} + +\def\@lign % restore inside \displ@y + {\tabskip\zeroskip + \everycr{}} + +\def\displaylines#1% + {\displ@y + \tabskip\zeroskip + \halign + {\hbox to \displaywidth{$\@lign\hfil\displaystyle##\hfil$}\crcr + #1\crcr}} + +\def\eqalignno#1% + {\displ@y + \tabskip\centering + \halign to \displaywidth + {\hfil$\@lign\displaystyle{##}$\tabskip\zeroskip + &$\@lign\displaystyle{{}##}$\hfil\tabskip\centering + &\llap{$\@lign##$}\tabskip\zeroskip\crcr + #1\crcr}} + +\def\leqalignno#1% + {\displ@y + \tabskip\centering + \halign to \displaywidth + {\hfil$\@lign\displaystyle{##}$\tabskip\zeroskip + &$\@lign\displaystyle{{}##}$\hfil\tabskip\centering + &\kern-\displaywidth\rlap{$\@lign##$}\tabskip\displaywidth\crcr + #1\crcr}} + +% temporary here + +% \startcatcodetable \mthcatcodes +% \setcatcodetable\ctxcatcodes +% \catcode`\_ = 13 +% \catcode`\' = 13 +% \stopcatcodetable +% +% \letcatcodecommand \mthcatcodes `\_ \activemathunderscore +% \letcatcodecommand \mthcatcodes `\' \activemathquote + +% \appendtoks \setcatcodetable\mthcatcodes \to \everymath : spoils xml + +% tricky, but some day we will reimplement math + +\bgroup + \catcode`\_ = 13 + \catcode`\' = 13 + \doglobal\appendtoks + \let_\activemathunderscore + \let'\activemathquote + \to \everymathematics +\egroup + +% so far + +\protect \endinput diff --git a/tex/context/base/math-pln.mkiv b/tex/context/base/math-pln.mkiv new file mode 100644 index 000000000..ab584f10a --- /dev/null +++ b/tex/context/base/math-pln.mkiv @@ -0,0 +1,298 @@ +%D \module +%D [ file=math-pln, +%D version=2001.11.16, +%D title=\CONTEXT\ Math Macros, +%D subtitle=Plain Helpers, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=\PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D This is a temporary module, some of this code will move to +%D the other math modules. Much is copied from Plain \TEX. + +% \points should become \bodyfontsize + +\writestatus{loading}{ConTeXt Math Macros / Plain Helpers} + +\unprotect + +\ifx\displ@y\undefined \let\displ@y\relax\fi + +\def\oalign#1% + {\leavevmode + \vtop + {\baselineskip\zeroskip \lineskip.25ex% + \ialign{##\crcr#1\crcr}}} + +\def\o@lign + {\lineskiplimit\zeropoint \oalign} + +\def\ooalign % chars over each other + {\lineskiplimit-\maxdimen + \oalign} + +\def\sh@ft#1% kern by #1 times the current slant + {\dimen@#1% + \kern\expandafter\withoutpt\the\slantperpoint + \dimen@} + +\def\dots + {\relax\ifmmode\ldots\else$\mathsurround\zeropoint\ldots\,$\fi} + +\def\hrulefill + {\leaders\hrule\hfill} + +\def\dotfill + {\cleaders\hbox{$\mathsurround\zeropoint \mkern1.5mu.\mkern1.5mu$}\hfill} + +\def\rightarrowfill + {$\mathsurround\zeropoint\smash-\mkern-7mu% + \cleaders\hbox{$\mkern-2mu\smash-\mkern-2mu$}\hfill + \mkern-7mu\mathord\rightarrow$} + +\def\leftarrowfill + {$\mathsurround\zeropoint\mathord\leftarrow\mkern-7mu% + \cleaders\hbox{$\mkern-2mu\smash-\mkern-2mu$}\hfill + \mkern-7mu\smash-$} + +\let\sp=^ % will become obsolete +\let\sb=_ % will become obsolete + +\ifx\,\undefined \def\,{\mskip \thinmuskip } \fi +\ifx\>\undefined \def\>{\mskip \medmuskip } \fi +\ifx\;\undefined \def\;{\mskip \thickmuskip} \fi +\ifx\!\undefined \def\!{\mskip-\thinmuskip } \fi +\ifx\*\undefined \def\*{\discretionary{\thinspace\the\textfont2\char2}{}{}} \fi + +% {\catcode`\'=\active \gdef'{^\bgroup\prim@s}} + +\def\activemathquote{^\bgroup\prim@s} + +\def\prim@s + {\prime\futurelet\next\pr@m@s} + +\def\pr@m@s + {\ifx'\next + \@EA\pr@@@s + \else\ifx^\next + \@EAEAEA\pr@@@t + \else + \@EAEAEA\egroup + \fi\fi} + +\def\pr@@@s#1% + {\prim@s} + +\def\pr@@@t#1#2% + {#2\egroup} + +% {\catcode`\_=\active \global\let_=\_} % _ in math is either subscript or \_ + +\let\activemathunderscore\_ + +\def\relbar {\mathrel{\smash-}} % - has the same height as + +\def\Relbar {\mathrel=} + +\def\Longrightarrow {\Relbar\joinrel\Rightarrow} % beware, this overloades cambria native +\def\longrightarrow {\relbar\joinrel\rightarrow} +\def\longleftarrow {\leftarrow\joinrel\relbar} +\def\Longleftarrow {\Leftarrow\joinrel\Relbar} +\def\longmapsto {\mapstochar\longrightarrow} +\def\longleftrightarrow{\leftarrow\joinrel\rightarrow} +\def\Longleftrightarrow{\Leftarrow\joinrel\Rightarrow} + +\def\choose{\atopwithdelims()} +\def\brack {\atopwithdelims[]} +\def\brace {\atopwithdelims\{\}} + +\def\mathpalette#1#2% + {\mathchoice + {#1\displaystyle {#2}}% + {#1\textstyle {#2}}% + {#1\scriptstyle {#2}}% + {#1\scriptscriptstyle{#2}}} + +\def\cong + {\mathrel{\mathpalette\@vereq\sim}} % congruence sign + +\def\@vereq#1#2% + {\lower.5\points\vbox{\lineskiplimit\maxdimen\lineskip-.5\points + \ialign{$\mathsurround\zeropoint#1\hfil##\hfil$\crcr#2\crcr=\crcr}}} + +\def\notin + {\mathrel{\mathpalette\c@ncel\in}} + +\def\c@ncel#1#2% + {\mathsurround\zeropoint\ooalign{$\hfil#1\mkern1mu/\hfil$\crcr$#1#2$}} + +\def\rightleftharpoons + {\mathrel{\mathpalette\rlh@{}}} + +\def\rlh@#1% + {\vcenter + {\mathsurround\zeropoint + \hbox + {\ooalign + {\raise2pt\hbox{$#1\rightharpoonup$}\crcr + $#1\leftharpoondown$}}}} + +\def\buildrel#1\over#2% + {\mathrel{\mathop{\kern\zeropoint#2}\limits^{#1}}} + +\def\doteq + {\buildrel\textstyle.\over=} + +\ifx\mfunction\undefined \def\mfunction#1{\mathbin{\rm#1}} \fi + +\def\bmod + {\nonscript + \mskip-\medmuskip + \mkern5mu + \mfunction{mod}% + \penalty900 + \mkern5mu + \nonscript + \mskip-\medmuskip} + +\def\pmod#1% + {\allowbreak + \mkern18mu + (\mfunction{mod}\,\,#1)} + +\def\cases#1% + {\left\{% + \,% + \vcenter + {\normalbaselines + \mathsurround\zeropoint + \ialign{$##\hfil$&\quad##\hfil\crcr#1\crcr}}% + \right.} + +\def\matrix#1% + {\null + \,% + \vcenter + {\normalbaselines\mathsurround\zeropoint + \ialign{\hfil$##$\hfil&&\quad\hfil$##$\hfil\crcr + \mathstrut\crcr\noalign{\kern-\baselineskip} + #1\crcr\mathstrut\crcr\noalign{\kern-\baselineskip}}}% + \,} + +\def\pmatrix#1% + {\left(\matrix{#1}\right)} + +\newdimen\mathparentwd + +% \setbox0=\hbox{\tenex B} \mathparentwd=\wd0 % width of the big left ( + +\def\bordermatrix#1% + {\begingroup + \mathsurround\zeropoint + \setbox\zerocount\vbox + {\def\cr{\crcr\noalign{\kern2\points\global\let\cr\endline}}% + \ialign{$##$\hfil\kern2\points\kern\mathparentwd&\thinspace\hfil$##$\hfil + &&\quad\hfil$##$\hfil\crcr + \omit\strut\hfil\crcr\noalign{\kern-\baselineskip}% + #1\crcr\omit\strut\cr}}% + \setbox\plustwo\vbox + {\unvcopy\zerocount\global\setbox\plusone\lastbox}% + \setbox\plustwo\hbox + {\unhbox\plusone\unskip\global\setbox\plusone\lastbox}% + \setbox\plustwo\hbox + {$\kern\wd\plusone\kern-\mathparentwd\left(\kern-\wd\plusone + \global\setbox\plusone\vbox{\box\plusone\kern2\points}% + \vcenter{\kern-\ht\plusone\unvbox\zerocount\kern-\baselineskip}\,\right)$}% + \null + \;% + \vbox{\kern\ht\plusone\box\plustwo}% + \endgroup} + +% \def\openup{\afterassignment\@penup\dimen@=} +% +% \def\@penup{\advance\lineskip\dimen@ +% \advance\baselineskip\dimen@ +% \advance\lineskiplimit\dimen@} + +\def\openup + {\afterassignment\doopenup\scratchdimen=} + +\def\doopenup + {\advance\lineskip \scratchdimen + \advance\baselineskip \scratchdimen + \advance\lineskiplimit\scratchdimen} + +% \def\jot{.25\bodyfontsize} % plain tex: 3 pt (todo: better name and configurable) + +\def\displayopenupvalue{.25\bodyfontsize} + +\def\eqalign#1% + {\null + \,% + \vcenter + {\openup\displayopenupvalue % was \openup\jot + \mathsurround\zeropoint + \ialign + {\strut\hfil$\displaystyle{##}$&$\displaystyle{{}##}$\hfil\crcr + #1\crcr}}% + \,} + +\def\@lign % restore inside \displ@y + {\tabskip\zeroskip + \everycr{}} + +\def\displaylines#1% + {\displ@y + \tabskip\zeroskip + \halign + {\hbox to \displaywidth{$\@lign\hfil\displaystyle##\hfil$}\crcr + #1\crcr}} + +\def\eqalignno#1% + {\displ@y + \tabskip\centering + \halign to \displaywidth + {\hfil$\@lign\displaystyle{##}$\tabskip\zeroskip + &$\@lign\displaystyle{{}##}$\hfil\tabskip\centering + &\llap{$\@lign##$}\tabskip\zeroskip\crcr + #1\crcr}} + +\def\leqalignno#1% + {\displ@y + \tabskip\centering + \halign to \displaywidth + {\hfil$\@lign\displaystyle{##}$\tabskip\zeroskip + &$\@lign\displaystyle{{}##}$\hfil\tabskip\centering + &\kern-\displaywidth\rlap{$\@lign##$}\tabskip\displaywidth\crcr + #1\crcr}} + +% temporary here (weird code) + +% \startcatcodetable \mthcatcodes +% \setcatcodetable\ctxcatcodes +% \catcode`\_ = 13 +% \catcode`\' = 13 +% \stopcatcodetable +% +% \letcatcodecommand \mthcatcodes `\_ \activemathunderscore +% \letcatcodecommand \mthcatcodes `\' \activemathquote + +% \appendtoks \setcatcodetable\mthcatcodes \to \everymath : spoils xml + +% tricky, but some day we will reimplement math + +\bgroup + \catcode`\_ = 13 + \catcode`\' = 13 + \doglobal\appendtoks + \let_\activemathunderscore + \let'\activemathquote + \to \everymathematics +\egroup + +% so far + +\protect \endinput diff --git a/tex/context/base/math-run.mkii b/tex/context/base/math-run.mkii new file mode 100644 index 000000000..6bc4bf4d5 --- /dev/null +++ b/tex/context/base/math-run.mkii @@ -0,0 +1,105 @@ +%D \module +%D [ file=math-run, +%D version=2001.23.04, +%D title=\CONTEXT\ Math Macros, +%D subtitle=Runtime Macros, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=Hans Hagen \& Ton Otten] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Math Macros / Runtime Macros} + +\unprotect + +\ifx\showmathmodern\undefined \global\chardef\showmathmodern\zerocount \fi + +\gdef\showmathcharacters% nearly \showcharacters + {\par + \bgroup + \ifcase\showmathmodern\or\ifx\modern\undefined\chardef\showmathmodern\zerocount\fi\fi + \setuptextrules[\c!bodyfont=,\c!style=] + \starttextrule{math characters -- \currentmathcollection} + \whitespace + \dontcomplain + \forgetall + \def\startmathcollection[##1]{} + \let\stopmathcollection\relax + \dimen0\zeropoint + \dimen2\zeropoint + \def\definemathsymbol {\dosixtupleempty\dodefinemathsymbol} + \def\definemathcharacter{\dosixtupleempty\dodefinemathcharacter} + \def\definemathcommand {\dotripleempty \dodefinemathcommand} + %\newcounter\mathcolor + \def\dodefinemathsymbol[##1][##2][##3][##4][##5][##6]% + {%\doifcolorelse{math \purefamilyhex{##3}}{} + % {\increment\mathcolor + % \definecolor[math \purefamilyhex{##3}][\mathcolor]}% + \setbox0\hbox spread 1em{\mathematics{\getvalue{##1}{}{}{}}}% + \ifdim\wd0>\dimen0 \dimen0=\wd0 \fi + \setbox2\hbox spread 1em{\hbox to 1em{\tttf\purefamilyhex{##3}\hss}\box0 ##1}% + \ifdim\wd2>\dimen2 \dimen2=\wd2 \fi} + \def\dodefinemathcharacter[##1][##2][##3][##4][##5][##6]{} + \def\dodefinemathcommand [##1][##2][##3]##4{} + \readsysfile{\f!mathprefix tex.mkii}\!!doneatrue\!!doneafalse + \readsysfile{\f!mathprefix ams.mkii}\!!donebtrue\!!donebfalse + \if!!donea + \if!!doneb + \edef\encwidth{\the\dimen0} + \dimen0=\hsize + \advance\dimen0 2em + \advance\dimen2 2em + \ifcase\showmathmodern\or\advance\dimen2 4em\fi + \divide \dimen0 by \dimen2 \advance\dimen0 1sp + \edef\enccols{\number\dimen0} + \startcolumns[\c!n=\enccols,\c!distance=2em] + \def\dodefinemathsymbol[##1][##2][##3][##4][##5][##6]% + {%\localcolortrue + %\color + % [math \purefamilyhex{##3}] + {\hbox + {\ifcase\showmathmodern\or + \hbox to \encwidth{\modern\let\mathcollection\nomathcollection\mathematics{\getvalue{##1}{}{}{}}\hss}% + \fi + \hbox to \encwidth{\mathematics{\getvalue{##1}{}{}{}}\hss}% + \hbox to 1em{\tttf\purefamilyhex{##3}\hss}##1}\par}} + \readsysfile{\f!mathprefix tex.mkii}\donothing\donothing + \readsysfile{\f!mathprefix ams.mkii}\donothing\donothing + \stopcolumns + \else + \par \tttf no chars in \f!mathprefix ams\par + \fi + \else + \par \tttf no chars in \f!mathprefix tex\par + \fi + \stoptextrule + \egroup} + +% \definecolor[math \purefamilyhex{mr}] [darkred] +% \definecolor[math \purefamilyhex{mi}] [darkgreen] +% \definecolor[math \purefamilyhex{sy}] [darkblue] +% \definecolor[math \purefamilyhex{ex}] [darkmagenta] +% \definecolor[math \purefamilyhex{nn}] [darkyellow] +% \definecolor[math \purefamilyhex{ma}] [lightred] +% \definecolor[math \purefamilyhex{mb}] [lightgreen] +% \definecolor[math \purefamilyhex{mc}] [lightblue] +% \definecolor[math \purefamilyhex{md}] [lightmagenta] + +\gdef\showmathtoken#1% + {\starttabulate[|lT|lT|lT|l|] + \NC token \NC #1 \NC \NR + \NC collection \NC \ifcsname\@mt@\mathcollection#1\endcsname + \mathcollection + \else\ifcsname\@mt@\nomathcollection#1\endcsname + \nomathcollection + \else + ?% + \fi\fi \NC \NR + \NC visualization \NC \mathematics{\getvalue{#1}} \NC \NR + \NC definition \NC \tttf \@EA\defconvertedcommand\@EA\ascii\csname\@mt@\mathcollection#1\endcsname \ascii \NC \NR + \stoptabulate} + +\protect \endinput diff --git a/tex/context/base/math-scr.mkiv b/tex/context/base/math-scr.mkiv new file mode 100644 index 000000000..eb1db4714 --- /dev/null +++ b/tex/context/base/math-scr.mkiv @@ -0,0 +1,135 @@ +%D \module +%D [ file=math-scr, +%D version=2007.07.19, +%D title=\CONTEXT\ Math Macros, +%D subtitle=Scripts, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=\PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Math Macros / Scripts} + +\unprotect + +%D \macros +%D {super, sub} +%D +%D \TEX\ uses \type{^} and \type{_} for entering super- and +%D subscript mode. We want however a bit more control than +%D normally provided, and therefore provide \type {\super} +%D and \type{sub}. +%D +%D The grid snapping has been removed. + +\global\let\normalsuper=^ +\global\let\normalsuber=_ + +\ifdefined\supersubmode \else \newcount\supersubmode \fi +\ifdefined\newevery \else \everysupersub \EverySuperSub \fi + +\appendtoks \advance\supersubmode \plusone \to \everysupersub + +\setuptextformulas + [\c!size=\v!normal] + +\def\normalsupsub#1#2% + {^{\the\everysupersub#1}_{\the\everysupersub#2}} + +\def\super#1{^{\the\everysupersub#1}} +\def\suber#1{_{\the\everysupersub#1}} +\def\supsub#1#2{\super{#1}\suber{#2}} +\def\subsup#1#2{\suber{#1}\super{#2}} + +%D \macros +%D {enablesupersub,enablesimplesupersub} +%D +%D We can let \type {^} and \type {_} act like \type {\super} +%D and \type {\sub} by saying \type {\enablesupersub}. + +\bgroup +\catcode`\^=\@@active +\catcode`\_=\@@active +\gdef\enablesupersub + {\catcode`\^=\@@active + \def^{\ifmmode\expandafter\super\else\expandafter\normalsuper\fi}% + \catcode`\_=\@@active + \def_{\ifmmode\expandafter\suber\else\expandafter\normalsuber\fi}} +\egroup + +%D \macros +%D {restoremathstyle} +%D +%D We can pick up the current math style by calling \type +%D {\restoremathstyle}. + +\def\restoremathstyle + {\ifmmode + \ifcase\supersubmode + \textstyle + \or + \scriptstyle + \else + \scriptscriptstyle + \fi + \fi} + +%D These macros were first needed by Frits Spijker (also +%D known as Gajes) for typesetting the minus sign that is +%D keyed into scientific calculators. + +% This is the first alternative, which works okay for the +% minus, but less for the plus. +% +% \def\dodoraisedmathord#1#2#3% +% {\mathord{{#2\raise.#1ex\hbox{#2#3}}}} +% +% \def\doraisedmathord#1% +% {\mathchoice +% {\dodoraisedmathord5\tf #1}% +% {\dodoraisedmathord5\tf #1}% +% {\dodoraisedmathord4\tfx #1}% +% {\dodoraisedmathord3\tfxx#1}} +% +% \def\negative{\doraisedmathord-} +% \def\positive{\doraisedmathord+} +% +% So, now we use the monospaced signs, that we also +% define as symbol, so that they can be overloaded. + +\def\dodoraisedmathord#1#2#3% + {\mathord{{#2\raise.#1ex\hbox{#2\symbol[#3]}}}} + +\def\doraisedmathord#1% + {\mathchoice + {\dodoraisedmathord5\tf {#1}}% + {\dodoraisedmathord5\tf {#1}}% + {\dodoraisedmathord4\tx {#1}}% + {\dodoraisedmathord3\txx{#1}}} + +\def\dodonumbermathord#1#2% + {\setbox\scratchbox\hbox{0}% + \mathord{\hbox to \wd\scratchbox{\hss#1\symbol[#2]\hss}}} + +\def\donumbermathord#1% + {\mathchoice + {\dodonumbermathord\tf {#1}}% + {\dodonumbermathord\tf {#1}}% + {\dodonumbermathord\tx {#1}}% + {\dodonumbermathord\txx{#1}}} + +\definesymbol[positive] [\getglyph{Mono}{+}] +\definesymbol[negative] [\getglyph{Mono}{-}] +\definesymbol[zeroamount][\getglyph{Mono}{-}] + +\def\negative {\doraisedmathord{negative}} +\def\positive {\doraisedmathord{positive}} +\def\zeroamount{\donumbermathord{zeroamount}} + +%D How negative such a symbol looks is demonstrated in: +%D $\negative 10^{\negative 10^{\negative 10}}$. + +\protect \endinput diff --git a/tex/context/base/math-tex.mkii b/tex/context/base/math-tex.mkii new file mode 100644 index 000000000..8f34a190e --- /dev/null +++ b/tex/context/base/math-tex.mkii @@ -0,0 +1,720 @@ +%D \module +%D [ file=math-tex, +%D version=2001.04.12, +%D subtitle=Plain Specials, +%D author={Hans Hagen, Taco Hoekwater \& Aditya Mahajan}, +%D date=\currentdate, +%D copyright=\PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +% beware: in mkiv we will do it differently + +\unprotect + +\mathcode`\ ="8000 \mathcode`\_="8000 \mathcode`\'="8000 + +\definefamilysynonym [default] [calligraphic] [sy] +\definefamilysynonym [default] [oldstyle] [mi] +\definefamilysynonym [default] [blackboard] [mr] +\definefamilysynonym [default] [gothic] [mr] +\definefamilysynonym [default] [fraktur] [mr] + +\definefamilysynonym [default] [lcgreek] [mi] +\definefamilysynonym [default] [ucgreek] [mr] +\definefamilysynonym [default] [vargreek] [mi] + + +%D \macros +%D {\setupmathematics} +%D +%D Configuration for integrals. (If needed we can speed this up and make it +%D installable; no processaction is needed then). + +\chardef\intlimitcode\zerocount % 0 nolimits 1 displaylimits 2 limits + +\def\intlimits + {\ifcase\intlimitcode \nolimits \or \displaylimits \or \limits \fi} + +\def\setupmathematics + {\dosingleargument\dosetupmathematics} + +\def\dosetupmathematics[#1]% + {\getparameters[\??mo][#1] + \processaction[\@@mointegral] + [ nolimits=>\chardef\intlimitcode\zerocount, + displaylimits=>\chardef\intlimitcode\plusone, + limits=>\chardef\intlimitcode\plustwo]} + +%D \startbuffer +%D $\int_a^b f(x) dx$ and also +%D $\iint_a^b f(x,y) dxdy$, $\iiint_a^b f(x,y) dxdy$, +%D $\iiiint_a^b f(x) dx$ +%D \startformula +%D \int_a^b f(x) dx \quad +%D \iint_a^b f(x) dx \quad +%D \iiint_a^b f(x) dx \quad +%D \iiiint_a^b f(x) dx \quad +%D \stopformula +%D \stopbuffer +%D +%D Default: \getbuffer +%D +%D Displaylimits: \setupmathematics[integral=displaylimits] \getbuffer +%D +%D Limits: \setupmathematics[integral=limits] \getbuffer + +\startmathcollection [default] + +\definemathsymbol [alpha] [nothing] [lcgreek] ["0B] +\definemathsymbol [beta] [nothing] [lcgreek] ["0C] +\definemathsymbol [gamma] [nothing] [lcgreek] ["0D] +\definemathsymbol [delta] [nothing] [lcgreek] ["0E] +\definemathsymbol [epsilon] [nothing] [lcgreek] ["0F] +\definemathsymbol [zeta] [nothing] [lcgreek] ["10] +\definemathsymbol [eta] [nothing] [lcgreek] ["11] +\definemathsymbol [theta] [nothing] [lcgreek] ["12] +\definemathsymbol [iota] [nothing] [lcgreek] ["13] +\definemathsymbol [kappa] [nothing] [lcgreek] ["14] +\definemathsymbol [lambda] [nothing] [lcgreek] ["15] +\definemathsymbol [mu] [nothing] [lcgreek] ["16] +\definemathsymbol [nu] [nothing] [lcgreek] ["17] +\definemathsymbol [xi] [nothing] [lcgreek] ["18] +\definemathsymbol [omicron] [nothing] [lcgreek] ["6F] +\definemathsymbol [pi] [nothing] [lcgreek] ["19] +\definemathsymbol [rho] [nothing] [lcgreek] ["1A] +\definemathsymbol [sigma] [nothing] [lcgreek] ["1B] +\definemathsymbol [tau] [nothing] [lcgreek] ["1C] +\definemathsymbol [upsilon] [nothing] [lcgreek] ["1D] +\definemathsymbol [phi] [nothing] [lcgreek] ["1E] +\definemathsymbol [chi] [nothing] [lcgreek] ["1F] +\definemathsymbol [psi] [nothing] [lcgreek] ["20] +\definemathsymbol [omega] [nothing] [lcgreek] ["21] + +\stopmathcollection + +\startmathcollection [default] + +\definemathsymbol [varepsilon] [nothing] [vargreek] ["22] +\definemathsymbol [vartheta] [nothing] [vargreek] ["23] +\definemathsymbol [varpi] [nothing] [vargreek] ["24] +\definemathsymbol [varrho] [nothing] [vargreek] ["25] +\definemathsymbol [varsigma] [nothing] [vargreek] ["26] +\definemathsymbol [varphi] [nothing] [vargreek] ["27] + +\stopmathcollection + +\startmathcollection [default] + +\definemathsymbol [Alpha] [alpha] [ucgreek] ["41] % A +\definemathsymbol [Beta] [alpha] [ucgreek] ["42] % B +\definemathsymbol [Gamma] [alpha] [ucgreek] ["00] +\definemathsymbol [Delta] [alpha] [ucgreek] ["01] +\definemathsymbol [Epsilon] [alpha] [ucgreek] ["45] % E +\definemathsymbol [Zeta] [alpha] [ucgreek] ["5A] % Z +\definemathsymbol [Eta] [alpha] [ucgreek] ["48] % H +\definemathsymbol [Theta] [alpha] [ucgreek] ["02] +\definemathsymbol [Iota] [alpha] [ucgreek] ["49] % I +\definemathsymbol [Kappa] [alpha] [ucgreek] ["4B] % K +\definemathsymbol [Lambda] [alpha] [ucgreek] ["03] +\definemathsymbol [Mu] [alpha] [ucgreek] ["4D] % M +\definemathsymbol [Nu] [alpha] [ucgreek] ["4E] % N +\definemathsymbol [Xi] [alpha] [ucgreek] ["04] +\definemathsymbol [Omicron] [alpha] [ucgreek] ["4F] % O +\definemathsymbol [Pi] [alpha] [ucgreek] ["05] +\definemathsymbol [Rho] [alpha] [ucgreek] ["52] % R +\definemathsymbol [Sigma] [alpha] [ucgreek] ["06] +\definemathsymbol [Tau] [alpha] [ucgreek] ["54] % T +\definemathsymbol [Upsilon] [alpha] [ucgreek] ["07] +\definemathsymbol [Phi] [alpha] [ucgreek] ["08] +\definemathsymbol [Chi] [alpha] [ucgreek] ["58] % X +\definemathsymbol [Psi] [alpha] [ucgreek] ["09] +\definemathsymbol [Omega] [alpha] [ucgreek] ["0A] + +\stopmathcollection + +% The \mfunction macro is an alternative for \hbox with a +% controlable font switch. + +\startmathcollection[default] + +\definemathcommand [arccos] [nolop] {\mfunction{arccos}} +\definemathcommand [arcsin] [nolop] {\mfunction{arcsin}} +\definemathcommand [arctan] [nolop] {\mfunction{arctan}} +\definemathcommand [arg] [nolop] {\mfunction{arg}} +\definemathcommand [cosh] [nolop] {\mfunction{cosh}} +\definemathcommand [cos] [nolop] {\mfunction{cos}} +\definemathcommand [coth] [nolop] {\mfunction{coth}} +\definemathcommand [cot] [nolop] {\mfunction{cot}} +\definemathcommand [csc] [nolop] {\mfunction{csc}} +\definemathcommand [deg] [nolop] {\mfunction{deg}} +\definemathcommand [det] [limop] {\mfunction{det}} +\definemathcommand [dim] [nolop] {\mfunction{dim}} +\definemathcommand [exp] [nolop] {\mfunction{exp}} +\definemathcommand [gcd] [limop] {\mfunction{gcd}} +\definemathcommand [hom] [nolop] {\mfunction{hom}} +\definemathcommand [inf] [limop] {\mfunction{inf}} +\definemathcommand [injlim] [limop] {\mfunction{inj\,lim}} +\definemathcommand [ker] [nolop] {\mfunction{ker}} +\definemathcommand [lg] [nolop] {\mfunction{lg}} +\definemathcommand [liminf] [limop] {\mfunction{lim\,inf}} +\definemathcommand [limsup] [limop] {\mfunction{lim\,sup}} +\definemathcommand [lim] [limop] {\mfunction{lim}} +\definemathcommand [ln] [nolop] {\mfunction{ln}} +\definemathcommand [log] [nolop] {\mfunction{log}} +\definemathcommand [median] [limop] {\mfunction{median}} +\definemathcommand [max] [limop] {\mfunction{max}} +\definemathcommand [min] [limop] {\mfunction{min}} +\definemathcommand [mod] [limop] {\mfunction{mod}} +\definemathcommand [div] [limop] {\mfunction{div}} +\definemathcommand [projlim] [limop] {\mfunction{proj\,lim}} +\definemathcommand [Pr] [limop] {\mfunction{Pr}} +\definemathcommand [sec] [nolop] {\mfunction{sec}} +\definemathcommand [sinh] [nolop] {\mfunction{sinh}} +\definemathcommand [sin] [nolop] {\mfunction{sin}} +\definemathcommand [sup] [limop] {\mfunction{sup}} +\definemathcommand [tanh] [nolop] {\mfunction{tanh}} +\definemathcommand [tan] [nolop] {\mfunction{tan}} + +\stopmathcollection + +\let\normalmatharg\arg % todo: maybe automatically + +\startmathcollection[default] + +\definemathcommand [integers] {\mfunction{Z}} +\definemathcommand [reals] {\mfunction{R}} +\definemathcommand [rationals] {\mfunction{Q}} +\definemathcommand [naturalnumbers] {\mfunction{N}} +\definemathcommand [complexes] {\mfunction{C}} +\definemathcommand [primes] {\mfunction{P}} + +\stopmathcollection + +\startmathcollection[default] + +\definemathcharacter [!] [close] [mr] ["21] +\definemathcharacter [(] [open] [mr] ["28] +\definemathcharacter [)] [close] [mr] ["29] +\definemathcharacter [*] [bin] [sy] ["03] +\definemathcharacter [+] [bin] [mr] ["2B] +\definemathcharacter [,] [punct] [mi] ["3B] +\definemathcharacter [-] [bin] [sy] ["00] +\definemathcharacter [.] [ord] [mi] ["3A] +\definemathcharacter [/] [ord] [mi] ["3D] +\definemathcharacter [:] [rel] [mr] ["3A] +\definemathcharacter [;] [punct] [mr] ["3B] +\definemathcharacter [<] [rel] [mi] ["3C] +\definemathcharacter [=] [rel] [mr] ["3D] +\definemathcharacter [>] [rel] [mi] ["3E] +\definemathcharacter [?] [close] [mr] ["3F] +\definemathcharacter [91] [open] [mr] ["5B] % [ +\definemathcharacter [92] [ord] [sy] ["6E] % \ +\definemathcharacter [93] [close] [mr] ["5D] % ] +\definemathcharacter [123] [open] [sy] ["66] % { +\definemathcharacter [124] [ord] [sy] ["6A] % | +\definemathcharacter [125] [close] [sy] ["67] % } + +\stopmathcollection + +\startmathcollection[default] + +\definemathcharacter [(] [nothing] [mr] ["28] [ex] ["00] +\definemathcharacter [)] [nothing] [mr] ["29] [ex] ["01] +\definemathcharacter [91] [nothing] [mr] ["5B] [ex] ["02] % [ +\definemathcharacter [93] [nothing] [mr] ["5D] [ex] ["03] % ] +\definemathcharacter [<] [nothing] [sy] ["68] [ex] ["0A] +\definemathcharacter [>] [nothing] [sy] ["69] [ex] ["0B] +\definemathcharacter [/] [nothing] [mr] ["2F] [ex] ["0E] +\definemathcharacter [124] [nothing] [sy] ["6A] [ex] ["0C] % | +\definemathcharacter [92] [nothing] [sy] ["6E] [ex] ["0F] % \ + +\stopmathcollection + +\def\PLAINangle + {{\vbox{\ialign{$\mathsurround\zeropoint\scriptstyle##$\crcr + \not\mathrel{\mkern14mu}\crcr + \noalign{\nointerlineskip} + \mkern2.5mu\leaders\hrule height.34pt\hfill\mkern2.5mu\crcr}}}} + +\startmathcollection[default] + +\definemathsymbol [aleph] [nothing] [sy] ["40] +\definemathsymbol [imath] [nothing] [mi] ["7B] +\definemathsymbol [jmath] [nothing] [mi] ["7C] +\definemathsymbol [ell] [nothing] [mi] ["60] +\definemathsymbol [wp] [nothing] [mi] ["7D] +\definemathsymbol [Re] [nothing] [sy] ["3C] +\definemathsymbol [Im] [nothing] [sy] ["3D] +\definemathsymbol [partial] [nothing] [mi] ["40] +\definemathsymbol [infty] [nothing] [sy] ["31] +\definemathsymbol [prime] [nothing] [sy] ["30] +\definemathsymbol [emptyset] [nothing] [sy] ["3B] +\definemathsymbol [nabla] [nothing] [sy] ["72] +\definemathsymbol [top] [nothing] [sy] ["3E] +\definemathsymbol [bot] [nothing] [sy] ["3F] + +\definemathcommand [hbar] {{\mathchar'26\mkern-9muh}} +\definemathcommand [surd] {{\mathchar"1270}} % ? +\definemathcommand [angle] {\PLAINangle} + +\stopmathcollection + +\startmathcollection[default] + +\definemathsymbol [triangle] [ord] [sy] ["34] +\definemathsymbol [forall] [ord] [sy] ["38] +\definemathsymbol [exists] [ord] [sy] ["39] +\definemathsymbol [neg] [ord] [sy] ["3A] +\definemathsymbol [flat] [ord] [mi] ["5B] +\definemathsymbol [natural] [ord] [mi] ["5C] +\definemathsymbol [sharp] [ord] [mi] ["5D] +\definemathsymbol [clubsuit] [ord] [sy] ["7C] +\definemathsymbol [diamondsuit] [ord] [sy] ["7D] +\definemathsymbol [heartsuit] [ord] [sy] ["7E] +\definemathsymbol [spadesuit] [ord] [sy] ["7F] + +\stopmathcollection + +\startmathcollection[default] + +\definemathcommand [square] {\hbox{\hsmash{$\sqcup$}$\sqcap$}} + +\stopmathcollection + +\startmathcollection[default] + +\definemathsymbol [coprod] [op] [ex] ["60] +\definemathsymbol [bigvee] [op] [ex] ["57] +\definemathsymbol [bigwedge] [op] [ex] ["56] +\definemathsymbol [biguplus] [op] [ex] ["55] +\definemathsymbol [bigcap] [op] [ex] ["54] +\definemathsymbol [bigcup] [op] [ex] ["53] +\definemathsymbol [intop] [op] [ex] ["52] +\definemathsymbol [prod] [op] [ex] ["51] +\definemathsymbol [sum] [op] [ex] ["50] +\definemathsymbol [bigotimes] [op] [ex] ["4E] +\definemathsymbol [bigoplus] [op] [ex] ["4C] +\definemathsymbol [bigodot] [op] [ex] ["4A] +\definemathsymbol [ointop] [op] [ex] ["48] +\definemathsymbol [bigsqcup] [op] [ex] ["46] +\definemathsymbol [smallint] [op] [sy] ["73] + +\stopmathcollection + +\startmathcollection[default] + +\definemathsymbol [triangleleft] [bin] [mi] ["2F] +\definemathsymbol [triangleright] [bin] [mi] ["2E] +\definemathsymbol [bigtriangleup] [bin] [sy] ["34] +\definemathsymbol [bigtriangledown] [bin] [sy] ["35] +\definemathsymbol [wedge] [bin] [sy] ["5E] +\definemathsymbol [vee] [bin] [sy] ["5F] +\definemathsymbol [cap] [bin] [sy] ["5C] +\definemathsymbol [cup] [bin] [sy] ["5B] +\definemathsymbol [ddagger] [bin] [sy] ["7A] +\definemathsymbol [dagger] [bin] [sy] ["79] +\definemathsymbol [sqcap] [bin] [sy] ["75] +\definemathsymbol [sqcup] [bin] [sy] ["74] +\definemathsymbol [uplus] [bin] [sy] ["5D] +\definemathsymbol [amalg] [bin] [sy] ["71] +\definemathsymbol [diamond] [bin] [sy] ["05] +\definemathsymbol [bullet] [bin] [sy] ["0F] +\definemathsymbol [wr] [bin] [sy] ["6F] +\definemathsymbol [div] [bin] [sy] ["04] +\definemathsymbol [odot] [bin] [sy] ["0C] +\definemathsymbol [oslash] [bin] [sy] ["0B] +\definemathsymbol [otimes] [bin] [sy] ["0A] +\definemathsymbol [ominus] [bin] [sy] ["09] +\definemathsymbol [oplus] [bin] [sy] ["08] +\definemathsymbol [mp] [bin] [sy] ["07] +\definemathsymbol [pm] [bin] [sy] ["06] +\definemathsymbol [circ] [bin] [sy] ["0E] +\definemathsymbol [bigcirc] [bin] [sy] ["0D] +\definemathsymbol [setminus] [bin] [sy] ["6E] +\definemathsymbol [cdot] [bin] [sy] ["01] +\definemathsymbol [ast] [bin] [sy] ["03] +\definemathsymbol [times] [bin] [sy] ["02] +\definemathsymbol [star] [bin] [mi] ["3F] + +\stopmathcollection + +\startmathcollection[default] + +\definemathsymbol [propto] [rel] [sy] ["2F] +\definemathsymbol [sqsubseteq] [rel] [sy] ["76] +\definemathsymbol [sqsupseteq] [rel] [sy] ["77] +\definemathsymbol [parallel] [rel] [sy] ["6B] +\definemathsymbol [mid] [rel] [sy] ["6A] +\definemathsymbol [dashv] [rel] [sy] ["61] +\definemathsymbol [vdash] [rel] [sy] ["60] +\definemathsymbol [nearrow] [rel] [sy] ["25] +\definemathsymbol [searrow] [rel] [sy] ["26] +\definemathsymbol [nwarrow] [rel] [sy] ["2D] +\definemathsymbol [swarrow] [rel] [sy] ["2E] +\definemathsymbol [Leftrightarrow] [rel] [sy] ["2C] +\definemathsymbol [Leftarrow] [rel] [sy] ["28] +\definemathsymbol [Rightarrow] [rel] [sy] ["29] + +\stopmathcollection + +\startmathcollection[default] + +\definemathcommand [lnot] {\neg} +\definemathcommand [int] {\intop \intlimits} +\definemathcommand [oint] {\ointop\intlimits} +\definemathcommand [land] {\wedge} +\definemathcommand [lor] {\vee} +\definemathcommand [neq] {\not=} +\definemathcommand [ne] {\neq} +\definemathcommand [le] {\leq} +\definemathcommand [ge] {\geq} +\definemathcommand [eq] {=} +\definemathcommand [gt] {>} +\definemathcommand [lt] {<} +\definemathcommand [gets] {\leftarrow} +\definemathcommand [owns] {\ni} +\definemathcommand [to] {\rightarrow} +\definemathcommand [mapsto] {\mapstochar\rightarrow} + +\stopmathcollection + +\startmathcollection[default] + +\definemathsymbol [leq] [rel] [sy] ["14] +\definemathsymbol [geq] [rel] [sy] ["15] +\definemathsymbol [succ] [rel] [sy] ["1F] +\definemathsymbol [prec] [rel] [sy] ["1E] +\definemathsymbol [approx] [rel] [sy] ["19] +\definemathsymbol [succeq] [rel] [sy] ["17] +\definemathsymbol [preceq] [rel] [sy] ["16] +\definemathsymbol [supset] [rel] [sy] ["1B] +\definemathsymbol [subset] [rel] [sy] ["1A] +\definemathsymbol [supseteq] [rel] [sy] ["13] +\definemathsymbol [subseteq] [rel] [sy] ["12] +\definemathsymbol [in] [rel] [sy] ["32] +\definemathsymbol [ni] [rel] [sy] ["33] +\definemathsymbol [gg] [rel] [sy] ["1D] +\definemathsymbol [ll] [rel] [sy] ["1C] +\definemathsymbol [not] [rel] [sy] ["36] +\definemathsymbol [leftrightarrow] [rel] [sy] ["24] +\definemathsymbol [leftarrow] [rel] [sy] ["20] +\definemathsymbol [rightarrow] [rel] [sy] ["21] +\definemathsymbol [mapstochar] [rel] [sy] ["37] +\definemathsymbol [sim] [rel] [sy] ["18] +\definemathsymbol [simeq] [rel] [sy] ["27] +\definemathsymbol [perp] [rel] [sy] ["3F] +\definemathsymbol [equiv] [rel] [sy] ["11] +\definemathsymbol [asymp] [rel] [sy] ["10] +\definemathsymbol [smile] [rel] [mi] ["5E] +\definemathsymbol [frown] [rel] [mi] ["5F] +\definemathsymbol [leftharpoonup] [rel] [mi] ["28] +\definemathsymbol [leftharpoondown] [rel] [mi] ["29] +\definemathsymbol [rightharpoonup] [rel] [mi] ["2A] +\definemathsymbol [rightharpoondown] [rel] [mi] ["2B] +\definemathsymbol [lhook] [rel] [mi] ["2C] +\definemathsymbol [rhook] [rel] [mi] ["2D] + +\stopmathcollection + +\def\PLAINldots + {\ldotp\ldotp\ldotp} + +\def\PLAINcdots + {\cdotp\cdotp\cdotp} + +\def\PLAINvdots + {\vbox{\baselineskip.4\bodyfontsize\lineskiplimit\zeropoint + \kern.6\bodyfontsize\hbox{.}\hbox{.}\hbox{.}}} + +\def\PLAINddots + {\mkern1mu\raise.7\bodyfontsize\vbox{\kern.7\bodyfontsize\hbox{.}}\mkern2mu + \raise.4\bodyfontsize\hbox{.}\mkern2mu\raise.1\bodyfontsize\hbox{.}\mkern1mu} + +\startmathcollection[default] + +\definemathcommand [hookrightarrow] {\lhook\joinrel\rightarrow} +\definemathcommand [hookleftarrow] {\leftarrow\joinrel\rhook} +\definemathcommand [bowtie] {\mathrel\triangleright\joinrel\mathrel\triangleleft} +\definemathcommand [models] {\mathrel|\joinrel=} +\definemathcommand [iff] {\;\Longleftrightarrow\;} + +\definemathsymbol [ldotp] [punct] [mi] ["3A] +\definemathsymbol [cdotp] [punct] [sy] ["01] +\definemathsymbol [colon] [punct] [mr] ["3A] + +\definemathcommand [ldots] [inner] {\PLAINldots} +\definemathcommand [cdots] [inner] {\PLAINcdots} +\definemathcommand [vdots] [nothing] {\PLAINvdots} +\definemathcommand [ddots] [inner] {\PLAINddots} + +\stopmathcollection + +\startmathcollection[default] + +\definemathsymbol [acute] [accent] [mr] ["13] +\definemathsymbol [grave] [accent] [mr] ["12] +\definemathsymbol [ddot] [accent] [mr] ["7F] +\definemathsymbol [tilde] [accent] [mr] ["7E] +\definemathsymbol [mathring] [accent] [mr] ["17] +\definemathsymbol [bar] [accent] [mr] ["16] +\definemathsymbol [breve] [accent] [mr] ["15] +\definemathsymbol [check] [accent] [mr] ["14] +\definemathsymbol [hat] [accent] [mr] ["5E] +\definemathsymbol [vec] [accent] [mi] ["7E] % [ord] +\definemathsymbol [dot] [accent] [mr] ["5F] +\definemathsymbol [widetilde] [accent] [ex] ["65] % [ord] +\definemathsymbol [widehat] [accent] [ex] ["62] % [ord] + +\stopmathcollection + +\startmathcollection[default] + +\definemathsymbol [lmoustache] [open] [ex] ["7A] [ex] ["40] +\definemathsymbol [rmoustache] [close] [ex] ["7B] [ex] ["41] +\definemathsymbol [lgroup] [open] [mr] ["28] [ex] ["3A] % ? +\definemathsymbol [rgroup] [close] [mr] ["29] [ex] ["3B] % ? +\definemathsymbol [arrowvert] [nothing] [sy] ["6A] [ex] ["3C] +\definemathsymbol [Arrowvert] [nothing] [sy] ["6B] [ex] ["3D] +\definemathsymbol [bracevert] [nothing] [ex] ["3E] % ? +\definemathsymbol [Vert] [nothing] [sy] ["6B] [ex] ["0D] +\definemathsymbol [vert] [nothing] [sy] ["6A] [ex] ["0C] +\definemathsymbol [uparrow] [rel] [sy] ["22] [ex] ["78] +\definemathsymbol [downarrow] [rel] [sy] ["23] [ex] ["79] +\definemathsymbol [updownarrow] [rel] [sy] ["6C] [ex] ["3F] +\definemathsymbol [Uparrow] [rel] [sy] ["2A] [ex] ["7E] +\definemathsymbol [Downarrow] [rel] [sy] ["2B] [ex] ["7F] +\definemathsymbol [Updownarrow] [rel] [sy] ["6D] [ex] ["77] +\definemathsymbol [backslash] [nothing] [sy] ["6E] [ex] ["0F] +\definemathsymbol [langle] [open] [sy] ["68] [ex] ["0A] +\definemathsymbol [rangle] [close] [sy] ["69] [ex] ["0B] +\definemathsymbol [lbrace] [open] [sy] ["66] [ex] ["08] +\definemathsymbol [rbrace] [close] [sy] ["67] [ex] ["09] +\definemathsymbol [lceil] [open] [sy] ["64] [ex] ["06] +\definemathsymbol [rceil] [close] [sy] ["65] [ex] ["07] +\definemathsymbol [lfloor] [open] [sy] ["62] [ex] ["04] +\definemathsymbol [rfloor] [close] [sy] ["63] [ex] ["05] + +\definemathsymbol [sqrt] [radical] [sy] ["70] [ex] ["70] + +\stopmathcollection + +%D By request: + +\startmathcollection[default] + +\definemathsymbol [lvert] [open] [sy] ["6A] [ex] ["0C] +\definemathsymbol [rvert] [close] [sy] ["6A] [ex] ["0C] + +\definemathsymbol [lVert] [open] [sy] ["6B] [ex] ["0D] +\definemathsymbol [rVert] [close] [sy] ["6B] [ex] ["0D] + +\stopmathcollection + +%D For brooks: +%D +%D \starttyping +%D $\sqrt[3]{10}$ +%D \stoptyping + +\ifx\normalsqrt\undefined \let\normalsqrt\sqrt \fi % just set to: \dohandlemathtoken {sqrt} + +\def\notsosqrt[#1]{\root#1\of} + +\unexpanded\def\sqrt{\doifnextoptionalelse\notsosqrt\normalsqrt} + +\def\PLAINbig {\@@dobig{0.85}} +\def\PLAINBig {\@@dobig{1.15}} +\def\PLAINbigg{\@@dobig{1.45}} +\def\PLAINBigg{\@@dobig{1.75}} + +\startrawmathcollection [default] + +\definemathcommand [bigl] [open] [one] {\big} +\definemathcommand [bigm] [rel] [one] {\big} +\definemathcommand [bigr] [close] [one] {\big} +\definemathcommand [Bigl] [open] [one] {\Big} +\definemathcommand [Bigm] [rel] [one] {\Big} +\definemathcommand [Bigr] [close] [one] {\Big} +\definemathcommand [biggl] [open] [one] {\bigg} +\definemathcommand [biggm] [rel] [one] {\bigg} +\definemathcommand [biggr] [close] [one] {\bigg} +\definemathcommand [Biggl] [open] [one] {\Bigg} +\definemathcommand [Biggm] [rel] [one] {\Bigg} +\definemathcommand [Biggr] [close] [one] {\Bigg} + +\definemathcommand [big] {\PLAINbig} +\definemathcommand [Big] {\PLAINBig} +\definemathcommand [bigg] {\PLAINbigg} +\definemathcommand [Bigg] {\PLAINBigg} + +\stoprawmathcollection + +\startmathcollection[default] + +\definemathsymbol [dag] [box] [sy] ["79] +\definemathsymbol [ddag] [box] [sy] ["7A] +\definemathsymbol [S] [box] [sy] ["78] +\definemathsymbol [P] [box] [sy] ["7B] +\definemathsymbol [Orb] [box] [sy] ["0D] + +\stopmathcollection + +\def\PLAINroot#1#2% + {\setbox\zerocount\hbox{$\mathsurround\zeropoint#1\sqrt{#2}$}\dimen@\ht\zerocount + \advance\dimen@-\dp\zerocount + \mkern5mu\raise.6\dimen@\copy\rootbox \mkern-10mu\box\zerocount} + +\def\PLAINmatrix#1% + {\null\,\vcenter{\normalbaselines\mathsurround\zeropoint + \ialign{\hfil$##$\hfil&&\quad\hfil$##$\hfil\crcr + \mathstrut\crcr\noalign{\kern-\baselineskip} + #1\crcr\mathstrut\crcr\noalign{\kern-\baselineskip}}}\,} + +\startrawmathcollection[default] + +\definemathcommand [mathstrut] {\vphantom{(}} +\definemathcommand [joinrel] {\mathrel{\mkern-3mu}} +\definemathcommand [r@@t] {\PLAINroot} +\definemathcommand [matrix] {\PLAINmatrix} + +\definemathcommand [over] {\normalover} % hack, to do + +\stoprawmathcollection + +\def\{{\lbrace} +\def\}{\rbrace} + +%def\bbd{\fam\purefamily{blackboard}} +%def\cal{\fam\purefamily{calfamily}} + +\def\mit{\fam\purefamily{mitfamily}} + +\def\Bbb{\blackboard} % conforming amstex + +\startmathcollection[default] + + \definemathsymbol [mathperiod] [ord] [mi] ["3A] + \definemathsymbol [textperiod] [punct] [mi] ["3A] + + \definemathsymbol [mathcomma] [ord] [mi] ["3B] + \definemathsymbol [textcomma] [punct] [mi] ["3B] + +\stopmathcollection + +\definemathpunctuation . mathperiod textperiod +\definemathpunctuation , mathcomma textcomma + +%D The following colon related definitions are provided by Aditya +%D Mahajan who derived them from \type {mathtools.sty} and \type +%D {colonequals.sty}. + +%D \macros +%D {centercolon, colonminus, minuscolon, colonequals, equalscolon, +%D colonapprox, approxcolon, colonsim, simcolon, coloncolon, +%D coloncolonminus, minuscoloncolon, coloncolonequals, +%D equalscoloncolon, coloncolonapprox, approxcoloncolon, +%D colonsim, simcoloncolon} +%D +%D In $a := b$ the colon is not vertically centered with the equal +%D to. Also the distance between colon and equal to is a bit large. +%D So, we define a vertically centered colon \tex {centercolon} and +%D a few macros for colon and double colon relation symbols. +%D +%D \startlines +%D \formula {A \centercolon B} +%D \formula {A \colonminus B} +%D \formula {A \minuscolon B} +%D \formula {A \colonequals B} +%D \formula {A \equalscolon B} +%D \formula {A \colonapprox B} +%D \formula {A \approxcolon B} +%D \formula {A \colonsim B} +%D \formula {A \simcolon B} +%D \formula {A \coloncolon B} +%D \formula {A \coloncolonminus B} +%D \formula {A \minuscoloncolon B} +%D \formula {A \coloncolonequals B} +%D \formula {A \equalscoloncolon B} +%D \formula {A \coloncolonapprox B} +%D \formula {A \approxcoloncolon B} +%D \formula {A \colonsim B} +%D \formula {A \simcoloncolon B} +%D \stoplines + +%D The next macros take care of the space between the colon and the +%D relation symbol. + +\definemathcommand [colonsep] {\mkern-1.2mu} +\definemathcommand [doublecolonsep] {\mkern-0.9mu} + +%D The next macro vertically centeres its contents. + +\def\@center@math#1% + {\vcenter{\hbox{$\mathsurround\zeropoint#1$}}} + +\def\@center@colon + {\mathpalette\@center@math{\colon}} + +%D Now we define all the colon relations. + +\definemathcommand [centercolon] [rel] {\@center@colon} +\definemathcommand [colonminus] [rel] {\centercolon\colonsep\mathrel{-}} +\definemathcommand [minuscolon] [rel] {\mathrel{-}\colonsep\centercolon} +\definemathcommand [colonequals] [rel] {\centercolon\colonsep=} +\definemathcommand [equalscolon] [rel] {=\centercolon\colonsep} +\definemathcommand [colonapprox] [rel] {\centercolon\colonsep\approx} +\definemathcommand [approxcolon] [rel] {\approx\centercolon\colonsep} +\definemathcommand [colonsim] [rel] {\centercolon\colonsep\sim} +\definemathcommand [simcolon] [rel] {\sim\centercolon\colonsep} + +\definemathcommand [coloncolon] [rel] {\centercolon\doublecolonsep\centercolon} +\definemathcommand [coloncolonminus] [rel] {\coloncolon\colonsep\mathrel{-}} +\definemathcommand [minuscoloncolon] [rel] {\mathrel{-}\colonsep\coloncolon} +\definemathcommand [coloncolonequals] [rel] {\coloncolon\colonsep=} +\definemathcommand [equalscoloncolon] [rel] {=\coloncolon\colonsep} +\definemathcommand [coloncolonapprox] [rel] {\coloncolon\colonsep\approx} +\definemathcommand [approxcoloncolon] [rel] {\approx\coloncolon\colonsep} +\definemathcommand [colonsim] [rel] {\coloncolon\colonsep\sim} +\definemathcommand [simcoloncolon] [rel] {\sim\coloncolon\colonsep} + +%D More integrals (AM): + +\def\dointkern{\mkern-6mu\mathchoice{\mkern-3mu}{}{}{}} + +\definemathcommand [iint] {\repeatintegral\plusone } +\definemathcommand [iiint] {\repeatintegral\plustwo } +\definemathcommand [iiiint] {\repeatintegral\plusthree} + +\def\repeatintegral#1% + {\scratchtoks\emptytoks + \let\dointlimits\donothing + \let\dodointlimits\intlimits + \dorecurse{#1}{\appendtoks \intop \dointkern \to \scratchtoks} + \appendtoks \intop \dointlimits \dodointlimits \to \scratchtoks + \edef\dodorepeatintegral{\the\scratchtoks}% + \futurelet\next\dorepeatintegral} + +%D If the \type{\limits} option is used after \type{\iint}, use +%D \type{\mathop} and fudge the left hand space a bit to make the +%D subscript visually centered. + +\def\dorepeatintegral + {\ifx\next\limits \dointlimitcorrection \else + \ifx\next\displaylimits \dointlimitcorrection \else + \ifx\next\nolimits \donothing \else + \ifcase\intlimitcode\else \dointlimitcorrection \fi\fi\fi\fi + \dodorepeatintegral} + +\def\dointlimitcorrection + {\mkern-7mu\mathchoice{\mkern-2mu}{}{}{}% + \mathop\bgroup + \mkern7mu\mathchoice{\mkern2mu}{}{}{}% + \let\dointlimits\egroup} + +\setupmathematics + [integral=nolimits] + +\protect \endinput diff --git a/tex/context/base/math-tim.mkii b/tex/context/base/math-tim.mkii new file mode 100644 index 000000000..3b9aea103 --- /dev/null +++ b/tex/context/base/math-tim.mkii @@ -0,0 +1,371 @@ +%D \module +%D [ file=math-tim, +%D version=2001.04.12, +%D title=\CONTEXT\ Math Macros, +%D subtitle=Mathtime Specials, +%D author={Hans Hagen \& Taco Hoekwater}, +%D date=\currentdate, +%D copyright=\PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\endinput % i will clean this up after taco has gone over it + +%D With thanks to Berthold Horn from YandY for providing me +%D evaluation copies of the MathTimePlus fonts. + +% version 0 : Michael Spivak +% version 1 : Taco Hoekwater +% version 2 : Hans Hagen +% version 3 : etc etc etc + +\unprotect + +%D We use the predefined spare families \type {\mcfam} and +%D \type {\mdfam}. + +\let\cafam\mcfam \let\hexcafam\hexmcfam +\let\gbfam\mdfam \let\hexgbfam\hexmdfam +\let\gkfam\mdfam \let\hexgkfam\hexmdfam + +% Why is this needed? + +% \font\tenmd =mtgu at 10pt +% \font\sevenmd=mtgu at 7.6pt +% \font\fivemd =mtgu at 6pt +% \font\tenmc =mtms at 10pt +% \font\sevenmc=mtms at 7.6pt +% \font\fivemc =mtms at 6pt +% +% \textfont \mcfam\tenmc \textfont \mdfam\tenmd +% \scriptfont \mcfam\sevenmc \scriptfont \mdfam\sevenmd +% \scriptscriptfont\mcfam\fivemc \scriptscriptfont\mdfam\fivemd + +% \addtocommalist{gk}\familylist +% \addtocommalist{gb}\familylist +% +% \defineinterfaceconstant {ca} {ca} % boohoo! +% \defineinterfaceconstant {gk} {gk} % boohoo! +% \defineinterfaceconstant {gb} {gb} % boohoo! + +% \definealternativestyle[script] [\ca][\ca] +% \definealternativestyle[greek] [\gk][\gk] +% \definealternativestyle[boldgreek][\gb][\gb] + +% \definebodyfont +% [5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,14.4pt] [rm] +% [ca=mtms sa 1, +% gk=mtgu sa 1, +% gb=mtgub sa 1] + +%D Since a font size is a rather fuzzy thing, it will be no +%D surprise that the Math Times fonts have different specs +%D than the Computer Modern Roman fonts. +%D +%D \starttabulate[|Bl|c|c|c|c|c|c|c|c|c|c|] +%D \NC Computer Modern\NC +%D 5 \NC6 \NC7 \NC8 \NC9 \NC10 \NC11 \NC12 \NC14 \NC18\NC\NR +%D \NC Math Times \NC +%D 6.0\NC6.8\NC7.6\NC8.4\NC9.2\NC10.0\NC10.8\NC11.6\NC13.2\NC--\NC\NR +%D \stoptabulate +%D +%D The following definitions presume the existence of \type +%D {tio} and \type {tibio} font alternatives. Definitions for +%D \type {\tf.} etc and \type {\sc} are left as they are. + +%D moved code + +%D The next lines set up coding dependant versions of math +%D accents (for \type {texnansi} of course). These are needed +%D in commands like \type {\bf} and \type {\it} (which use the +%D text font encoding) a opposed to \type {_no spec_} and +%D \type {mi} that use the math font encoding. This stuff +%D should be made more general! + +% \daghex, \ddaghex and \dothex were wrong in my version of mtmacs + +\def\daghex {86} % to do +\def\ddaghex {87} +\def\Shex {A7} +\def\Phex {B6} +\def\barhex {16} +\def\gravehex{12} +\def\acutehex{13} +\def\checkhex{14} +\def\brevehex{15} +\def\hathex {5E} +\def\dothex {05} +\def\tildehex{7E} +\def\ddothex {7F} + +%D The \type {mtex} fonts need a recalculation of \type +%D {\p@renwd}, which in \CONTEXT\ is done automatically. + +%D The following definitions are mostly copied from the file +%D \type {mtmacs.tex}, which banner said: +%D +%D \starttyping +%D MTMACS.TEX VERSION 1.1.1 (1996 Dec 8) +%D COPYRIGHT (C) 1992, 1993, 1996 BY THE TEXPLORATORS CORPORATION +%D ALL RIGHTS RESERVED +%D \stoptyping +%D +%D We reformatted the macros and changed a few bits and +%D pieces. A further cleanup with regards to the scratch +%D registers will be done later. + +\mathchardef\Gamma = "0130 +\mathchardef\Delta = "0131 +\mathchardef\Theta = "0132 +\mathchardef\Lambda = "0133 +\mathchardef\Xi = "0134 +\mathchardef\Pi = "0135 +\mathchardef\Sigma = "0136 +\mathchardef\Upsilon = "0137 +\mathchardef\Phi = "0138 +\mathchardef\Psi = "0139 +\mathchardef\Omega = "017F +\mathchardef\varGamma = "0100 +\mathchardef\varDelta = "0101 +\mathchardef\varTheta = "0102 +\mathchardef\varLambda = "0103 +\mathchardef\varXi = "0104 +\mathchardef\varPi = "0105 +\mathchardef\varSigma = "0106 +\mathchardef\varUpsilon = "0107 +\mathchardef\varPhi = "0108 +\mathchardef\varPsi = "0109 +\mathchardef\varOmega = "010A +\mathchardef\varkappa = "017E + +\mathchardef\ldotp = "613A + +\mathchardef\triangleleft = "2247 +\mathchardef\triangleright = "2246 +\mathchardef\comp = "2242 + +\mathchardef\Relbar = "3248 +\mathchardef\dagger = "20\daghex % to do +\mathchardef\ddagger = "20\ddaghex % to do +\mathchardef\dotacc = "0250 + +\mathcode`\( = "412E +\mathcode`\) = "512F +\mathcode`\. = "013A +\mathcode`\, = "613B +\mathcode`\+ = "2243 +\mathcode`\= = "3244 +\mathcode`\; = "6249 + +\delcode `\( = "12E300 +\delcode `\) = "12F301 + +\let\mit=\undefined % to do + +\def\vec{\mathaccent"0245 } +\def\t#1{{\edef\next{\the\font}\the\textfont2\accent"41\next#1}} + +% fam equals -1 unless the user has said something +% like \rm (cf. the texbook page 290) + +\def\ifdefaultfamelse#1#2% + {\ifnum\fam=\minusone\mathaccent#1\else\mathaccent#2\fi} + +\let\noaccents@\relax + +\def\grave {\ifdefaultfamelse{"024A}{"70\gravehex}} +\def\acute {\ifdefaultfamelse{"024B}{"70\acutehex}} +\def\check {\ifdefaultfamelse{"024C}{"70\checkhex}} +\def\breve {\ifdefaultfamelse{"024D}{"70\brevehex}} +\def\bar {\ifdefaultfamelse{"024E}{"70\barhex }} +\def\hat {\ifdefaultfamelse{"024F}{"70\hathex }} +\def\dot {\ifdefaultfamelse{"0250}{"70\dothex }} +\def\tilde {\ifdefaultfamelse{"0251}{"70\tildehex}} +\def\ddot {\ifdefaultfamelse{"0252}{"70\ddothex }} + +\def\widebar{\mathaccent"0253 } + +\def\mathhexbox@#1#2#3% + {\relax + \ifmmode + \mathpalette{}{\mathsurround\zeropoint\rm\mathchar"#1#2#3}% + \else + \leavevmode + \hbox{$\mathsurround\zeropoint\rm\mathchar"#1#2#3$}% + \fi} + +\def\dag {\edef\next@{0\daghex }\expandafter\mathhexbox@\next@} +\def\ddag{\edef\next@{0\ddaghex}\expandafter\mathhexbox@\next@} + +\def\S{\edef\next@{0\Shex}\expandafter\mathhexbox@\next@} +\def\P{\edef\next@{0\Phex}\expandafter\mathhexbox@\next@} + +\def\vdots% + {\vbox + {\baselineskip4\points + \lineskiplimit\zeropoint + \kern6\points\hbox{$\mathsurround\zeropoint.$}\hbox{$\mathsurround\zeropoint.$}\hbox{$\mathsurround\zeropoint.$}}} + +\def\ddots% + {\mathinner + {\mkern1mu + \raise7\points\vbox{\kern 7\points\hbox{$\mathsurround\zeropoint.$}}\mkern2mu + \raise4\points\hbox{$\mathsurround\zeropoint.$}\mkern2mu + \raise \points\hbox{$\mathsurround\zeropoint.$}\mkern1mu}} + +\def\hbar + {{\mathchoice + {\hbox{\lower.07em \hbox{$\mathchar"\barhex$}}} + {\hbox{\lower.07em \hbox{$\mathchar"\barhex$}}} + {\hbox{\lower.049em\hbox{$\scriptstyle\mathchar"\barhex$}}} + {\hbox{\lower.035em\hbox{$\scriptscriptstyle\mathchar"\barhex$}}}% + \mkern-6.3muh}} + +\def\angle% + {{\vbox{\ialign{$\mathsurround\zeropoint\scriptstyle##$\crcr + \not\mathrel{\mkern14mu}\crcr + \noalign{\nointerlineskip} + \mkern2.5mu\leaders\hrule height.48\points\hfill\mkern2.5mu\crcr}}}} + +\newdimen\amstexex + +\amstexex = .2326ex + +\def\varinjlim% + {\mathop{\vtop{\ialign{##\crcr + \hfil\the\textfont\zerocount lim\hfil\crcr + \noalign{\nointerlineskip}\rightarrowfill\crcr + \noalign{\nointerlineskip\kern-\amstexex}\crcr}}}} + +\def\varprojlim% + {\mathop{\vtop{\ialign{##\crcr + \hfil\the\textfont\zerocount lim\hfil\crcr + \noalign{\nointerlineskip}\leftarrowfill\crcr + \noalign{\nointerlineskip\kern-\amstexex}\crcr}}}} + +\def\varliminf{\mathop{\underbar {lim}}} % context-ified +\def\varlimsup{\mathop{\overstrike{lim}}} % context-ified + +\def\spdot {^{\hbox{\raise\amstexex\hbox{\the\textfont\zerocount .}}}} +\def\spddot {^{\hbox{\raise\amstexex\hbox{\the\textfont\zerocount ..}}}} +\def\spdddot {^{\hbox{\raise\amstexex\hbox{\the\textfont\zerocount ...}}}} +\def\spddddot{^{\hbox{\raise\amstexex\hbox{\the\textfont\zerocount....}}}} + +%D Here some code is merged in order to save strings. + +\def\domultidot#1#2% + {\setbox0\hbox{$#1#2$}% + \setbox2\hbox{$#1\infty$}% + \dimen0\ht2 + \ifdim\ht0<\dimen0 \dimen0\ht0 \fi + \setbox2\hbox{$#1#2\mathchar"012D$}% + \dimen2\wd2 + \advance\dimen2-\wd0 + \setbox0\hbox{$#1\rhook$}% + \advance\dimen2-\wd0 + \vbox + {\offinterlineskip + \ialign{\hfil$#1##$\hfil\cr + \kern\dimen2\dotacc\mkern-2.5mu\dotacc\mkern-2.5mu\dotacc\thirddot\cr + \noalign{\vskip-\dimen0}% + #2\cr}}} + +\def\dddot% + {\def\thirddot{}% + \mathpalette\domultidot} + +\def\ddddot% + {\def\thirddot{\mkern-2.5mu\dotacc}% + \mathpalette\domultidot} + +\newcount\uproot@ +\newcount\leftroot@ + +\def\nonmatherr#1% + {\errmessage{\noexpand#1allowed only in math mode}} + +\def\uproot#1% + {\relax + \ifmmode + \uproot@#1\relax + \else + \nonmatherr\uproot + \fi} + +\def\leftroot#1% + {\relax + \ifmmode + \leftroot@#1\relax + \else + \nonmatherr\leftroot + \fi} + +\def\root#1\of#2% + {\setbox\rootbox=\hbox{$\mathsurround\zeropoint\scriptscriptstyle{#1}$}% + \mathpalette\r@@t{#2}} + +\def\r@@t#1#2% + {\setbox\zerocount\hbox{$\uproot@\zerocount\leftroot\zerocount\mathsurround\zeropoint#1\sqrt{#2}$}% + \dimen@\ht\zerocount\advance\dimen@-\dp\zerocount + \dimen@ii\dimen@ + \ifdim\dimen@>30\points \advance\dimen@ii-16\points \else + \ifdim\dimen@>24\points \advance\dimen@ii -8\points \else + \ifdim\dimen@>18\points \advance\dimen@ii -6\points \else + \ifdim\dimen@>12\points \advance\dimen@ii -4\points \else + \ifdim\dimen@>10\points \advance\dimen@ii -2\points \fi\fi\fi\fi\fi + \setbox\plustwo=\hbox{$\mathsurround\zeropoint#1\mskip\uproot@ mu$}% + \advance\dimen@ii by1.667\wd\plustwo + \mkern-\leftroot@ mu\mkern5mu\raise.6\dimen@ii\copy\rootbox + \mkern-8mu\mkern\leftroot@ mu\box\zerocount\leftroot\zerocount\uproot\zerocount} + +\def\space@.{\futurelet\space@\relax} \space@. % really needed ? + +\def\jadjust% + {\mkern-\plustwo mu} + +%D For the moment the following code is left unchanged. It is +%D not used anyway. + +\newif\ifsubscriptcorrection \subscriptcorrectionfalse + +\ifsubscriptcorrection + + \expandafter\let\csname subscript character \string_\endcsname_ + + \catcode`\_=\active + + \def_% + {\ifmmode + \expandafter\sb@ + \else + \expandafter\csname subscript character \string_\endcsname + \fi} + + \let\sb_ + + \ifnum\catcode`\^^A=8 \catcode`\^^A\active\let^^A_\fi + + \def\sb@#1% + {\csname subscript character \string_\endcsname + {\futurelet\next\sb@@#1}} + + \def\sb@@% + {\ifx\next\space@ + \def\next@. % + {\futurelet\next\sb@@}% + \else + \def\next@.% + {\ifx\next j% + \mkern-\plustwo mu\else + \ifx\next f% + \mkern-\plustwo mu\else + \ifx\next p% + \mkern-\plusone mu\fi\fi\fi}% + \fi + \next@.} + +\fi + +\protect diff --git a/tex/context/base/math-uni.mkii b/tex/context/base/math-uni.mkii new file mode 100644 index 000000000..e8fd3f05d --- /dev/null +++ b/tex/context/base/math-uni.mkii @@ -0,0 +1,237 @@ +%D \module +%D [ file=math-uni, +%D version=2005.06.11, +%D title=\CONTEXT\ Math Macros, +%D subtitle=unicode support, +%D author={Nikolai Weibull \& Taco Hoekwater}, +%D date=\currentdate, +%D copyright=PRAGMA] + +\unprotect + +% needed for unic-032 % TH + +\startmathcollection[default] + + \definemathcommand [unic@doubleverticalline] {\mathord{\parallel}} + \definemathcommand [unic@doublelowline] {\underline{\textunderscore}} + + \definemathcommand [unic@doubleprime] {''} + \definemathcommand [unic@tripleprime] {'''} + + \definemathcommand [unic@reverseddoubleprime] {\mathord{\backprime\backprime}} + \definemathcommand [unic@reversedtripleprime] {\mathord{\backprime\backprime\backprime}} + + \definemathcommand [unic@fractionslash] {\vulgarfraction{}{}} + \definemathcommand [unic@lowasterisk] {\mathord{\lower-.2em\hbox{$\ast$}}} + + \definemathcommand [unic@functionapplication] {\mathop{}} + \definemathcommand [unic@invisibletimes] {\mathbin{}} + \definemathcommand [unic@invisibleseparator] {\mathpunct{}} + +\stopmathcollection + +% needed for unic-033 % TH + +\def\unic@vulgarfraction#1#2% + {\hbox + {\high{{\tfx\it #1}\kern -.2em}% + \symbol[vulgarfraction]% + \kern -.2em\low{{\tfx\it #2}}}} + +\startmathcollection[default] + + \definemathcommand [unic@accountof] {\unic@vulgarfraction{a}{c}} + \definemathcommand [unic@addressedtothesubject] {\unic@vulgarfraction{a}{s}} + \definemathcommand [unic@doublestruckC] {{\Bbb C}} + \definemathcommand [unic@degreecelsius] {{\textdegree \tf C}} + \definemathcommand [unic@centrelinesymbol] {\unknownchar} + \definemathcommand [unic@careof] {\unic@vulgarfraction{c}{o}} + \definemathcommand [unic@cadauna] {\unic@vulgarfraction{c}{u}} + \definemathcommand [unic@eulerconstant] {{\rm e}} + \definemathcommand [unic@scruple] {\unknownchar} + \definemathcommand [unic@degreefahrenheit] {{\textdegree \tf F}} + \definemathcommand [unic@scriptg] {\unknownchar} + \definemathcommand [unic@scriptH] {{\cal H}} + \definemathcommand [unic@blackletterH] {{\fraktur H}} + \definemathcommand [unic@doublestruckH] {{\Bbb H}} + \definemathcommand [unic@planckconstant] {h} + \definemathcommand [unic@planckconstantovertwopi] {\hslash} + \definemathcommand [unic@scriptI] {{\cal I}} + \definemathcommand [unic@blackletterI] {{\fraktur I}} + \definemathcommand [unic@scriptL] {{\cal L}} + \definemathcommand [unic@scriptl] {\ell} + \definemathcommand [unic@lbbarsymbol] {\unknownchar} + \definemathcommand [unic@doublestruckN] {{\Bbb N}} + \definemathcommand [unic@numerosign] {\hbox{\symbol[numero]}} + \definemathcommand [unic@soundrecordingcopyright] {\encircled{{\tfx P}}} + \definemathcommand [unic@scriptP] {{\cal P}} + \definemathcommand [unic@doublestruckP] {{\Bbb P}} + \definemathcommand [unic@doublestruckQ] {{\Bbb Q}} + \definemathcommand [unic@scriptR] {{\cal R}} + \definemathcommand [unic@blackletterR] {{\fraktur R}} + \definemathcommand [unic@doublestruckR] {{\Bbb R}} + \definemathcommand [unic@prescriptiontake] {\unknownchar} + \definemathcommand [unic@response] {\unknownchar} + \definemathcommand [unic@servicemark] {\high{\txx SM}} + \definemathcommand [unic@telephonesign] {\high{\txx TEL}} + \definemathcommand [unic@trademarksign] {\trademark} + \definemathcommand [unic@versickle] {\unknownchar} + \definemathcommand [unic@doublestruckZ] {{\Bbb Z}} + \definemathcommand [unic@ouncesign] {\unknownchar} + \definemathcommand [unic@ohmsign] {\Omega} + \definemathcommand [unic@invertedohmsign] {\rotate[\c!rotation=180]{\Omega}} + \definemathcommand [unic@blackletterZ] {{\fraktur Z}} + \definemathcommand [unic@turnedgreekletteriota] {\rotate[\c!rotation=180]{\iota}} + \definemathcommand [unic@kelvinsign] {{\tf K}} + \definemathcommand [unic@angstromsign] {\Angstrom} + \definemathcommand [unic@scriptB] {{\cal B}} + \definemathcommand [unic@blackletterC] {{\fraktur C}} + \definemathcommand [unic@estimatedsymbol] {\unknownchar} + \definemathcommand [unic@scripte] {\unknownchar} + \definemathcommand [unic@scriptE] {{\cal E}} + \definemathcommand [unic@scriptF] {{\cal F}} + \definemathcommand [unic@turnedF] {\rotate[\c!rotation=180]{{\tf F}}} + \definemathcommand [unic@scriptM] {{\cal M}} + \definemathcommand [unic@scripto] {\unknownchar} + + \definemathcommand [unic@informationsource] {{\bf i}} + \definemathcommand [unic@rotatedQ] {\rotate[\c!rotation=90]{{\tf Q}}} + \definemathcommand [unic@facsimilesign] {\unknownchar} + \definemathcommand [unic@doublestruckpi] {\pi}%{\unknownchar} + \definemathcommand [unic@doublestruckgamma] {\gamma}%{\unknownchar} + \definemathcommand [unic@doublestruckGamma] {\Gamma}%{\unknownchar} + \definemathcommand [unic@doublestruckPi] {\Pi}%{\unknownchar} + \definemathcommand [unic@doublestrucknarysummation] {\unknownchar} + \definemathcommand [unic@turnedsansserifG] {\rotate[\c!rotation=180]{{\ss G}}} + \definemathcommand [unic@turnedsansserifL] {\rotate[\c!rotation=180]{{\ss L}}} + \definemathcommand [unic@reversedsansserifL] {\mirror{{\ss L}}} + \definemathcommand [unic@turnedsansserifY] {\rotate[\c!rotation=180]{{\ss Y}}} + \definemathcommand [unic@doublestruckitalicD] {D}%{\unknownchar} + \definemathcommand [unic@doublestruckitalicd] {d}%{\unknownchar} + \definemathcommand [unic@doublestruckitalice] {e}%{\unknownchar} + \definemathcommand [unic@doublestruckitalici] {i}%{\unknownchar} + \definemathcommand [unic@doublestruckitalicj] {j}%{\unknownchar} + \definemathcommand [unic@propertyline] {\unknownchar} + \definemathcommand [unic@turnedampersand] {\rotate[\c!rotation=180]{\&}} + \definemathcommand [unic@persign] {\unknownchar} + + \definemathcommand [unic@fractiononethird] {\vulgarfraction{1}{3}} + \definemathcommand [unic@fractiontwothirds] {\vulgarfraction{2}{3}} + \definemathcommand [unic@fractiononefifth] {\vulgarfraction{1}{5}} + \definemathcommand [unic@fractiontwofifths] {\vulgarfraction{2}{5}} + \definemathcommand [unic@fractionthreefifths] {\vulgarfraction{3}{5}} + \definemathcommand [unic@fractionfourfifths] {\vulgarfraction{4}{5}} + \definemathcommand [unic@fractiononesixth] {\vulgarfraction{1}{6}} + \definemathcommand [unic@fractionfivesixths] {\vulgarfraction{5}{6}} + \definemathcommand [unic@fractiononeeighth] {\vulgarfraction{1}{8}} + \definemathcommand [unic@fractionthreeeighths] {\vulgarfraction{3}{8}} + \definemathcommand [unic@fractionfiveeighths] {\vulgarfraction{5}{8}} + \definemathcommand [unic@fractionseveneighths] {\vulgarfraction{7}{8}} + \definemathcommand [unic@fractionnumeratorone] {\vulgarfraction{1}{}} + \definemathcommand [unic@romannumeralOne] {{\tf I}} + \definemathcommand [unic@romannumeralTwo] {{\tf II}} + \definemathcommand [unic@romannumeralThree] {{\tf III}} + \definemathcommand [unic@romannumeralFour] {{\tf IV}} + \definemathcommand [unic@romannumeralFive] {{\tf V}} + \definemathcommand [unic@romannumeralSix] {{\tf VI}} + \definemathcommand [unic@romannumeralSeven] {{\tf VII}} + \definemathcommand [unic@romannumeralEight] {{\tf VIII}} + \definemathcommand [unic@romannumeralNine] {{\tf IX}} + \definemathcommand [unic@romannumeralTen] {{\tf X}} + \definemathcommand [unic@romannumeralEleven] {{\tf XI}} + \definemathcommand [unic@romannumeralTwelve] {{\tf XII}} + \definemathcommand [unic@romannumeralFifty] {{\tf L}} + \definemathcommand [unic@romannumeralOnehundred] {{\tf C}} + \definemathcommand [unic@romannumeralFivehundred] {{\tf D}} + \definemathcommand [unic@romannumeralOnethousand] {{\tf M}} + \definemathcommand [unic@romannumeralone] {{\tf i}} + \definemathcommand [unic@romannumeraltwo] {{\tf ii}} + \definemathcommand [unic@romannumeralthree] {{\tf iii}} + \definemathcommand [unic@romannumeralfour] {{\tf iv}} + \definemathcommand [unic@romannumeralfive] {{\tf v}} + \definemathcommand [unic@romannumeralsix] {{\tf vi}} + \definemathcommand [unic@romannumeralseven] {{\tf vii}} + \definemathcommand [unic@romannumeraleight] {{\tf viii}} + \definemathcommand [unic@romannumeralnine] {{\tf ix}} + \definemathcommand [unic@romannumeralten] {{\tf x}} + \definemathcommand [unic@romannumeraleleven] {{\tf xi}} + \definemathcommand [unic@romannumeraltwelve] {{\tf xii}} + \definemathcommand [unic@romannumeralfifty] {{\tf l}} + \definemathcommand [unic@romannumeralonehundred] {{\tf c}} + \definemathcommand [unic@romannumeralfivehundred] {{\tf d}} + \definemathcommand [unic@romannumeralonethousand] {{\tf m}} + \definemathcommand [unic@romannumeralonethousandCD] {\unknownchar} + \definemathcommand [unic@romannumeralfivethousand] {\unknownchar} + \definemathcommand [unic@romannumeraltenthousand] {\unknownchar} + \definemathcommand [unic@romannumeralreversedonehundred] {\mirror{C}} + +\stopmathcollection + +% needed for unic-033 % NW + +\startmathcollection[default] + + \definemathcommand [unic@leftarrowtobar] {\mapstochar\leftarrow} + +\stopmathcollection + +% needed for unic-034 % NW + +\startmathcollection[default] + + \definemathcommand [unic@in] {\in} + \definemathcommand [unic@nin] {\not\in} + \definemathcommand [unic@nni] {\not\ni} + \definemathcommand [unic@minus] {-} + \definemathcommand [unic@divisionslash] {/} + \definemathcommand [unic@sqrt] {\sqrt{}} + \definemathcommand [unic@cubesqrt] {\root 3 \of {}} + \definemathcommand [unic@fourthsqrt] {\root 4 \of {}} + \definemathcommand [unic@divides] [op] {|} % TODO + \definemathcommand [unic@ndivides] [rel] {\mathop{\not|}} % TODO: horrible + \definemathcommand [unic@cap] {\cap} + \definemathcommand [unic@dblint] {\int\!\!\!\int} + \definemathcommand [unic@triint] {\int\!\!\!\int\!\!\!\int} + \definemathcommand [unic@ratio] [rel] {:} + \definemathcommand [unic@proportion] [rel] {::} + \definemathcommand [unic@excess] [op] {-\!\!:} + \definemathcommand [unic@geomprop] [op] {:\!\!\!-\!\!\!:} + \definemathcommand [unic@homothetic] [op] {\sim\!\!\!:} + \definemathcommand [unic@nsimeq] {\not\simeq} + \definemathcommand [unic@cong] {\cong} + \definemathcommand [unic@napproxeq] {\not\approxeq} + \definemathcommand [unic@napprox] {\not\approx} + \definemathcommand [unic@doteq] {\doteq} + \definemathcommand [unic@correspondsto] {\buildrel \frown \over =} + \definemathcommand [unic@estimates] {\buildrel \wedge \over =} + \definemathcommand [unic@equiangularto] {\buildrel \vee \over =} + \definemathcommand [unic@stareq] {\buildrel \star \over =} + \definemathcommand [unic@eqbydef] {\buildrel \rm def \over =} + \definemathcommand [unic@measuredby] {\buildrel \rm m \over =} + \definemathcommand [unic@questionedeq] {\buildrel \rm ? \over =} + \definemathcommand [unic@nequiv] {\not\equiv} + \definemathcommand [unic@nasymp] {\not\asymp} + \definemathcommand [unic@nlesssim] {\not\lesssim} + \definemathcommand [unic@ngtrsim] {\not\gtrsim} + \definemathcommand [unic@nlessgtr] {\not\lessgtr} + \definemathcommand [unic@ngtrless] {\not\gtrless} + \definemathcommand [unic@nsubset] {\not\subset} + \definemathcommand [unic@nsupset] {\not\supset} + \definemathcommand [unic@nsqsubseteq] {\not\sqsubseteq} + \definemathcommand [unic@nsqsupseteq] {\not\sqsupseteq} + +\stopmathcollection + +% needed for unic-039 % NW + +\startmathcollection[default] + + \definemathcommand [unic@Lbracket] [open] {[\![} + \definemathcommand [unic@Rbracket] [close] {]\!]} + \definemathcommand [unic@Langle] [open] {\langle\!\langle} + \definemathcommand [unic@Rangle] [close] {\rangle\!\rangle} + +\stopmathcollection + +\protect \endinput diff --git a/tex/context/base/math-vfu.lua b/tex/context/base/math-vfu.lua new file mode 100644 index 000000000..5023e6b4d --- /dev/null +++ b/tex/context/base/math-vfu.lua @@ -0,0 +1,1379 @@ +if not modules then modules = { } end modules ['math-vfu'] = { + version = 1.001, + comment = "companion to math-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- All these math vectors .. thanks to Aditya and Mojca they become +-- better and better. If you have problems with math fonts or miss +-- characters report it to the ConTeXt mailing list. + +local type, next = type, next + +local trace_virtual = false trackers.register("math.virtual", function(v) trace_virtual = v end) +local trace_timings = false trackers.register("math.timings", function(v) trace_timings = v end) + +fonts.enc.math = fonts.enc.math or { } + +local shared = { } + +fonts.vf.math = fonts.vf.math or { } +fonts.vf.math.optional = false + +local push, pop, back = { "push" }, { "pop" }, { "slot", 1, 0x2215 } + +local function negate(main,characters,id,size,unicode,basecode) + if not characters[unicode] then + local basechar = characters[basecode] + if basechar then + local ht, wd = basechar.height, basechar.width + characters[unicode] = { + width = wd, + height = ht, + depth = basechar.depth, + italic = basechar.italic, + kerns = basechar.kerns, + commands = { + { "slot", 1, basecode }, + push, + { "down", ht/5}, + { "right", - wd/2}, + back, + push, + } + } + end + end +end + +--~ \Umathchardef\braceld="0 "1 "FF07A +--~ \Umathchardef\bracerd="0 "1 "FF07B +--~ \Umathchardef\bracelu="0 "1 "FF07C +--~ \Umathchardef\braceru="0 "1 "FF07D + +local function brace(main,characters,id,size,unicode,first,rule,left,right,rule,last) + if not characters[unicode] then + characters[unicode] = { + horiz_variants = { + { extender = 0, glyph = first }, + { extender = 1, glyph = rule }, + { extender = 0, glyph = left }, + { extender = 0, glyph = right }, + { extender = 1, glyph = rule }, + { extender = 0, glyph = last }, + } + } + end +end + +local function arrow(main,characters,id,size,unicode,arrow,minus,isleft) + if characters[unicode] then + if isleft then + t = { + { extender = 0, glyph = arrow }, + { extender = 1, glyph = minus }, + } + else + t = { + { extender = 0, glyph = minus }, + { extender = 1, glyph = arrow }, + } + end + --~ main.characters[unicode] = { horiz_variants = t } + characters[unicode].horiz_variants = t + end +end + +local function parent(main,characters,id,size,unicode,first,rule,last) + if not characters[unicode] then + characters[unicode] = { + horiz_variants = { + { extender = 0, glyph = first }, + { extender = 1, glyph = rule }, + { extender = 0, glyph = last }, + } + } + end +end + +local push, pop, step = { "push" }, { "pop" }, 0.2 -- 0.1 is nicer but gives larger files + +local function make(main,characters,id,size,n,m) + local old = 0xFF000+n + local c = characters[old] + if c then + local upslot, dnslot, uprule, dnrule = 0xFF100+n, 0xFF200+n, 0xFF300+m, 0xFF400+m + local xu = main.parameters.x_height + 0.3*size + local xd = 0.3*size + local w, h, d = c.width, c.height, c.depth + local thickness = h - d + local rulewidth = step*size -- we could use an overlap + local slot = { "slot", id, old } + local rule = { "rule", thickness, rulewidth } + local up = { "down", -xu } + local dn = { "down", xd } + local ht, dp = xu + 3*thickness, 0 + if not characters[uprule] then + characters[uprule] = { width = rulewidth, height = ht, depth = dp, commands = { push, up, rule, pop } } + end + characters[upslot] = { width = w, height = ht, depth = dp, commands = { push, up, slot, pop } } + local ht, dp = 0, xd + 3*thickness + if not characters[dnrule] then + characters[dnrule] = { width = rulewidth, height = ht, depth = dp, commands = { push, dn, rule, pop } } + end + characters[dnslot] = { width = w, height = ht, depth = dp, commands = { push, dn, slot, pop } } + end +end + +local function minus(main,characters,id,size,unicode) + local minus = characters[0x002D] + if minus then + local mu = size/18 + local width = minus.width - 5*mu + characters[unicode] = { + width = width, height = minus.height, depth = minus.depth, + commands = { push, { "right", -3*mu }, { "slot", id, 0x002D }, pop } + } + end +end + +local function dots(main,characters,id,size,unicode) + local c = characters[0x002E] + if c then + local w, h, d = c.width, c.height, c.depth + local mu = size/18 + local right3mu = { "right", 3*mu } + local right1mu = { "right", 1*mu } + local up1size = { "down", -.1*size } + local up4size = { "down", -.4*size } + local up7size = { "down", -.7*size } + local right2muw = { "right", 2*mu + w } + local slot = { "slot", id, 0x002E } + if unicode == 0x22EF then + local c = characters[0x022C5] + if c then + local w, h, d = c.width, c.height, c.depth + local slot = { "slot", id, 0x022C5 } + characters[unicode] = { + width = 3*w + 2*3*mu, height = h, depth = d, + commands = { push, slot, right3mu, slot, right3mu, slot, pop } + } + end + elseif unicode == 0x22EE then + -- weird height ! + characters[unicode] = { + width = w, height = h+(1.4)*size, depth = 0, + commands = { push, push, slot, pop, up4size, push, slot, pop, up4size, slot, pop } + } + elseif unicode == 0x22F1 then + characters[unicode] = { + width = 3*w + 6*size/18, height = 1.5*size, depth = 0, + commands = { + push, + right1mu, + push, up7size, slot, pop, + right2muw, + push, up4size, slot, pop, + right2muw, + push, up1size, slot, pop, + right1mu, + pop + } + } + elseif unicode == 0x22F0 then + characters[unicode] = { + width = 3*w + 6*size/18, height = 1.5*size, depth = 0, + commands = { + push, + right1mu, + push, up1size, slot, pop, + right2muw, + push, up4size, slot, pop, + right2muw, + push, up7size, slot, pop, + right1mu, + pop + } + } + else + characters[unicode] = { + width = 3*w + 2*3*mu, height = h, depth = d, + commands = { push, slot, right3mu, slot, right3mu, slot, pop } + } + end + end +end + +local function vertbar(main,characters,id,size,parent,scale,unicode) + local cp = characters[parent] + if cp then + local sc = scale * size + local pc = { "slot", id, parent } + characters[unicode] = { + width = cp.width, + height = cp.height + sc, + depth = cp.depth + sc, + commands = { + push, { "down", -sc }, pc, pop, + push, { "down", sc }, pc, pop, + pc, + }, + next = cp.next -- can be extensible + } + cp.next = unicode + end +end + +function fonts.vf.math.alas(main,id,size) + local characters = main.characters + for i=0x7A,0x7D do + make(main,characters,id,size,i,1) + end + brace (main,characters,id,size,0x23DE,0xFF17A,0xFF301,0xFF17D,0xFF17C,0xFF301,0xFF17B) + brace (main,characters,id,size,0x23DF,0xFF27C,0xFF401,0xFF27B,0xFF27A,0xFF401,0xFF27D) + parent (main,characters,id,size,0x23DC,0xFF17A,0xFF301,0xFF17B) + parent (main,characters,id,size,0x23DD,0xFF27C,0xFF401,0xFF27D) + negate (main,characters,id,size,0x2260,0x003D) + dots (main,characters,id,size,0x2026) -- ldots + dots (main,characters,id,size,0x22EE) -- vdots + dots (main,characters,id,size,0x22EF) -- cdots + dots (main,characters,id,size,0x22F1) -- ddots + dots (main,characters,id,size,0x22F0) -- udots + minus (main,characters,id,size,0xFF501) + arrow (main,characters,id,size,0x2190,0xFE190,0xFF501,true) -- left + arrow (main,characters,id,size,0x2192,0xFE192,0xFF501,false) -- right + vertbar(main,characters,id,size,0x0007C,0.10,0xFF601) -- big : 0.85 bodyfontsize + vertbar(main,characters,id,size,0xFF601,0.30,0xFF602) -- Big : 1.15 bodyfontsize + vertbar(main,characters,id,size,0xFF602,0.30,0xFF603) -- bigg : 1.45 bodyfontsize + vertbar(main,characters,id,size,0xFF603,0.30,0xFF604) -- Bigg : 1.75 bodyfontsize + vertbar(main,characters,id,size,0x02225,0.10,0xFF605) + vertbar(main,characters,id,size,0xFF605,0.30,0xFF606) + vertbar(main,characters,id,size,0xFF606,0.30,0xFF607) + vertbar(main,characters,id,size,0xFF607,0.30,0xFF608) +end + +local unique = 0 -- testcase: \startTEXpage \math{!\text{-}\text{-}\text{-}} \stopTEXpage + +function fonts.basecopy(tfmtable,name) + local characters, parameters, fullname = tfmtable.characters, tfmtable.parameters, tfmtable.fullname + local t, c, p = { }, { }, { } + for k, v in next, tfmtable do + t[k] = v + end + if characters then + for k, v in next, characters do + c[k] = v + end + t.characters = c + else + logs.report("math virtual","font %s has no characters",name) + end + if parameters then + for k, v in next, parameters do + p[k] = v + end + t.parameters = p + else + logs.report("math virtual","font %s has no parameters",name) + end + -- tricky ... what if fullname does not exist + if fullname then + unique = unique + 1 + t.fullname = fullname .. "-" .. unique + end + return t +end + +local reported = { } +local reverse -- index -> unicode + +function fonts.vf.math.define(specification,set) + if not reverse then + reverse = { } + for k, v in next, fonts.enc.math do + local r = { } + for u, i in next, v do + r[i] = u + end + reverse[k] = r + end + end + local name = specification.name -- symbolic name + local size = specification.size -- given size + local fnt, lst, main = { }, { }, nil + local start = (trace_virtual or trace_timings) and os.clock() + local okset, n = { }, 0 + for s=1,#set do + local ss = set[s] + local ssname = ss.name + if ss.optional and fonts.vf.math.optional then + if trace_virtual then + logs.report("math virtual","loading font %s subfont %s with name %s at %s is skipped",name,s,ssname,size) + end + else + if ss.features then ssname = ssname .. "*" .. ss.features end + if ss.main then main = s end + local f, id = fonts.tfm.read_and_define(ssname,size) + if not f then + logs.report("math virtual","loading font %s subfont %s with name %s at %s is skipped, not found",name,s,ssname,size) + else + n = n + 1 + okset[n] = ss + fnt[n] = f + lst[n] = { id = id, size = size } + if not shared[s] then shared[n] = { } end + if trace_virtual then + logs.report("math virtual","loading font %s subfont %s with name %s at %s as id %s using encoding %s",name,s,ssname,size,id,ss.vector or "none") + end + end + end + end + -- beware, fnt[1] is already passed to tex (we need to make a simple copy then .. todo) + main = fonts.basecopy(fnt[1],name) + main.name, main.fonts, main.virtualized, main.math_parameters = name, lst, true, { } + local characters, descriptions = main.characters, main.descriptions + local mp = main.parameters + if mp then + mp.x_height = mp.x_height or 0 + end + local already_reported = false + for s=1,n do + local ss, fs = okset[s], fnt[s] + if not fs then + -- skip, error + elseif ss.optional and fonts.vf.math.optional then + -- skip, redundant + else + local mm, fp = main.math_parameters, fs.parameters + if mm and fp and mp then + if ss.extension then + mm.math_x_height = fp.x_height or 0 -- math_x_height height of x + mm.default_rule_thickness = fp[ 8] or 0 -- default_rule_thickness thickness of \over bars + mm.big_op_spacing1 = fp[ 9] or 0 -- big_op_spacing1 minimum clearance above a displayed op + mm.big_op_spacing2 = fp[10] or 0 -- big_op_spacing2 minimum clearance below a displayed op + mm.big_op_spacing3 = fp[11] or 0 -- big_op_spacing3 minimum baselineskip above displayed op + mm.big_op_spacing4 = fp[12] or 0 -- big_op_spacing4 minimum baselineskip below displayed op + mm.big_op_spacing5 = fp[13] or 0 -- big_op_spacing5 padding above and below displayed limits + -- logs.report("math virtual","loading and virtualizing font %s at size %s, setting ex parameters",name,size) + elseif ss.parameters then + mp.x_height = fp.x_height or mp.x_height + mm.x_height = mm.x_height or fp.x_height or 0 -- x_height height of x + mm.num1 = fp[ 8] or 0 -- num1 numerator shift-up in display styles + mm.num2 = fp[ 9] or 0 -- num2 numerator shift-up in non-display, non-\atop + mm.num3 = fp[10] or 0 -- num3 numerator shift-up in non-display \atop + mm.denom1 = fp[11] or 0 -- denom1 denominator shift-down in display styles + mm.denom2 = fp[12] or 0 -- denom2 denominator shift-down in non-display styles + mm.sup1 = fp[13] or 0 -- sup1 superscript shift-up in uncramped display style + mm.sup2 = fp[14] or 0 -- sup2 superscript shift-up in uncramped non-display + mm.sup3 = fp[15] or 0 -- sup3 superscript shift-up in cramped styles + mm.sub1 = fp[16] or 0 -- sub1 subscript shift-down if superscript is absent + mm.sub2 = fp[17] or 0 -- sub2 subscript shift-down if superscript is present + mm.sup_drop = fp[18] or 0 -- sup_drop superscript baseline below top of large box + mm.sub_drop = fp[19] or 0 -- sub_drop subscript baseline below bottom of large box + mm.delim1 = fp[20] or 0 -- delim1 size of \atopwithdelims delimiters in display styles + mm.delim2 = fp[21] or 0 -- delim2 size of \atopwithdelims delimiters in non-displays + mm.axis_height = fp[22] or 0 -- axis_height height of fraction lines above the baseline + -- logs.report("math virtual","loading and virtualizing font %s at size %s, setting sy parameters",name,size) + end + else + logs.report("math virtual","font %s, no parameters set",name) + end + local vectorname = ss.vector + if vectorname then + local offset = 0xFF000 + local vector = fonts.enc.math[vectorname] + local rotcev = reverse[vectorname] + if vector then + local fc, fd, si = fs.characters, fs.descriptions, shared[s] + local skewchar = ss.skewchar + for unicode, index in next, vector do + local fci = fc[index] + if not fci then + local fontname = fs.name or "unknown" + local rf = reported[fontname] + if not rf then rf = { } reported[fontname] = rf end + local rv = rf[vectorname] + if not rv then rv = { } rf[vectorname] = rv end + local ru = rv[unicode] + if not ru then + if trace_virtual then + logs.report("math virtual", "unicode point U+%05X has no index %04X in vector %s for font %s",unicode,index,vectorname,fontname) + elseif not already_reported then + logs.report("math virtual", "the mapping is incomplete for '%s' at %s",name,number.topoints(size)) + already_reported = true + end + rv[unicode] = true + end + else + local ref = si[index] + if not ref then + ref = { { 'slot', s, index } } + si[index] = ref + end + local kerns = fci.kerns + if kerns then + local width = fci.width + local krn = { } + for k=1,#kerns do + local rk = rotcev[k] + if rk then + krn[rk] = kerns[k] + end + end + if not next(krn) then + krn = nil + end + local t = { + width = width, + height = fci.height, + depth = fci.depth, + italic = fci.italic, + kerns = krn, + commands = ref, + } + if skewchar and kerns then + local k = kerns[skewchar] + if k then + t.top_accent = width/2 + k + end + end + characters[unicode] = t + else + characters[unicode] = { + width = fci.width, + height = fci.height, + depth = fci.depth, + italic = fci.italic, + commands = ref, + } + end + end + end + if ss.extension then + -- todo: if multiple ex, then 256 offsets per instance + local extension = fonts.enc.math["large-to-small"] + local variants_done = fs.variants_done + for index, fci in next, fc do -- the raw ex file + if type(index) == "number" then + local ref = si[index] + if not ref then + ref = { { 'slot', s, index } } + si[index] = ref + end + local t = { + width = fci.width, + height = fci.height, + depth = fci.depth, + italic = fci.italic, + commands = ref, + } + local n = fci.next + if n then + t.next = offset + n + elseif variants_done then + local vv = fci.vert_variants + if vv then + t.vert_variants = vv + end + local hv = fci.horiz_variants + if hv then + t.horiz_variants = hv + end + else + local vv = fci.vert_variants + if vv then + for i=1,#vv do + local vvi = vv[i] + vvi.glyph = vvi.glyph + offset + end + t.vert_variants = vv + end + local hv = fci.horiz_variants + if hv then + for i=1,#hv do + local hvi = hv[i] + hvi.glyph = hvi.glyph + offset + end + t.horiz_variants = hv + end + end + characters[offset + index] = t + end + end + fs.variants_done = true + for unicode, index in next, extension do + local cu = characters[unicode] + if cu then + cu.next = offset + index + --~ local n, c, d = unicode, cu, { } + --~ print("START", unicode) + --~ while n do + --~ n = c.next + --~ if n then + --~ print("NEXT", n) + --~ c = characters[n] + --~ if not c then + --~ print("EXIT") + --~ elseif d[n] then + --~ print("LOOP") + --~ break + --~ end + --~ d[n] = true + --~ end + --~ end + else + local fci = fc[index] + if not fci then +--~ characters[unicode] = { +--~ width = 0, +--~ height = 0, +--~ depth = 0, +--~ index = 0, +--~ } + else + local ref = si[index] + if not ref then + ref = { { 'slot', s, index } } + si[index] = ref + end + local kerns = fci.kerns + if kerns then + local krn = { } + for k=1,#kerns do + krn[offset + k] = kerns[k] + end + characters[unicode] = { + width = fci.width, + height = fci.height, + depth = fci.depth, + italic = fci.italic, + commands = ref, + kerns = krn, + next = offset + index, + } + else + characters[unicode] = { + width = fci.width, + height = fci.height, + depth = fci.depth, + italic = fci.italic, + commands = ref, + next = offset + index, + } + end + end + end + end + end + end + end + mathematics.extras.copy(main) --not needed here (yet) + end + end + lst[#lst+1] = { id = font.nextid(), size = size } + if mp then -- weak catch + fonts.vf.math.alas(main,#lst,size) + end + if trace_virtual or trace_timings then + logs.report("math virtual","loading and virtualizing font %s at size %s took %0.3f seconds",name,size,os.clock()-start) + end + main.has_italic = true + main.type = "virtual" -- not needed + mathematics.scaleparameters(main,main,1) + main.nomath = false +--~ print(table.serialize(characters[0x222B])) +--~ print(main.fontname,table.serialize(main.MathConstants)) + return main +end + +function mathematics.make_font(name, set) + fonts.define.methods[name] = function(specification) + return fonts.vf.math.define(specification,set) + end +end + +-- varphi is part of the alphabet, contrary to the other var*s' + +fonts.enc.math["large-to-small"] = { + [0x00028] = 0x00, -- ( + [0x00029] = 0x01, -- ) + [0x0005B] = 0x02, -- [ + [0x0005D] = 0x03, -- ] + [0x0230A] = 0x04, -- lfloor + [0x0230B] = 0x05, -- rfloor + [0x02308] = 0x06, -- lceil + [0x02309] = 0x07, -- rceil + [0x0007B] = 0x08, -- { + [0x0007D] = 0x09, -- } + [0x027E8] = 0x0A, -- < + [0x027E9] = 0x0B, -- > + [0x0007C] = 0x0C, -- | +--~ [0x0] = 0x0D, -- lVert rVert Vert +-- [0x0002F] = 0x0E, -- / + [0x0005C] = 0x0F, -- \ +--~ [0x0] = 0x3A, -- lgroup +--~ [0x0] = 0x3B, -- rgroup +--~ [0x0] = 0x3C, -- arrowvert +--~ [0x0] = 0x3D, -- Arrowvert + [0x02195] = 0x3F, -- updownarrow +--~ [0x0] = 0x40, -- lmoustache +--~ [0x0] = 0x41, -- rmoustache + [0x0221A] = 0x70, -- sqrt + [0x021D5] = 0x77, -- Updownarrow + [0x02191] = 0x78, -- uparrow + [0x02193] = 0x79, -- downarrow + [0x021D1] = 0x7E, -- Uparrow + [0x021D3] = 0x7F, -- Downarrow + [0x0220F] = 0x59, -- prod + [0x02210] = 0x61, -- coprod + [0x02211] = 0x58, -- sum + [0x0222B] = 0x5A, -- intop + [0x0222E] = 0x49, -- ointop + [0xFE302] = 0x62, -- widehat + [0xFE303] = 0x65, -- widetilde + [0x022C0] = 0x5E, -- bigwedge + [0x022C1] = 0x5F, -- bigvee + [0x022C2] = 0x5C, -- bigcap + [0x022C3] = 0x5B, -- bigcup + [0x02044] = 0x0E, -- / +} + +fonts.enc.math["tex-ex"] = { + [0x0220F] = 0x51, -- prod + [0x0222B] = 0x52, -- intop + [0x02210] = 0x60, -- coprod + [0x02211] = 0x50, -- sum + [0x022C0] = 0x56, -- bigwedge + [0x022C1] = 0x57, -- bigvee + [0x022C2] = 0x54, -- bigcap + [0x022C3] = 0x53, -- bigcup + [0x02A04] = 0x55, -- biguplus + [0x02A02] = 0x4E, -- bigotimes + [0x02A01] = 0x4C, -- bigoplus + [0x02A03] = 0x4A, -- bigodot + [0x0222E] = 0x48, -- ointop + [0x02A06] = 0x46, -- bigsqcup +} + +-- only math stuff is needed, since we always use an lm or gyre +-- font as main font + +fonts.enc.math["tex-mr"] = { + [0x00393] = 0x00, -- Gamma + [0x00394] = 0x01, -- Delta + [0x00398] = 0x02, -- Theta + [0x0039B] = 0x03, -- Lambda + [0x0039E] = 0x04, -- Xi + [0x003A0] = 0x05, -- Pi + [0x003A3] = 0x06, -- Sigma + [0x003A5] = 0x07, -- Upsilon + [0x003A6] = 0x08, -- Phi + [0x003A8] = 0x09, -- Psi + [0x003A9] = 0x0A, -- Omega +-- [0x00060] = 0x12, -- [math]grave +-- [0x000B4] = 0x13, -- [math]acute +-- [0x002C7] = 0x14, -- [math]check +-- [0x002D8] = 0x15, -- [math]breve +-- [0x000AF] = 0x16, -- [math]bar +-- [0x00021] = 0x21, -- ! +-- [0x00028] = 0x28, -- ( +-- [0x00029] = 0x29, -- ) +-- [0x0002B] = 0x2B, -- + +-- [0x0002F] = 0x2F, -- / +-- [0x0003A] = 0x3A, -- : +-- [0x02236] = 0x3A, -- colon +-- [0x0003B] = 0x3B, -- ; +-- [0x0003C] = 0x3C, -- < +-- [0x0003D] = 0x3D, -- = +-- [0x0003E] = 0x3E, -- > +-- [0x0003F] = 0x3F, -- ? + [0x00391] = 0x41, -- Alpha + [0x00392] = 0x42, -- Beta + [0x02145] = 0x44, + [0x00395] = 0x45, -- Epsilon + [0x00397] = 0x48, -- Eta + [0x00399] = 0x49, -- Iota + [0x0039A] = 0x4B, -- Kappa + [0x0039C] = 0x4D, -- Mu + [0x0039D] = 0x4E, -- Nu + [0x0039F] = 0x4F, -- Omicron + [0x003A1] = 0x52, -- Rho + [0x003A4] = 0x54, -- Tau + [0x003A7] = 0x58, -- Chi + [0x00396] = 0x5A, -- Zeta +-- [0x0005B] = 0x5B, -- [ +-- [0x0005D] = 0x5D, -- ] +-- [0x0005E] = 0x5E, -- [math]hat -- the text one + [0x00302] = 0x5E, -- [math]hat -- the real math one +-- [0x002D9] = 0x5F, -- [math]dot + [0x02146] = 0x64, + [0x02147] = 0x65, +-- [0x002DC] = 0x7E, -- [math]tilde -- the text one + [0x00303] = 0x7E, -- [math]tilde -- the real one +-- [0x000A8] = 0x7F, -- [math]ddot +} + +fonts.enc.math["tex-mr-missing"] = { + [0x02236] = 0x3A, -- colon +} + +fonts.enc.math["tex-mi"] = { + [0x1D6E4] = 0x00, -- Gamma + [0x1D6E5] = 0x01, -- Delta + [0x1D6E9] = 0x02, -- Theta + [0x1D6F3] = 0x02, -- varTheta (not present in TeX) + [0x1D6EC] = 0x03, -- Lambda + [0x1D6EF] = 0x04, -- Xi + [0x1D6F1] = 0x05, -- Pi + [0x1D6F4] = 0x06, -- Sigma + [0x1D6F6] = 0x07, -- Upsilon + [0x1D6F7] = 0x08, -- Phi + [0x1D6F9] = 0x09, -- Psi + [0x1D6FA] = 0x0A, -- Omega + [0x1D6FC] = 0x0B, -- alpha + [0x1D6FD] = 0x0C, -- beta + [0x1D6FE] = 0x0D, -- gamma + [0x1D6FF] = 0x0E, -- delta + [0x1D716] = 0x0F, -- epsilon TODO: 1D716 + [0x1D701] = 0x10, -- zeta + [0x1D702] = 0x11, -- eta + [0x1D703] = 0x12, -- theta TODO: 1D703 + [0x1D704] = 0x13, -- iota + [0x1D705] = 0x14, -- kappa + [0x1D718] = 0x14, -- varkappa, not in tex fonts + [0x1D706] = 0x15, -- lambda + [0x1D707] = 0x16, -- mu + [0x1D708] = 0x17, -- nu + [0x1D709] = 0x18, -- xi + [0x1D70B] = 0x19, -- pi + [0x1D70C] = 0x1A, -- rho + [0x1D70E] = 0x1B, -- sigma + [0x1D70F] = 0x1C, -- tau + [0x1D710] = 0x1D, -- upsilon + [0x1D719] = 0x1E, -- phi + [0x1D712] = 0x1F, -- chi + [0x1D713] = 0x20, -- psi + [0x1D714] = 0x21, -- omega + [0x1D700] = 0x22, -- varepsilon (the other way around) + [0x1D717] = 0x23, -- vartheta + [0x1D71B] = 0x24, -- varpi + [0x1D71A] = 0x25, -- varrho + [0x1D70D] = 0x26, -- varsigma + [0x1D711] = 0x27, -- varphi (the other way around) + [0x021BC] = 0x28, -- leftharpoonup + [0x021BD] = 0x29, -- leftharpoondown + [0x021C0] = 0x2A, -- rightharpoonup + [0x021C1] = 0x2B, -- rightharpoondown + [0xFE322] = 0x2C, -- lhook (hook for combining arrows) + [0xFE323] = 0x2D, -- rhook (hook for combining arrows) + [0x022B3] = 0x2E, -- triangleright (TODO: which one is right?) + [0x022B2] = 0x2F, -- triangleleft (TODO: which one is right?) +-- [0x00041] = 0x30, -- 0 +-- [0x00041] = 0x31, -- 1 +-- [0x00041] = 0x32, -- 2 +-- [0x00041] = 0x33, -- 3 +-- [0x00041] = 0x34, -- 4 +-- [0x00041] = 0x35, -- 5 +-- [0x00041] = 0x36, -- 6 +-- [0x00041] = 0x37, -- 7 +-- [0x00041] = 0x38, -- 8 +-- [0x00041] = 0x39, -- 9 +--~ [0x0002E] = 0x3A, -- . + [0x0002C] = 0x3B, -- , + [0x0003C] = 0x3C, -- < +-- [0x0002F] = 0x3D, -- /, slash, solidus + [0x02044] = 0x3D, -- / AM: Not sure + [0x0003E] = 0x3E, -- > + [0x022C6] = 0x3F, -- star + [0x02202] = 0x40, -- partial +-- + [0x0266D] = 0x5B, -- flat + [0x0266E] = 0x5C, -- natural + [0x0266F] = 0x5D, -- sharp + [0x02323] = 0x5E, -- smile + [0x02322] = 0x5F, -- frown + [0x02113] = 0x60, -- ell +-- + [0x1D6A4] = 0x7B, -- imath (TODO: also 0131) + [0x1D6A5] = 0x7C, -- jmath (TODO: also 0237) + [0x02118] = 0x7D, -- wp + [0x020D7] = 0x7E, -- vec (TODO: not sure) +-- 0x7F, -- (no idea what that could be) +} + + +fonts.enc.math["tex-it"] = { +-- [0x1D434] = 0x41, -- A + [0x1D6E2] = 0x41, -- Alpha +-- [0x1D435] = 0x42, -- B + [0x1D6E3] = 0x42, -- Beta +-- [0x1D436] = 0x43, -- C +-- [0x1D437] = 0x44, -- D +-- [0x1D438] = 0x45, -- E + [0x1D6E6] = 0x45, -- Epsilon +-- [0x1D439] = 0x46, -- F +-- [0x1D43A] = 0x47, -- G +-- [0x1D43B] = 0x48, -- H + [0x1D6E8] = 0x48, -- Eta +-- [0x1D43C] = 0x49, -- I + [0x1D6EA] = 0x49, -- Iota +-- [0x1D43D] = 0x4A, -- J +-- [0x1D43E] = 0x4B, -- K + [0x1D6EB] = 0x4B, -- Kappa +-- [0x1D43F] = 0x4C, -- L +-- [0x1D440] = 0x4D, -- M + [0x1D6ED] = 0x4D, -- Mu +-- [0x1D441] = 0x4E, -- N + [0x1D6EE] = 0x4E, -- Nu +-- [0x1D442] = 0x4F, -- O + [0x1D6F0] = 0x4F, -- Omicron +-- [0x1D443] = 0x50, -- P + [0x1D6F2] = 0x50, -- Rho +-- [0x1D444] = 0x51, -- Q +-- [0x1D445] = 0x52, -- R +-- [0x1D446] = 0x53, -- S +-- [0x1D447] = 0x54, -- T + [0x1D6F5] = 0x54, -- Tau +-- [0x1D448] = 0x55, -- U +-- [0x1D449] = 0x56, -- V +-- [0x1D44A] = 0x57, -- W +-- [0x1D44B] = 0x58, -- X + [0x1D6F8] = 0x58, -- Chi +-- [0x1D44C] = 0x59, -- Y +-- [0x1D44D] = 0x5A, -- Z +-- +-- [0x1D44E] = 0x61, -- a +-- [0x1D44F] = 0x62, -- b +-- [0x1D450] = 0x63, -- c +-- [0x1D451] = 0x64, -- d +-- [0x1D452] = 0x65, -- e +-- [0x1D453] = 0x66, -- f +-- [0x1D454] = 0x67, -- g +-- [0x1D455] = 0x68, -- h + [0x0210E] = 0x68, -- Planck constant (h) +-- [0x1D456] = 0x69, -- i +-- [0x1D457] = 0x6A, -- j +-- [0x1D458] = 0x6B, -- k +-- [0x1D459] = 0x6C, -- l +-- [0x1D45A] = 0x6D, -- m +-- [0x1D45B] = 0x6E, -- n +-- [0x1D45C] = 0x6F, -- o + [0x1D70A] = 0x6F, -- omicron +-- [0x1D45D] = 0x70, -- p +-- [0x1D45E] = 0x71, -- q +-- [0x1D45F] = 0x72, -- r +-- [0x1D460] = 0x73, -- s +-- [0x1D461] = 0x74, -- t +-- [0x1D462] = 0x75, -- u +-- [0x1D463] = 0x76, -- v +-- [0x1D464] = 0x77, -- w +-- [0x1D465] = 0x78, -- x +-- [0x1D466] = 0x79, -- y +-- [0x1D467] = 0x7A, -- z +} + +fonts.enc.math["tex-ss"] = { } +fonts.enc.math["tex-tt"] = { } +fonts.enc.math["tex-bf"] = { } +fonts.enc.math["tex-bi"] = { } +fonts.enc.math["tex-fraktur"] = { } +fonts.enc.math["tex-fraktur-bold"] = { } + +function fonts.vf.math.set_letters(font_encoding, name, uppercase, lowercase) + local enc = font_encoding[name] + for i = 0,25 do + enc[uppercase+i] = i + 0x41 + enc[lowercase+i] = i + 0x61 + end +end + +function fonts.vf.math.set_digits(font_encoding, name, digits) + local enc = font_encoding[name] + for i = 0,9 do + enc[digits+i] = i + 0x30 + end +end + +fonts.enc.math["tex-sy"] = { + [0x0002D] = 0x00, -- - + [0x02212] = 0x00, -- - +-- [0x02201] = 0x00, -- complement +-- [0x02206] = 0x00, -- increment +-- [0x02204] = 0x00, -- not exists +--~ [0x000B7] = 0x01, -- cdot + [0x022C5] = 0x01, -- cdot + [0x000D7] = 0x02, -- times + [0x0002A] = 0x03, -- * + [0x02217] = 0x03, -- * + [0x000F7] = 0x04, -- div + [0x022C4] = 0x05, -- diamond + [0x000B1] = 0x06, -- pm + [0x02213] = 0x07, -- mp + [0x02295] = 0x08, -- oplus + [0x02296] = 0x09, -- ominus + [0x02297] = 0x0A, -- otimes + [0x02298] = 0x0B, -- oslash + [0x02299] = 0x0C, -- odot + [0x025EF] = 0x0D, -- bigcirc, Orb (either 25EF or 25CB) -- todo + [0x02218] = 0x0E, -- circ + [0x02219] = 0x0F, -- bullet + [0x02022] = 0x0F, -- bullet + [0x0224D] = 0x10, -- asymp + [0x02261] = 0x11, -- equiv + [0x02286] = 0x12, -- subseteq + [0x02287] = 0x13, -- supseteq + [0x02264] = 0x14, -- leq + [0x02265] = 0x15, -- geq + [0x02AAF] = 0x16, -- preceq +-- [0x0227C] = 0x16, -- preceq, AM:No see 2AAF + [0x02AB0] = 0x17, -- succeq +-- [0x0227D] = 0x17, -- succeq, AM:No see 2AB0 + [0x0223C] = 0x18, -- sim + [0x02248] = 0x19, -- approx + [0x02282] = 0x1A, -- subset + [0x02283] = 0x1B, -- supset + [0x0226A] = 0x1C, -- ll + [0x0226B] = 0x1D, -- gg + [0x0227A] = 0x1E, -- prec + [0x0227B] = 0x1F, -- succ + [0x02190] = 0x20, -- leftarrow + [0x02192] = 0x21, -- rightarrow +--~ [0xFE190] = 0x20, -- leftarrow +--~ [0xFE192] = 0x21, -- rightarrow + [0x02191] = 0x22, -- uparrow + [0x02193] = 0x23, -- downarrow + [0x02194] = 0x24, -- leftrightarrow + [0x02197] = 0x25, -- nearrow + [0x02198] = 0x26, -- searrow + [0x02243] = 0x27, -- simeq + [0x021D0] = 0x28, -- Leftarrow + [0x021D2] = 0x29, -- Rightarrow + [0x021D1] = 0x2A, -- Uparrow + [0x021D3] = 0x2B, -- Downarrow + [0x021D4] = 0x2C, -- Leftrightarrow + [0x02196] = 0x2D, -- nwarrow + [0x02199] = 0x2E, -- swarrow + [0x0221D] = 0x2F, -- propto + [0x02032] = 0x30, -- prime + [0x0221E] = 0x31, -- infty + [0x02208] = 0x32, -- in + [0x0220B] = 0x33, -- ni + [0x025B3] = 0x34, -- triangle, bigtriangleup + [0x025BD] = 0x35, -- bigtriangledown + [0x00338] = 0x36, -- not +-- 0x37, -- (beginning of arrow) + [0x02200] = 0x38, -- forall + [0x02203] = 0x39, -- exists + [0x000AC] = 0x3A, -- neg, lnot + [0x02205] = 0x3B, -- empty set + [0x0211C] = 0x3C, -- Re + [0x02111] = 0x3D, -- Im + [0x022A4] = 0x3E, -- top + [0x022A5] = 0x3F, -- bot, perp + [0x02135] = 0x40, -- aleph + [0x1D49C] = 0x41, -- script A + [0x0212C] = 0x42, -- script B + [0x1D49E] = 0x43, -- script C + [0x1D49F] = 0x44, -- script D + [0x02130] = 0x45, -- script E + [0x02131] = 0x46, -- script F + [0x1D4A2] = 0x47, -- script G + [0x0210B] = 0x48, -- script H + [0x02110] = 0x49, -- script I + [0x1D4A5] = 0x4A, -- script J + [0x1D4A6] = 0x4B, -- script K + [0x02112] = 0x4C, -- script L + [0x02133] = 0x4D, -- script M + [0x1D4A9] = 0x4E, -- script N + [0x1D4AA] = 0x4F, -- script O + [0x1D4AB] = 0x50, -- script P + [0x1D4AC] = 0x51, -- script Q + [0x0211B] = 0x52, -- script R + [0x1D4AE] = 0x53, -- script S + [0x1D4AF] = 0x54, -- script T + [0x1D4B0] = 0x55, -- script U + [0x1D4B1] = 0x56, -- script V + [0x1D4B2] = 0x57, -- script W + [0x1D4B3] = 0x58, -- script X + [0x1D4B4] = 0x59, -- script Y + [0x1D4B5] = 0x5A, -- script Z + [0x0222A] = 0x5B, -- cup + [0x02229] = 0x5C, -- cap + [0x0228E] = 0x5D, -- uplus + [0x02227] = 0x5E, -- wedge, land + [0x02228] = 0x5F, -- vee, lor + [0x022A2] = 0x60, -- vdash + [0x022A3] = 0x61, -- dashv + [0x0230A] = 0x62, -- lfloor + [0x0230B] = 0x63, -- rfloor + [0x02308] = 0x64, -- lceil + [0x02309] = 0x65, -- rceil + [0x0007B] = 0x66, -- {, lbrace + [0x0007D] = 0x67, -- }, rbrace + [0x027E8] = 0x68, -- <, langle + [0x027E9] = 0x69, -- >, rangle + [0x0007C] = 0x6A, -- |, mid, lvert, rvert + [0x02225] = 0x6B, -- parallel, Vert, lVert, rVert, arrowvert + [0x02195] = 0x6C, -- updownarrow + [0x021D5] = 0x6D, -- Updownarrow + [0x0005C] = 0x6E, -- \, backslash, setminus + [0x02216] = 0x6E, -- setminus + [0x02240] = 0x6F, -- wr + [0x0221A] = 0x70, -- sqrt. AM: Check surd?? + [0x02A3F] = 0x71, -- amalg + [0x1D6FB] = 0x72, -- nabla +-- [0x0222B] = 0x73, -- smallint (TODO: what about intop?) + [0x02294] = 0x74, -- sqcup + [0x02293] = 0x75, -- sqcap + [0x02291] = 0x76, -- sqsubseteq + [0x02292] = 0x77, -- sqsupseteq + [0x000A7] = 0x78, -- S + [0x02020] = 0x79, -- dagger, dag + [0x02021] = 0x7A, -- ddagger, ddag + [0x000B6] = 0x7B, -- P + [0x02663] = 0x7C, -- clubsuit + [0x02662] = 0x7D, -- diamondsuit + [0x02661] = 0x7E, -- heartsuit + [0x02660] = 0x7F, -- spadesuit + [0xFE321] = 0x37, -- mapstochar +} + +-- The names in masm10.enc can be trusted best and are shown in the first +-- column, while in the second column we show the tex/ams names. As usual +-- it costs hours to figure out such a table. + +fonts.enc.math["tex-ma"] = { + [0x022A1] = 0x00, -- squaredot \boxdot + [0x0229E] = 0x01, -- squareplus \boxplus + [0x022A0] = 0x02, -- squaremultiply \boxtimes + [0x025A1] = 0x03, -- square \square \Box + [0x025A0] = 0x04, -- squaresolid \blacksquare + [0x000B7] = 0x05, -- squaresmallsolid \centerdot + [0x022C4] = 0x06, -- diamond \Diamond \lozenge + [0x029EB] = 0x07, -- diamondsolid \blacklozenge + [0x021BA] = 0x08, -- clockwise \circlearrowright + [0x021BB] = 0x09, -- anticlockwise \circlearrowleft + [0x021CC] = 0x0A, -- harpoonleftright \rightleftharpoons + [0x021CB] = 0x0B, -- harpoonrightleft \leftrightharpoons + [0x0229F] = 0x0C, -- squareminus \boxminus + [0x022A9] = 0x0D, -- forces \Vdash + [0x022AA] = 0x0E, -- forcesbar \Vvdash + [0x022A8] = 0x0F, -- satisfies \vDash + [0x021A0] = 0x10, -- dblarrowheadright \twoheadrightarrow + [0x0219E] = 0x11, -- dblarrowheadleft \twoheadleftarrow + [0x021C7] = 0x12, -- dblarrowleft \leftleftarrows + [0x021C9] = 0x13, -- dblarrowright \rightrightarrows + [0x021C8] = 0x14, -- dblarrowup \upuparrows + [0x021CA] = 0x15, -- dblarrowdwn \downdownarrows + [0x021BE] = 0x16, -- harpoonupright \upharpoonright \restriction + [0x021C2] = 0x17, -- harpoondownright \downharpoonright + [0x021BF] = 0x18, -- harpoonupleft \upharpoonleft + [0x021C3] = 0x19, -- harpoondownleft \downharpoonleft + [0x021A3] = 0x1A, -- arrowtailright \rightarrowtail + [0x021A2] = 0x1B, -- arrowtailleft \leftarrowtail + [0x021C6] = 0x1C, -- arrowparrleftright \leftrightarrows +-- [0x021C5] = 0x00, -- \updownarrows (missing in lm) + [0x021C4] = 0x1D, -- arrowparrrightleft \rightleftarrows + [0x021B0] = 0x1E, -- shiftleft \Lsh + [0x021B1] = 0x1F, -- shiftright \Rsh + [0x021DD] = 0x20, -- squiggleright \leadsto \rightsquigarrow + [0x021AD] = 0x21, -- squiggleleftright \leftrightsquigarrow + [0x021AB] = 0x22, -- curlyleft \looparrowleft + [0x021AC] = 0x23, -- curlyright \looparrowright + [0x02257] = 0x24, -- circleequal \circeq + [0x0227F] = 0x25, -- followsorequal \succsim + [0x02273] = 0x26, -- greaterorsimilar \gtrsim + [0x02A86] = 0x27, -- greaterorapproxeql \gtrapprox + [0x022B8] = 0x28, -- multimap \multimap + [0x02234] = 0x29, -- therefore \therefore + [0x02235] = 0x2A, -- because \because + [0x02251] = 0x2B, -- equalsdots \Doteq \doteqdot + [0x0225C] = 0x2C, -- defines \triangleq + [0x0227E] = 0x2D, -- precedesorequal \precsim + [0x02272] = 0x2E, -- lessorsimilar \lesssim + [0x02A85] = 0x2F, -- lessorapproxeql \lessapprox + [0x02A95] = 0x30, -- equalorless \eqslantless + [0x02A96] = 0x31, -- equalorgreater \eqslantgtr + [0x022DE] = 0x32, -- equalorprecedes \curlyeqprec + [0x022DF] = 0x33, -- equalorfollows \curlyeqsucc + [0x0227C] = 0x34, -- precedesorcurly \preccurlyeq + [0x02266] = 0x35, -- lessdblequal \leqq + [0x02A7D] = 0x36, -- lessorequalslant \leqslant + [0x02276] = 0x37, -- lessorgreater \lessgtr + [0x02035] = 0x38, -- primereverse \backprime + -- [0x0] = 0x39, -- axisshort \dabar + [0x02253] = 0x3A, -- equaldotrightleft \risingdotseq + [0x02252] = 0x3B, -- equaldotleftright \fallingdotseq + [0x0227D] = 0x3C, -- followsorcurly \succcurlyeq + [0x02267] = 0x3D, -- greaterdblequal \geqq + [0x02A7E] = 0x3E, -- greaterorequalslant \geqslant + [0x02277] = 0x3F, -- greaterorless \gtrless + [0x0228F] = 0x40, -- squareimage \sqsubset + [0x02290] = 0x41, -- squareoriginal \sqsupset + -- wrong: + [0x022B3] = 0x42, -- triangleright \rhd \vartriangleright + [0x022B2] = 0x43, -- triangleleft \lhd \vartriangleleft + [0x022B5] = 0x44, -- trianglerightequal \unrhd \trianglerighteq + [0x022B4] = 0x45, -- triangleleftequal \unlhd \trianglelefteq + -- + [0x02605] = 0x46, -- star \bigstar + [0x0226C] = 0x47, -- between \between + [0x025BC] = 0x48, -- triangledownsld \blacktriangledown + [0x025B6] = 0x49, -- trianglerightsld \blacktriangleright + [0x025C0] = 0x4A, -- triangleleftsld \blacktriangleleft + -- [0x0] = 0x4B, -- arrowaxisright + -- [0x0] = 0x4C, -- arrowaxisleft + [0x025B2] = 0x4D, -- triangle \triangleup \vartriangle + [0x025B2] = 0x4E, -- trianglesolid \blacktriangle + [0x025BC] = 0x4F, -- triangleinv \triangledown + [0x02256] = 0x50, -- ringinequal \eqcirc + [0x022DA] = 0x51, -- lessequalgreater \lesseqgtr + [0x022DB] = 0x52, -- greaterlessequal \gtreqless + [0x02A8B] = 0x53, -- lessdbleqlgreater \lesseqqgtr + [0x02A8C] = 0x54, -- greaterdbleqlless \gtreqqless + [0x000A5] = 0x55, -- Yen \yen + [0x021DB] = 0x56, -- arrowtripleright \Rrightarrow + [0x021DA] = 0x57, -- arrowtripleleft \Lleftarrow + [0x02713] = 0x58, -- check \checkmark + [0x022BB] = 0x59, -- orunderscore \veebar + [0x022BC] = 0x5A, -- nand \barwedge + [0x02306] = 0x5B, -- perpcorrespond \doublebarwedge + [0x02220] = 0x5C, -- angle \angle + [0x02221] = 0x5D, -- measuredangle \measuredangle + [0x02222] = 0x5E, -- sphericalangle \sphericalangle + -- [0x0] = 0x5F, -- proportional \varpropto + -- [0x0] = 0x60, -- smile \smallsmile + -- [0x0] = 0x61, -- frown \smallfrown + [0x022D0] = 0x62, -- subsetdbl \Subset + [0x022D1] = 0x63, -- supersetdbl \Supset + [0x022D3] = 0x64, -- uniondbl \doublecup \Cup + [0x00100] = 0x65, -- intersectiondbl \doublecap \Cap + [0x022CF] = 0x66, -- uprise \curlywedge + [0x022CE] = 0x67, -- downfall \curlyvee + [0x022CB] = 0x68, -- multiopenleft \leftthreetimes + [0x022CC] = 0x69, -- multiopenright \rightthreetimes + [0x02AC5] = 0x6A, -- subsetdblequal \subseteqq + [0x02AC6] = 0x6B, -- supersetdblequal \supseteqq + [0x0224F] = 0x6C, -- difference \bumpeq + [0x0224E] = 0x6D, -- geomequivalent \Bumpeq + [0x022D8] = 0x6E, -- muchless \lll \llless + [0x022D9] = 0x6F, -- muchgreater \ggg \gggtr + [0x0231C] = 0x70, -- rightanglenw \ulcorner + [0x0231D] = 0x71, -- rightanglene \urcorner + [0x024C7] = 0x72, -- circleR \circledR + [0x024C8] = 0x73, -- circleS \circledS + [0x022D4] = 0x74, -- fork \pitchfork + [0x02245] = 0x75, -- dotplus \dotplus + [0x0223D] = 0x76, -- revsimilar \backsim + [0x022CD] = 0x77, -- revasymptequal \backsimeq -- AM: Check this! I mapped it to simeq. + [0x0231E] = 0x78, -- rightanglesw \llcorner + [0x0231F] = 0x79, -- rightanglese \lrcorner + [0x02720] = 0x7A, -- maltesecross \maltese + [0x02201] = 0x7B, -- complement \complement + [0x022BA] = 0x7C, -- intercal \intercal + [0x0229A] = 0x7D, -- circlering \circledcirc + [0x0229B] = 0x7E, -- circleasterisk \circledast + [0x0229D] = 0x7F, -- circleminus \circleddash +} + +fonts.enc.math["tex-mb"] = { + -- [0x0] = 0x00, -- lessornotequal \lvertneqq + -- [0x0] = 0x01, -- greaterornotequal \gvertneqq + [0x02270] = 0x02, -- notlessequal \nleq + [0x02271] = 0x03, -- notgreaterequal \ngeq + [0x0226E] = 0x04, -- notless \nless + [0x0226F] = 0x05, -- notgreater \ngtr + [0x02280] = 0x06, -- notprecedes \nprec + [0x02281] = 0x07, -- notfollows \nsucc + [0x02268] = 0x08, -- lessornotdbleql \lneqq + [0x02269] = 0x09, -- greaterornotdbleql \gneqq + -- [0x0] = 0x0A, -- notlessorslnteql \nleqslant + -- [0x0] = 0x0B, -- notgreaterorslnteql \ngeqslant + [0x02A87] = 0x0C, -- lessnotequal \lneq + [0x02A88] = 0x0D, -- greaternotequal \gneq + -- [0x0] = 0x0E, -- notprecedesoreql \npreceq + -- [0x0] = 0x0F, -- notfollowsoreql \nsucceq + [0x022E8] = 0x10, -- precedeornoteqvlnt \precnsim + [0x022E9] = 0x11, -- followornoteqvlnt \succnsim + [0x022E6] = 0x12, -- lessornotsimilar \lnsim + [0x022E7] = 0x13, -- greaterornotsimilar \gnsim + -- [0x0] = 0x14, -- notlessdblequal \nleqq + -- [0x0] = 0x15, -- notgreaterdblequal \ngeqq + [0x02AB5] = 0x16, -- precedenotslnteql \precneqq + [0x02AB6] = 0x17, -- follownotslnteql \succneqq + [0x02AB9] = 0x18, -- precedenotdbleqv \precnapprox + [0x02ABA] = 0x19, -- follownotdbleqv \succnapprox + [0x02A89] = 0x1A, -- lessnotdblequal \lnapprox + [0x02A8A] = 0x1B, -- greaternotdblequal \gnapprox + [0x02241] = 0x1C, -- notsimilar \nsim + [0x02247] = 0x1D, -- notapproxequal \ncong + -- [0x0] = 0x1E, -- upslope \diagup + -- [0x0] = 0x1F, -- downslope \diagdown + -- [0x0] = 0x20, -- notsubsetoreql \varsubsetneq + -- [0x0] = 0x21, -- notsupersetoreql \varsupsetneq + -- [0x0] = 0x22, -- notsubsetordbleql \nsubseteqq + -- [0x0] = 0x23, -- notsupersetordbleql \nsupseteqq + [0x02ACB] = 0x24, -- subsetornotdbleql \subsetneqq + [0x02ACC] = 0x25, -- supersetornotdbleql \supsetneqq + -- [0x0] = 0x26, -- subsetornoteql \varsubsetneqq + -- [0x0] = 0x27, -- supersetornoteql \varsupsetneqq + [0x0228A] = 0x28, -- subsetnoteql \subsetneq + [0x0228B] = 0x29, -- supersetnoteql \supsetneq + [0x02288] = 0x2A, -- notsubseteql \nsubseteq + [0x02289] = 0x2B, -- notsuperseteql \nsupseteq + [0x02226] = 0x2C, -- notparallel \nparallel + [0x02224] = 0x2D, -- notbar \nmid \ndivides + -- [0x0] = 0x2E, -- notshortbar \nshortmid + -- [0x0] = 0x2F, -- notshortparallel \nshortparallel + [0x022AC] = 0x30, -- notturnstile \nvdash + [0x022AE] = 0x31, -- notforces \nVdash + [0x022AD] = 0x32, -- notsatisfies \nvDash + [0x022AF] = 0x33, -- notforcesextra \nVDash + [0x022ED] = 0x34, -- nottriangeqlright \ntrianglerighteq + [0x022EC] = 0x35, -- nottriangeqlleft \ntrianglelefteq + [0x022EA] = 0x36, -- nottriangleleft \ntriangleleft + [0x022EB] = 0x37, -- nottriangleright \ntriangleright + [0x0219A] = 0x38, -- notarrowleft \nleftarrow + [0x0219B] = 0x39, -- notarrowright \nrightarrow + [0x021CD] = 0x3A, -- notdblarrowleft \nLeftarrow + [0x021CF] = 0x3B, -- notdblarrowright \nRightarrow + [0x021CE] = 0x3C, -- notdblarrowboth \nLeftrightarrow + [0x021AE] = 0x3D, -- notarrowboth \nleftrightarrow + [0x022C7] = 0x3E, -- dividemultiply \divideontimes + [0x02300] = 0x3F, -- diametersign \varnothing + [0x02204] = 0x40, -- notexistential \nexists + [0x1D538] = 0x41, -- A (blackboard A) + [0x1D539] = 0x42, -- B + [0x02102] = 0x43, -- C + [0x1D53B] = 0x44, -- D + [0x1D53C] = 0x45, -- E + [0x1D53D] = 0x46, -- F + [0x1D53E] = 0x47, -- G + [0x0210D] = 0x48, -- H + [0x1D540] = 0x49, -- I + [0x1D541] = 0x4A, -- J + [0x1D542] = 0x4B, -- K + [0x1D543] = 0x4C, -- L + [0x1D544] = 0x4D, -- M + [0x02115] = 0x4E, -- N + [0x1D546] = 0x4F, -- O + [0x02119] = 0x50, -- P + [0x0211A] = 0x51, -- Q + [0x0211D] = 0x52, -- R + [0x1D54A] = 0x53, -- S + [0x1D54B] = 0x54, -- T + [0x1D54C] = 0x55, -- U + [0x1D54D] = 0x56, -- V + [0x1D54E] = 0x57, -- W + [0x1D54F] = 0x58, -- X + [0x1D550] = 0x59, -- Y + [0x02124] = 0x5A, -- Z (blackboard Z) + [0x02132] = 0x60, -- hatwide \Finv + [0x02141] = 0x61, -- hatwider \Game + -- [0x0] = 0x62, tildewide + -- [0x0] = 0x63, tildewider + -- [0x0] = 0x64, Finv + -- [0x0] = 0x65, Gmir + [0x02127] = 0x66, -- Omegainv \mho + [0x000F0] = 0x67, -- eth \eth + [0x02242] = 0x68, -- equalorsimilar \eqsim + [0x02136] = 0x69, -- beth \beth + [0x02137] = 0x6A, -- gimel \gimel + [0x02138] = 0x6B, -- daleth \daleth + [0x022D6] = 0x6C, -- lessdot \lessdot + [0x022D7] = 0x6D, -- greaterdot \gtrdot + [0x022C9] = 0x6E, -- multicloseleft \ltimes + [0x022CA] = 0x6F, -- multicloseright \rtimes + -- [0x0] = 0x70, -- barshort \shortmid + -- [0x0] = 0x71, -- parallelshort \shortparallel + -- [0x02216] = 0x72, -- integerdivide \smallsetminus (2216 already part of tex-sy + -- [0x0] = 0x73, -- similar \thicksim + -- [0x0] = 0x74, -- approxequal \thickapprox + [0x0224A] = 0x75, -- approxorequal \approxeq + [0x02AB8] = 0x76, -- followsorequal \succapprox + [0x02AB7] = 0x77, -- precedesorequal \precapprox + [0x021B6] = 0x78, -- archleftdown \curvearrowleft + [0x021B7] = 0x79, -- archrightdown \curvearrowright + [0x003DC] = 0x7A, -- Digamma \digamma + [0x003F0] = 0x7B, -- kappa \varkappa + [0x1D55C] = 0x7C, -- k \Bbbk (blackboard k) + [0x0210F] = 0x7D, -- planckover2pi \hslash + [0x00127] = 0x7E, -- planckover2pi1 \hbar + [0x003F6] = 0x7F, -- epsiloninv \backepsilon +} + +fonts.enc.math["tex-fraktur"] = { +-- [0x1D504] = 0x41, -- A (fraktur A) +-- [0x1D505] = 0x42, -- B + [0x0212D] = 0x43, -- C +-- [0x1D507] = 0x44, -- D +-- [0x1D508] = 0x45, -- E +-- [0x1D509] = 0x46, -- F +-- [0x1D50A] = 0x47, -- G + [0x0210C] = 0x48, -- H + [0x02111] = 0x49, -- I +-- [0x1D50D] = 0x4A, -- J +-- [0x1D50E] = 0x4B, -- K +-- [0x1D50F] = 0x4C, -- L +-- [0x1D510] = 0x4D, -- M +-- [0x1D511] = 0x4E, -- N +-- [0x1D512] = 0x4F, -- O +-- [0x1D513] = 0x50, -- P +-- [0x1D514] = 0x51, -- Q + [0x0211C] = 0x52, -- R +-- [0x1D516] = 0x53, -- S +-- [0x1D517] = 0x54, -- T +-- [0x1D518] = 0x55, -- U +-- [0x1D519] = 0x56, -- V +-- [0x1D51A] = 0x57, -- W +-- [0x1D51B] = 0x58, -- X +-- [0x1D51C] = 0x59, -- Y + [0x02128] = 0x5A, -- Z (fraktur Z) +-- [0x1D51E] = 0x61, -- a (fraktur a) +-- [0x1D51F] = 0x62, -- b +-- [0x1D520] = 0x63, -- c +-- [0x1D521] = 0x64, -- d +-- [0x1D522] = 0x65, -- e +-- [0x1D523] = 0x66, -- f +-- [0x1D524] = 0x67, -- g +-- [0x1D525] = 0x68, -- h +-- [0x1D526] = 0x69, -- i +-- [0x1D527] = 0x6A, -- j +-- [0x1D528] = 0x6B, -- k +-- [0x1D529] = 0x6C, -- l +-- [0x1D52A] = 0x6D, -- m +-- [0x1D52B] = 0x6E, -- n +-- [0x1D52C] = 0x6F, -- o +-- [0x1D52D] = 0x70, -- p +-- [0x1D52E] = 0x71, -- q +-- [0x1D52F] = 0x72, -- r +-- [0x1D530] = 0x73, -- s +-- [0x1D531] = 0x74, -- t +-- [0x1D532] = 0x75, -- u +-- [0x1D533] = 0x76, -- v +-- [0x1D534] = 0x77, -- w +-- [0x1D535] = 0x78, -- x +-- [0x1D536] = 0x79, -- y +-- [0x1D537] = 0x7A, -- z +} + +-- now that all other vectors are defined ... + +fonts.vf.math.set_letters(fonts.enc.math, "tex-it", 0x1D434, 0x1D44E) +fonts.vf.math.set_letters(fonts.enc.math, "tex-ss", 0x1D5A0, 0x1D5BA) +fonts.vf.math.set_letters(fonts.enc.math, "tex-tt", 0x1D670, 0x1D68A) +fonts.vf.math.set_letters(fonts.enc.math, "tex-bf", 0x1D400, 0x1D41A) +fonts.vf.math.set_letters(fonts.enc.math, "tex-bi", 0x1D468, 0x1D482) +fonts.vf.math.set_letters(fonts.enc.math, "tex-fraktur", 0x1D504, 0x1D51E) +fonts.vf.math.set_letters(fonts.enc.math, "tex-fraktur-bold", 0x1D56C, 0x1D586) + +fonts.vf.math.set_digits (fonts.enc.math, "tex-ss", 0x1D7E2) +fonts.vf.math.set_digits (fonts.enc.math, "tex-tt", 0x1D7F6) +fonts.vf.math.set_digits (fonts.enc.math, "tex-bf", 0x1D7CE) + +-- fonts.vf.math.set_digits (fonts.enc.math, "tex-bi", 0x1D7CE) + +-- todo: add ss, tt, bf etc vectors +-- todo: we can make ss tt etc an option diff --git a/tex/context/base/meta-clp.tex b/tex/context/base/meta-clp.tex new file mode 100644 index 000000000..be2506b19 --- /dev/null +++ b/tex/context/base/meta-clp.tex @@ -0,0 +1,164 @@ +%D \module +%D [ file=meta-clp, +%D version=2000.07.06, +%D title=\METAPOST\ Graphics, +%D subtitle=Clipping, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D In this library, we define a bunch of clipping paths that +%D can be fed to \type {\clip}. + +\startMPclip{ellipse} + clip currentpicture to unitcircle + xscaled \width yscaled \height ; +\stopMPclip + +\startMPclip{negellipse} + clip currentpicture to (unitcircle peepholed unitsquare) + xscaled \width yscaled \height ; +\stopMPclip + +\startMPclip{urellipse} + clip currentpicture to urcircle scaled 2 + xscaled \width yscaled \height ; +\stopMPclip + +\startMPclip{ulellipse} + clip currentpicture to ulcircle scaled 2 shifted (1,0) + xscaled \width yscaled \height ; +\stopMPclip + +\startMPclip{llellipse} + clip currentpicture to llcircle scaled 2 shifted (1,1) + xscaled \width yscaled \height ; +\stopMPclip + +\startMPclip{lrellipse} + clip currentpicture to lrcircle scaled 2 shifted (0,1) + xscaled \width yscaled \height ; +\stopMPclip + +\startMPclip{tellipse} + clip currentpicture to tcircle shifted (.5,0) yscaled 2 + xscaled \width yscaled \height ; +\stopMPclip + +\startMPclip{bellipse} + clip currentpicture to bcircle shifted (.5,.5) yscaled 2 + xscaled \width yscaled \height ; +\stopMPclip + +\startMPclip{lellipse} + clip currentpicture to lcircle shifted (.5,.5) xscaled 2 + xscaled \width yscaled \height ; +\stopMPclip + +\startMPclip{rellipse} + clip currentpicture to rcircle shifted (0,.5) xscaled 2 + xscaled \width yscaled \height ; +\stopMPclip + +\startMPclip{diamond} + clip currentpicture to unitdiamond + xscaled \width yscaled \height ; +\stopMPclip + +\startMPclip{negdiamond} + clip currentpicture to (unitdiamond peepholed unitsquare) + xscaled \width yscaled \height ; +\stopMPclip + +\startMPclip{urtriangle} + clip currentpicture to urtriangle scaled 2 + xscaled \width yscaled \height ; +\stopMPclip + +\startMPclip{ultriangle} + clip currentpicture to ultriangle scaled 2 shifted (1,0) + xscaled \width yscaled \height ; +\stopMPclip + +\startMPclip{lltriangle} + clip currentpicture to lltriangle scaled 2 shifted (1,1) + xscaled \width yscaled \height ; +\stopMPclip + +\startMPclip{lrtriangle} + clip currentpicture to lrtriangle scaled 2 shifted (0,1) + xscaled \width yscaled \height ; +\stopMPclip + +% More efficient: +% +% \def\dosimpleMPclip#1 +% {clip currentpicture to (#1) xscaled \width yscaled \height ;} +% +% \def\simpleMPclip#1#2% +% {\startMPclip{#1}\dosimpleMPclip{#2}\stopMPclip} +% +% \simpleMPclip {ellipse} {unitcircle} +% \simpleMPclip {diamond} {unitdiamond} +% +% \simpleMPclip {negellipse} {unitcircle peepholed unitsquare} +% \simpleMPclip {negdiamond} {unitdiamond peepholed unitsquare} +% +% \simpleMPclip {urellipse} {urcircle scaled 2 shifted (0,0)} +% \simpleMPclip {ulellipse} {ulcircle scaled 2 shifted (1,0)} +% \simpleMPclip {llellipse} {llcircle scaled 2 shifted (1,1)} +% \simpleMPclip {lrellipse} {lrcircle scaled 2 shifted (0,1)} +% +% \simpleMPclip {tellipse} {tcircle shifted (.5,0) yscaled 2} +% \simpleMPclip {bellipse} {bcircle shifted (.5,.5) yscaled 2} +% \simpleMPclip {lellipse} {lcircle shifted (.5,.5) xscaled 2} +% \simpleMPclip {rellipse} {rcircle shifted (0,.5) xscaled 2} +% +% \simpleMPclip {urtriangle} {urtriangle scaled 2 shifted (0,0)} +% \simpleMPclip {ultriangle} {ultriangle scaled 2 shifted (1,0)} +% \simpleMPclip {lltriangle} {lltriangle scaled 2 shifted (1,1)} +% \simpleMPclip {lrtriangle} {lrtriangle scaled 2 shifted (0,1)} + +%D \in {Figure} [fig:clipping paths] shows which paths are +%D predefined. When applied to a whole picture, their usage +%D is: +%D +%D \starttyping +%D \clip[nx=1,ny=1,mp=ellipse]{some clippable content} +%D \stoptyping +%D +%D \startbuffer +%D \setupclipping [nx=1,ny=1,x=1,y=1] +%D \setupblackrules[width=2cm,height=1cm] +%D \startcombination[6*3] % \startcombination[6*3] +%D {\clip[mp=urellipse] {\blackrule}} {urellipse} +%D {\clip[mp=ulellipse] {\blackrule}} {ulellipse} +%D {\clip[mp=llellipse] {\blackrule}} {llellipse} +%D {\clip[mp=lrellipse] {\blackrule}} {lrellipse} +%D {\clip[mp=ellipse] {\blackrule}} {ellipse} +%D {\clip[mp=negellipse]{\blackrule}} {negellipse} +%D {\clip[mp=tellipse] {\blackrule}} {tellipse} +%D {\clip[mp=bellipse] {\blackrule}} {bellipse} +%D {\clip[mp=lellipse] {\blackrule}} {lellipse} +%D {\clip[mp=rellipse] {\blackrule}} {rellipse} +%D {} {} +%D {} {} +%D {\clip[mp=urtriangle]{\blackrule}} {urtriangle} +%D {\clip[mp=ultriangle]{\blackrule}} {ultriangle} +%D {\clip[mp=lltriangle]{\blackrule}} {lltriangle} +%D {\clip[mp=lrtriangle]{\blackrule}} {lrtriangle} +%D {\clip[mp=diamond] {\blackrule}} {diamond} +%D {\clip[mp=negdiamond]{\blackrule}} {negdiamond} +%D \stopcombination % \stopcombination +%D \stopbuffer +%D +%D \placefigure % \placefigure +%D [here][fig:clipping paths] +%D {The predefined clipping paths.} +%D {\getbuffer} % {\getbuffer} + +\endinput diff --git a/tex/context/base/meta-dum.tex b/tex/context/base/meta-dum.tex new file mode 100644 index 000000000..bc19f3c5f --- /dev/null +++ b/tex/context/base/meta-dum.tex @@ -0,0 +1,123 @@ +%D \module +%D [ file=meta-dum, +%D version=2003.03.21, +%D title=\METAPOST\ Graphics, +%D subtitle=Dummy (External) Graphics, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\unprotect + +%D This library overloads the normal external figure +%D placeholder by a nicer one. +%D +%D \startbuffer +%D \useMPlibrary[dum] +%D +%D \startlinecorrection +%D \externalfigure[unknown-a][width=3cm,height=1cm] +%D \stoplinecorrection +%D +%D \startlinecorrection +%D \externalfigure[unknown-b][width=4cm,height=5cm] +%D \stoplinecorrection +%D \stopbuffer +%D +%D \typebuffer \getbuffer + +% currently preparempvariables is unable to resolve number +% fractions like reduction + +% June 22, 2003, this definition was patched to adapt itself +% to transparent colors + +\startuseMPgraphic{placeholder}{width,height,reduction,color} + numeric w, h, d, r ; color c, b, cc ; path p ; boolean t ; + t := is_transparent(\MPvar{color}) ; + c := not_transparent(\MPvar{color}) ; + b := not_transparent(white) ; + w := \MPvar{width} ; + h := \MPvar{height} ; + r := \MPvar{reduction} ; + d := max(w,h) ; + p := unitsquare xyscaled (w,h) ; + cc := r[.5c,b] ; + fill p withcolor if t : transparent(1,.5,cc) else : cc fi ; + for i := 1 upto 60 : + cc := r[c randomized(.3,.9),b] ; + fill fullcircle + scaled (d/5 randomized (d/5)) + shifted (center p randomized (d)) + withcolor if t : transparent(1,.5,cc) else : cc fi ; + endfor ; + clip currentpicture to p ; +\stopuseMPgraphic + +\definepalet + [placeholder] + [1=red,2=green,3=blue,4=cyan,5=magenta,6=yellow] + +% \newcounter \figurereplacementcycle + +\let\figurereplacementcycle\relax + +\setupexternalfigures + [\c!reduction=0, + \c!text=\v!yes] + +\let\normalexternalfigurereplacement\externalfigurereplacement + +\def\externalfigurereplacement#1#2#3% + {\getpaletsize[placeholder]% + \ifx\figurereplacementcycle\relax + \getrandomnumber \figurereplacementcycle \!!plusone \paletsize + \globallet \figurereplacementcycle \figurereplacementcycle + \else + \doglobal\increment\figurereplacementcycle + \fi + \ifnum\figurereplacementcycle>\paletsize + \globallet\figurereplacementcycle\!!plusone + \fi + \weightGRAYfalse % monochrome anyway + \MPcmykcolorsfalse + \MPspotcolorsfalse + \defineoverlay + [\s!dummy] + [\useMPgraphic + {placeholder}% + {width=\figurewidth, + height=\figureheight, + reduction=\@@efreduction, + color=placeholder:\figurereplacementcycle}]% + \expanded{\localframed + [\??ef] + [\c!width=\figurewidth, + \c!height=\figureheight, + \c!frame=\v!off, + \c!strut=\v!no, + \c!background=\s!dummy, + \c!foregroundcolor=\s!white]}% + {\doif\@@eftext\v!yes + {\infofont \setupinterlinespace \dohyphens % \nohyphens + \doifelse{#1}\s!dummy \!!doneafalse\!!doneatrue + \doifelse{#2}\s!dummy \!!donebfalse\!!donebtrue + \doifelse{#3}\s!unknown\!!donecfalse\!!donectrue + \if!!donea + name: \expanded{\verbatimstring{#1}}\strut\endgraf + \fi + \if!!doneb + \if!!donea file: \else \if!!donec file: \fi \fi + \expanded{\verbatimstring{#2}}\strut\endgraf + \fi + \if!!donec + state: \expanded{\verbatimstring{#3}}\strut\endgraf + \fi}}} + +\def\dummyfigure{\externalfigure[placeholder]} + +\protect \endinput diff --git a/tex/context/base/meta-fig.mkii b/tex/context/base/meta-fig.mkii new file mode 100644 index 000000000..3edd73b57 --- /dev/null +++ b/tex/context/base/meta-fig.mkii @@ -0,0 +1,89 @@ +%D \module +%D [ file=meta-fig, +%D version=2000.09.07, +%D title=\METAPOST\ Graphics, +%D subtitle=Stand Alone Graphics, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{MetaPost Graphics / Stand Alone Graphics} + +\unprotect + +%D This module implements a method for defining +%D stand||alone||graphics, that is, each graphic gets is own +%D page. Because graphics are wrapped in a \type {\framed}, +%D you can add overlays to the graphic directly, and since the +%D whole \CONTEXT\ machinery is available, you can also add +%D page backgrounds. +%D +%D \starttyping +%D \setupMPpage +%D [offset=1pt, +%D background=color, +%D backgroundcolor=green] +%D +%D \startMPpage +%D fill fullcircle scaled 10cm withcolor red ; +%D \stopMPpage +%D +%D \startMPpage +%D fill fullsquare rotated 45 scaled 8cm withcolor blue ; +%D \stopMPpage +%D \stoptyping +%D +%D Although this is hardly of any use, you can mix these +%D definitions with the text flow, since all settings are +%D kept local. The page is clipped to the image size. + +\presetlocalframed[\??mg] + +\def\setupMPpage + {\dodoubleargument\getparameters[\??mg]} + +\def\startMPpage + {\dodoubleempty\dostartMPpage} + +\long\def\dostartMPpage[#1][#2]% second arg gobbles space + {\dostartfittingpage[\??mg][#1]% + \obeyMPlines + \dodostartMPpage} + +\long\def\dodostartMPpage#1\stopMPpage + {\startuseMPgraphic{@@}#1\stopuseMPgraphic + \useMPgraphic{@@}% + \dostopfittingpage} + +\let\stopMPpage \relax % so that we can use it in \expanded + +\setupMPpage + [\c!scale=1000, + \c!strut=\v!no, + \c!align=, + \c!offset=\v!overlay, + \c!width=\v!fit, + \c!height=\v!fit, + \c!frame=\v!off] + +%D \macros +%D {MPfigure} +%D +%D A bit out of place, here but nevertheless: + +\def\MPfigure#1#2% test for dup figure + {\bgroup + \getfiguredimensionsonly[#1]% [\c!object=\v!no] already set + \startMPcode + externalfigure "#1" + xscaled \figurewidth\space + yscaled \figureheight\space + #2 ; + \stopMPcode + \egroup} + +\protect \endinput diff --git a/tex/context/base/meta-fig.mkiv b/tex/context/base/meta-fig.mkiv new file mode 100644 index 000000000..4738316da --- /dev/null +++ b/tex/context/base/meta-fig.mkiv @@ -0,0 +1,84 @@ +%D \module +%D [ file=meta-fig, +%D version=2000.09.07, +%D title=\METAPOST\ Graphics, +%D subtitle=Stand Alone Graphics, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{MetaPost Graphics / Stand Alone Graphics} + +\unprotect + +%D This module implements a method for defining +%D stand||alone||graphics, that is, each graphic gets is own +%D page. Because graphics are wrapped in a \type {\framed}, +%D you can add overlays to the graphic directly, and since the +%D whole \CONTEXT\ machinery is available, you can also add +%D page backgrounds. +%D +%D \starttyping +%D \setupMPpage +%D [offset=1pt, +%D background=color, +%D backgroundcolor=green] +%D +%D \startMPpage +%D fill fullcircle scaled 10cm withcolor red ; +%D \stopMPpage +%D +%D \startMPpage +%D fill fullsquare rotated 45 scaled 8cm withcolor blue ; +%D \stopMPpage +%D \stoptyping +%D +%D Although this is hardly of any use, you can mix these +%D definitions with the text flow, since all settings are +%D kept local. The page is clipped to the image size. + +\presetlocalframed[\??mg] + +\unexpanded\def\setupMPpage + {\dodoubleargument\getparameters[\??mg]} + +\def\startMPpage + {\dodoubleempty\dostartMPpage} + +\long\def\dostartMPpage[#1][#2]#3\stopMPpage % second arg gobbles space + {\dostartfittingpage[\??mg][#1]% + \processMPgraphic{#3}% + \dostopfittingpage} + +\let\stopMPpage \relax % so that we can use it in \expanded + +\setupMPpage + [\c!scale=1000, + \c!strut=\v!no, + \c!align=, + \c!offset=\v!overlay, + \c!width=\v!fit, + \c!height=\v!fit, + \c!frame=\v!off] + +%D \macros +%D {MPfigure} +%D +%D A bit out of place, here but nevertheless: + +\def\MPfigure#1#2% test for dup figure, can be replaced by a textext + {\bgroup + \getfiguredimensionsonly[#1]% [\c!object=\v!no] already set + \startMPcode + externalfigure "#1" + xscaled \the\dimexpr\figurewidth \relax\space % must be points + yscaled \the\dimexpr\figureheight\relax\space % must be points + #2 ; + \stopMPcode + \egroup} + +\protect \endinput diff --git a/tex/context/base/meta-fun.lua b/tex/context/base/meta-fun.lua new file mode 100644 index 000000000..fef2aa12b --- /dev/null +++ b/tex/context/base/meta-fun.lua @@ -0,0 +1,55 @@ +if not modules then modules = { } end modules ['meta-fun'] = { + version = 1.001, + comment = "companion to meta-fun.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files", +} + +-- very experimental, actually a joke ... see metafun manual for usage + +local format, loadstring, type = string.format, loadstring, type +local texwrite = tex.write + +metafun = metafun or { } + +function metafun.topath(t,connector) + tex.write("(") + if #t > 0 then + for i=1,#t do + if i > 1 then + texwrite(connector or "..") + end + local ti = t[i] + if type(ti) == "string" then + texwrite(ti) + else + texwrite(format("(%s,%s)",ti.x or ti[1] or 0,ti.y or ti[2] or 0)) + end + end + else + texwrite("origin") + end + texwrite(")") +end + +function metafun.interpolate(f,b,e,s,c) + local done = false + tex.write("(") + for i=b,e,(e-b)/s do + local d = loadstring(format("return function(x) return %s end",f)) + if d then + d = d() + if done then + texwrite(c or "...") + else + done = true + end + texwrite(format("(%s,%s)",i,d(i))) + end + end + if not done then + texwrite("origin") + end + texwrite(")") +end diff --git a/tex/context/base/meta-fun.mkiv b/tex/context/base/meta-fun.mkiv new file mode 100644 index 000000000..a43ffefff --- /dev/null +++ b/tex/context/base/meta-fun.mkiv @@ -0,0 +1,20 @@ +%D \module +%D [ file=meta-fun, +%D version=2009.06.02, +%D title=\METAPOST\ Graphics, +%D subtitle=Goodies, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{MetaPost Graphics / Goodies} + +\registerctxluafile{meta-fun}{1.001} + +\unprotect + +\protect \endinput diff --git a/tex/context/base/meta-ini.mkii b/tex/context/base/meta-ini.mkii new file mode 100644 index 000000000..e7c6d218e --- /dev/null +++ b/tex/context/base/meta-ini.mkii @@ -0,0 +1,1054 @@ +%D \module +%D [ file=meta-ini, +%D version=1999.07.10, +%D title=\METAPOST\ Graphics, +%D subtitle=Initialization, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +% currently the running color influences the mp graphic in +% pdftex, but this will change [i.e. become optional]; one +% problem is that pdf has no grouping with regards to the +% color + +\writestatus{loading}{MetaPost Graphics / Initializations} + +\unprotect + +%D This module extends the functionality of the support module +%D \type {supp-mps}, the module that is responsible for +%D \METAPOST\ inclusion in \CONTEXT. Some basic macros will be +%D extended. Since some support is depends on \METAPOST\ +%D macros. so let's first preload a few auxiliary \METAPOST\ +%D files. + +\maxnofMPgraphics = 4000 % metafun disables the 4K boundary + +\appendtoks \flushMPgraphics \to \everygoodbye % \everylastshipout + +\def\@@MPG{@MPG@} + +\startMPextensions + if unknown context_tool: input mp-tool; fi; + if unknown context_spec: input mp-spec; fi; + if unknown context_grph: input mp-grph; fi; +\stopMPextensions + +%D Since we want lables to follow the document settings, we +%D also set the font related variables. + +\ifnum\texengine=\xetexengine + \startMPinitializations % scale is not yet ok + defaultfont:="rm-lmtt10"; + defaultscale:=\the\bodyfontsize/10pt; + \stopMPinitializations +\else + \startMPinitializations % scale is not yet ok + defaultfont:="\truefontname{Regular}"; + defaultscale:=\the\bodyfontsize/10pt; + \stopMPinitializations +\fi + +%D In order to support fancy text features (like outline +%D fonts), we set: + +\startMPextensions + graphictextformat:="context"; + graphictextdirective "\the\everyMPTEXgraphic"; +\stopMPextensions + +% \startMPextensions +% textextdirective "\the\everyMPTEXgraphic"; +% \stopMPextensions + +%D A signal that we're in combines \CONTEXT||\METAFUN mode: + +\startMPextensions + string contextversion; + contextversion:="\contextversion"; +\stopMPextensions + +%D Some safeguards: +%D +%D \starttyping +%D \appendtoks \cleanupfeatures \to \everyMPgraphic +%D \stoptyping +%D +%D No, we don't want that (else we loose UTF etc). + +%D Another one: + +\prependtoks \MPstaticgraphictrue \to \everyoverlay +\prependtoks \MPstaticgraphictrue \to \everypagebody + +%D We save the number of graphics for the sake of \TEXEXEC. + +\newcounter\totalnofMPgraphics + +\def\thenofMPgraphics{\the\nofMPgraphics} % from supp-mps + +\appendtoks + \savecurrentvalue\totalnofMPgraphics\thenofMPgraphics +\to \everybye + +%D \macros +%D {setupMPvariables} +%D +%D When we build collections of \METAPOST\ graphics, like +%D background and buttons, the need for passing settings +%D arises. By (mis|)|using the local prefix that belongs to +%D \type {\framed}, we get a rather natural interface to +%D backgrounds. To prevent conflicts, we will use the \type +%D {-} in \METAPOST\ specific variables, like: +%D +%D \starttyping +%D \setupMPvariables[meta:button][size=20pt] +%D \stoptyping + +\def\@@meta{meta:} + +\def\setupMPvariables + {\dodoubleempty\dosetupMPvariables} + +\def\dosetupMPvariables[#1][#2]% + {\ifsecondargument + \getrawparameters[#1:][#2]% brr, todo: [\@@meta#1:] + \else + \getrawparameters[\@@meta][#1]% + \fi} + +\let\@@framed\s!unknown + +\def\MPvariable#1% + {\csname + \ifcsname\@@framed\@@meta#1\endcsname\@@framed\fi\@@meta#1% + \endcsname} + +\let\MPvar\MPvariable + +\let\setMPvariables\setupMPvariables + +\def\MPrawvar#1#2{\csname#1:#2\endcsname} + +\def\presetMPvariable + {\dodoubleargument\dopresetMPvariable} + +\def\dopresetMPvariable[#1][#2=#3]% + {\doifundefined{#1:#2}{\setvalue{#1:#2}{#3}}} + +\def\useMPvariables + {\dodoubleargument\douseMPvariables} + +\def\douseMPvariables[#1][#2]% + {\def\@@meta{#1:}% + \prepareMPvariables{#2}} + +%D \macros +%D {startuniqueMPgraphic, uniqueMPgraphic} +%D +%D This macros is probably of most use to myself, since I like +%D to use graphics that adapt themselves. The next \METAPOST\ +%D kind of graphic is both unique and reused when possible. +%D +%D \starttyping +%D \defineoverlay[example][\uniqueMPgraphic{test}] +%D +%D \startuniqueMPgraphic {test} +%D draw unitsquare xscaled \overlaywidth yscaled \overlayheight ; +%D \stopuniqueMPgraphic +%D \stoptyping + +%D For educational purposes, we show the original version +%D first. This one used a rather simple method for determining +%D the uniqueness. +%D +%D \starttyping +%D \long\def\startuniqueMPgraphic#1#2\stopuniqueMPgraphic% +%D {\setvalue{\@@MPG#1}% +%D {\startreusableMPgraphic{\overlaystamp:#1}#2\stopreusableMPgraphic +%D \reuseMPgraphic{\overlaystamp:#1}}} +%D +%D \def\uniqueMPgraphic#1% +%D {\getvalue{\@@MPG#1}} +%D \stoptyping + +%\def\overlaystamp % watch the \MPcolor, since colors can be redefined +% {\overlaywidth:\overlayheight:\overlaydepth +% :\MPcolor{\overlaycolor}:\MPcolor{\overlaylinecolor}} + +\def\overlaystamp % watch the \MPcolor, since colors can be redefined + {\overlaywidth:\overlayheight:\overlaydepth + :\MPcolor\overlaycolor:\MPcolor\overlaylinecolor} + +%D A better approach is to let additional variables play a role +%D in determining the uniqueness. In the next macro, the +%D second, optional, argument is used to guarantee the +%D uniqueness, as well as prepare variables for passing them to +%D \METAPOST. +%D +%D \starttyping +%D \startuniqueMPgraphic{meta:hash}{gap,angle,...} +%D \stoptyping +%D +%D The calling macro also accepts a second argument. For +%D convenient use in overlay definitions, we use \type {{}} +%D instead of \type {[]}. +%D +%D \starttyping +%D \uniqueMPgraphic{meta:hash}{gap=10pt,angle=30} +%D \stoptyping + +\long\def\handleuniqueMPgraphic#1#2#3% + {\blabelgroup + \def\@@meta{#1:}% + \extendMPoverlaystamp{#2}% incl prepare + \ifundefined{\@@MPG\overlaystamp:#1}% + \enableincludeMPgraphics + \startMPgraphic#3\stopMPgraphic + \doifobjectssupportedelse\donothing\useMPboxfalse + \ifuseMPbox + \@EA\douseMPbox + \else + \@EA\nouseMPbox + \fi {\@@MPG\overlaystamp:#1}% + \fi + \getvalue{\@@MPG\overlaystamp:#1}% + \elabelgroup} + +\long\def\startuniqueMPgraphic + {\blabelgroup + \dodoublegroupempty\dostartuniqueMPgraphic} + +\long\def\dostartuniqueMPgraphic + {\obeyMPlines + \dodostartuniqueMPgraphic} + +\long\def\dodostartuniqueMPgraphic#1#2#3\stopuniqueMPgraphic% + {\long\setgvalue{\@@MPG#1}{\handleuniqueMPgraphic{#1}{#2}{#3}}% + \elabelgroup} + +\unexpanded\def\uniqueMPgraphic + {\dodoublegroupempty\douniqueMPgraphic} + +\def\douniqueMPgraphic#1#2% + {\blabelgroup + \setupMPvariables[#1][#2]% + \getvalue{\@@MPG#1}{}% + \elabelgroup} + +\let\stopuniqueMPcode \relax % so that we can use it in \expanded + +\def\includeMPgraphic#1% + {\executeifdefined{\@@MPG#1};} % ; if not found + +\long\def\handleuseMPgraphic#1#2#3% + {\blabelgroup + \def\@@meta{#1:}% + \prepareMPvariables{#2}% + \enableincludeMPgraphics + \startMPgraphic#3\stopMPgraphic + \ifMPrun \else % see mfun-004 : processing buffer + \loadMPgraphic{\MPgraphicfile.\the\currentMPgraphic}{}% + \placeMPgraphic + \fi + \deallocateMPslot\currentMPgraphic + \elabelgroup} + +\long\def\startuseMPgraphic + {\blabelgroup + \dodoublegroupempty\dostartuseMPgraphic} + +\long\def\dostartuseMPgraphic + {\obeyMPlines + \dodostartuseMPgraphic} + +\long\def\dodostartuseMPgraphic#1#2#3\stopuseMPgraphic + {\long\setgvalue{\@@MPG#1}{\handleuseMPgraphic{#1}{#2}{#3}}% + \elabelgroup} + +\long\def\startusableMPgraphic % redundant but handy + {\blabelgroup + \dodoublegroupempty\dostartusableMPgraphic} + +\long\def\dostartusableMPgraphic % redundant but handy + {\obeyMPlines + \dodostartusableMPgraphic} + +\long\def\dodostartusableMPgraphic#1#2#3\stopusableMPgraphic + {\long\setgvalue{\@@MPG#1}{\handleuseMPgraphic{#1}{#2}{#3}}% + \elabelgroup} + +\long\def\handlereusableMPgraphic#1#2#3% + {\blabelgroup + \def\@@meta{#1:}% + \prepareMPvariables{#2}% + \enableincludeMPgraphics + \startMPgraphic#3\stopMPgraphic + \doifobjectssupportedelse\donothing\useMPboxfalse + \ifuseMPbox + \@EA\douseMPbox + \else + \@EA\nouseMPbox + \fi {\@@MPG#1}% + \getvalue{\@@MPG#1}% + \elabelgroup} + +\long\def\startreusableMPgraphic + {\blabelgroup + \dodoublegroupempty\dostartreusableMPgraphic} + +\def\dostartreusableMPgraphic + {\obeyMPlines + \dodostartreusableMPgraphic} + +\long\def\dodostartreusableMPgraphic#1#2#3\stopreusableMPgraphic + {\long\setgvalue{\@@MPG#1}{\handlereusableMPgraphic{#1}{#2}{#3}}% + \elabelgroup} + +\unexpanded\def\useMPgraphic + {\dodoublegroupempty\douseMPgraphic} + +\def\douseMPgraphic#1#2% + {\blabelgroup + \setupMPvariables[#1][#2]% + \getvalue{\@@MPG#1}{}% + \elabelgroup} + +\let\reuseMPgraphic\useMPgraphic + +\let\stopuseMPcode \relax % so that we can use it in \expanded +\let\stopusableMPcode \relax % so that we can use it in \expanded +\let\stopreusableMPcode \relax % so that we can use it in \expanded +\let\stopuniqueMPcode \relax % so that we can use it in \expanded + +\def\enableincludeMPgraphics + {\let\handleuseMPgraphic \thirdofthreearguments + \let\handlereusableMPgraphic\thirdofthreearguments} + +% todo: each code/page/buffer a var class + +%D \macros +%D {startuniqueMPpagegraphic,uniqueMPpagegraphic} +%D +%D Experimental. + +\def\MPpageprefix{\doifoddpageelse oe:} + +\def\overlaypagestamp + {\MPpageprefix\overlaywidth:\overlayheight:\overlaydepth + :\MPcolor\overlaycolor:\MPcolor\overlaylinecolor} + +\long\def\startuniqueMPpagegraphic + {\blabelgroup + \dodoublegroupempty\dostartuniqueMPpagegraphic} + +\long\def\dostartuniqueMPpagegraphic + {\obeyMPlines + \dodostartuniqueMPpagegraphic} + +\long\def\dodostartuniqueMPpagegraphic#1#2#3\stopuniqueMPpagegraphic + {\long\setgvalue{\@@MPG o:#1}{\handleuniqueMPgraphic{o:#1}{#2}{#3}}% + \long\setgvalue{\@@MPG e:#1}{\handleuniqueMPgraphic{e:#1}{#2}{#3}}% + \elabelgroup} + +\unexpanded\def\uniqueMPpagegraphic + {\dodoublegroupempty\douniqueMPpagegraphic} + +% \def\douniqueMPpagegraphic#1#2% +% {\blabelgroup +% \let\overlaystamp\overlaypagestamp +% \setupMPvariables[#1][#2]% +% \getvalue{\@@MPG\MPpageprefix#1}{}% +% \elabelgroup} + +\def\douniqueMPpagegraphic#1#2% + {\blabelgroup + \let\overlaystamp\overlaypagestamp + \setupMPvariables[\MPpageprefix#1][#2]% prefix is new here + \getvalue{\@@MPG\MPpageprefix#1}{}% + \elabelgroup} + +%D One way of defining a stamp is: +%D +%D \starttyping +%D \def\extendMPoverlaystamp#1% +%D {\def\docommand##1% +%D {\edef\overlaystamp{\overlaystamp:\MPvariable{##1}}}% +%D \processcommalist[#1]\docommand} +%D \stoptyping + +%D Since we need to feed \METAPOST\ with expanded dimensions, +%D we introduce a dedicated expansion engine. + +\def\prepareMPvariable#1% + {\ifundefined{\@@framed\@@meta#1}% + \doprepareMPvariable{\@@meta#1}% + \else + \doprepareMPvariable{\@@framed\@@meta#1}% + \fi} + +% \startlines +% \def\xxx{\lineheight} \doprepareMPvariable{xxx} \xxx +% \def\xxx{2pt} \doprepareMPvariable{xxx} \xxx +% \def\xxx{2} \doprepareMPvariable{xxx} \xxx +% \def\xxx{\scratchcounter} \doprepareMPvariable{xxx} \xxx +% \def\xxx{red} \doprepareMPvariable{xxx} \xxx +% \def\xxx{0.4} \doprepareMPvariable{xxx} \xxx +% \stoplines + +\def\doprepareMPvariable#1% + {\edef\theMPvariable{\getvalue{#1}}% + \doifelsenothing\theMPvariable + {\setevalue{#1}{\MPcolor{black}}} + {\defconvertedcommand\ascii\theMPvariable % otherwise problems + \doifcolorelse \ascii % with 2\bodyfontsize + {\setevalue{#1}{\MPcolor\theMPvariable}} + {% can be aux macro + \setbox\scratchbox\hbox{\scratchdimen\theMPvariable sp}% + \ifdim\wd\scratchbox=\zeropoint + % \scratchcounter\theMPvariable + % \setevalue{#1}{\the\scratchcounter}% + % also accepts 0.number : + \setevalue{#1}{\number\theMPvariable}% + \else + \scratchdimen\theMPvariable + \setevalue{#1}{\the\scratchdimen}% + \fi}}} + +%D We redefine \type {\extendMPoverlaystamp} to preprocess +%D variables using \type {\prepareMPvariable}. + +\def\doextendMPoverlaystamp#1% + {\prepareMPvariable{#1}% + \edef\overlaystamp{\overlaystamp:\MPvariable{#1}}} + +\def\extendMPoverlaystamp#1% + {\processcommalist[#1]\doextendMPoverlaystamp} + +\def\prepareMPvariables#1% + {\processcommalist[#1]\prepareMPvariable} + +%D \macros +%D {MPdatafile} +%D +%D We redefine a macro from \type {supp-mps.tex}: + +\def\MPdatafile + {\bufferprefix mpd-\the\currentMPgraphic.mpd} + +%D \macros +%D {MPrunfile} +%D +%D This one is more abstract and does not assume knowledge +%D of buffer prefixes. + +\def\MPrunfile#1% + {\bufferprefix mprun.#1} + +%D \macros +%D {getMPdata} +%D +%D When we collect graphics in one file, we run into +%D troubles, since \METAPOST\ has a built in limit (of 4) +%D on the number of files it can handle. It's therefore +%D better to collect all data in one file and filter it. + +\def\MPdataMPDfile{\jobname-mpgraph.mpd} +\def\MPdataMPOfile{\jobname-mpgraph.mpo} +\def\MPdataMPYfile{\jobname-mpgraph.mpy} + +\startMPextensions + boolean collapse_data; collapse_data:=true; + def data_mpd_file = "\MPdataMPDfile" enddef ; + def data_mpo_file = "\MPdataMPOfile" enddef ; + def data_mpy_file = "\MPdataMPYfile" enddef ; +\stopMPextensions + +\def\getMPdata + {\long\def\MPdata##1##2{\ifnum##1=\currentMPgraphic\relax##2\fi}% + \startreadingfile + % \startnointerference % no, else we need to do all data global + \readlocfile\MPdataMPDfile\donothing\donothing + % \stopnointerference + \stopreadingfile} + +%D We have to enable this mechanism with: + +\startMPextensions +\stopMPextensions + +%D For the moment, the next one is a private macro: + +\def\processMPbuffer + {\dosingleempty\doprocessMPbuffer} + +\def\doprocessMPbuffer[#1]% + {\doifelsenothing{#1} + {\doprocessMPbuffer[\jobname]} + {\bgroup + \setnormalcatcodes + \obeyMPlines + %\let\par\empty % oeps, this makes dvi mode graphics hang when not found + \!!toksa\emptytoks + \def\copyMPbufferline{\expandafter\appendtoks\fileline\to\!!toksa}% + \def\dodoprocessMPbuffer##1% + {\doprocessfile\scratchread{\TEXbufferfile{##1}}\copyMPbufferline}% + \processcommalist[#1]\dodoprocessMPbuffer + \@EA\startMPcode\the\!!toksa\stopMPcode % more efficient + \egroup}} + +\def\runMPbuffer + {\dosingleempty\dorunMPbuffer} + +\def\dorunMPbuffer[#1]% processing only + {{\MPruntrue\doprocessMPbuffer[#1]}} + +%D \macros +%D {startMPenvironment, resetMPenvironment} +%D +%D In order to synchronize the main \TEX\ run and the runs +%D local to \METAPOST, environments can be passed. + +\ifx\everyMPTEXgraphic\undefined + \newtoks\everyMPTEXgraphic +\fi + +%D A more general way of passing environments is: + +\def\startMPenvironment % second arg gobbles spaces, so that reset gives \emptytoks + {\bgroup + \catcode`\^^M=\@@space + \dodoubleempty\dostartMPenvironment} + +\long\def\dostartMPenvironment[#1][#2]#3\stopMPenvironment + {\egroup + \doif{#1}\s!reset\resetMPenvironment % reset mp toks + \doif{#1}\v!global{#3}% % use in main doc too + \doif{#1}+{#3}% % use in main doc too + \defconvertedargument\ascii{#3}% + \expandafter\appendtoks\ascii\to\everyMPTEXgraphic} + +\def\resetMPenvironment + {\everyMPTEXgraphic\emptytoks % = is really needed ! + \startMPenvironment + \global\loadfontfileoncetrue + \stopMPenvironment} + +\resetMPenvironment + +\def\useMPenvironmentbuffer[#1]% + {\expanded{\startMPenvironment\noexpand\readfile{\TEXbufferfile{\jobname}}{}{}}\stopMPenvironment} + +% \useMPenvironmentbuffer[mp] % what was this? + +%D This command takes \type {[reset]} as optional +%D argument. +%D +%D \starttyping +%D \startMPenvironment +%D \setupbodyfont[pos,14.4pt] +%D \stopMPenvironment +%D +%D \startMPcode +%D draw btex \sl Hans Hagen etex scaled 5 ; +%D \stopMPcode +%D \stoptyping +%D +%D The \type {\resetMPenvironment} is a quick way to erase +%D the token list. +%D +%D You should be aware of independencies. For instance, if you use a font +%D in a graphic that is not used in the main document, you need to load the +%D typescript at the outer level (either directly or by using the global +%D option). +%D +%D \starttyping +%D \usetypescript[palatino][texnansi] +%D +%D \startMPenvironment +%D \usetypescript[palatino][texnansi] +%D \enableregime[utf] +%D \setupbodyfont[palatino] +%D \stopMPenvironment +%D +%D \startMPpage +%D draw btex aap‒noot coördinatie – één etex ; +%D \stopMPpage +%D \stoptyping + +%D We don't want spurious files, do we? + +%\def\initializeMPgraphics +% {%\ifx\bufferprefix\empty \else +% \immediate\openout\MPwrite\MPgraphicfile.mp +% \immediate\write\MPwrite{end.}% +% \immediate\closeout\MPwrite +% }%\fi} + +% strange : + +% \def\initializeMPgraphicfile +% {\bgroup +% \doinitializeMPgraphicfile +% \MPruntrue +% \doinitializeMPgraphicfile +% \egroup} + +% \def\doinitializeMPgraphicfile +% {\immediate\openout\scratchwrite\MPgraphicfile.mp +% \immediate\write\scratchwrite{end.}% +% \immediate\closeout\scratchwrite} + +\def\initializeMPgraphicfile + {\immediate\openout\scratchwrite\MPgraphicfile.mp + \immediate\write\scratchwrite{end.}% + \immediate\closeout\scratchwrite} + +\def\initializeMPgraphics + {\bgroup + \initializeMPgraphicfile + \ifx\bufferprefix\empty\else + \let\bufferprefix\empty + \initializeMPgraphicfile + \fi + \egroup} + +%D Loading specific \METAPOST\ related definitions is +%D accomplished by: + +\def\douseMPlibrary#1% + {\ifundefined{\c!file\f!metapostprefix#1}% + \letvalueempty{\c!file\f!metapostprefix#1}% + \makeshortfilename[\truefilename{\f!metapostprefix#1}]% + \startreadingfile + \readsysfile\shortfilename{\showmessage\m!metapost1{#1}}\donothing + \stopreadingfile + \fi} + +\def\useMPlibrary[#1]% + {\processcommalist[#1]\douseMPlibrary} + +%D \macros +%D {setMPtext, MPtext, MPstring, MPbetex} +%D +%D To be documented: +%D +%D \starttyping +%D \setMPtext{identifier}{text} +%D +%D \MPtext {identifier} +%D \MPstring{identifier} +%D \MPbetex {identifier} +%D \stoptyping + +\def\@@MPT{@MPT@} + +\def\forceMPTEXgraphic + {\long\def\checkMPTEXgraphic##1{\global\MPTEXgraphictrue}} + +\def\setMPtext#1#2% todo : #1 must be made : safe + {%\forceMPTEXgraphic + \defconvertedargument\ascii{#2}% + \dodoglobal\letvalue{\@@MPT#1}\ascii} + +% \def\MPtext #1{\getvalue{\@@MPT#1}} +% \def\MPstring #1{"\getvalue{\@@MPT#1}"} +% \def\MPbetex #1{btex \getvalue{\@@MPT#1} etex} + +\def\MPtext #1{\executeifdefined{\@@MPT#1}\empty} +\def\MPstring #1{"\executeifdefined{\@@MPT#1}\empty"} +\def\MPbetex #1{btex \executeifdefined{\@@MPT#1}\empty\space etex} + +%D Unfortunately \METAPOST\ does not have \CMYK\ support +%D built in, but by means of specials we can supply the +%D information needed to handle them naturaly. + +\newif\ifMPcmykcolors \MPcmykcolorstrue +\newif\ifMPspotcolors \MPspotcolorstrue + +\startMPinitializations + cmykcolors:=\ifMPcmykcolors true\else false\fi; + spotcolors:=\ifMPspotcolors true\else false\fi; +\stopMPinitializations + +%D In order to communicate conveniently with the \TEX\ +%D engine, we introduce some typesetting variables. + +% todo : backgroundoffsets + +\startMPextensions + color OverlayColor,OverlayLineColor; +\stopMPextensions + +\startMPinitializations + OverlayWidth:=\overlaywidth; + OverlayHeight:=\overlayheight; + OverlayDepth:=\overlayheight; + OverlayColor:=\MPcolor{\overlaycolor}; + OverlayLineWidth:=\overlaylinewidth; + OverlayLineColor:=\MPcolor{\overlaylinecolor}; + % + BaseLineSkip:=\the\baselineskip; + LineHeight:=\the\baselineskip; + BodyFontSize:=\the\bodyfontsize; + % + TopSkip:=\the\topskip; + StrutHeight:=\strutheight; + StrutDepth:=\strutdepth; + % + CurrentWidth:=\the\hsize; + CurrentHeight:=\the\vsize; + % + EmWidth:=\the\emwidth; + ExHeight:=\the\exheight; + % + PageNumber:=\the\pageno; + RealPageNumber:=\the\realpageno; + LastPageNumber:= \lastpage; +\stopMPinitializations + +\appendtoks + \disablediscretionaries + \disablecompoundcharacters +\to\everyMPgraphic + +% New, experimental (if complaints than only in enco-ffr.mkii), well +% Mojca complained that it does not work with utf-8 and textext, see +% ** in meta-tex. + +\appendtoks + \chardef\activecharactermode\zerocount +\to\everyMPgraphic + +\appendtoks + \expanded{\definecolor[currentcolor][\currentcolorname]}% +\to \everyMPgraphic + +\appendtoks + \baselineskip1\baselineskip + \lineheight 1\lineheight + \topskip 1\topskip +\to \everyMPgraphic + +%D Alas, the prologue settings differ per driver. + +\ifx\undefined\MPprologues \def\MPprologues{0} \fi + +\startMPinitializations + prologues:=\MPprologues; + mpprocset:=1; +\stopMPinitializations + +\appendtoks + \def\MPprologues{0}% + \def\MPOSTdriver{dvips}% +\to \everyresetspecials + +%D \macros +%D {PDFMPformoffset} +%D +%D In \PDF, forms are clipped and therefore we have to take +%D precautions to get this right. Since this is related to +%D objects, we use the same offset as used there. + +\def\PDFMPformoffset{\objectoffset} + +%D \macros +%D {insertMPfile} +%D +%D Bypassing the special driver and figure mechanism is not +%D that nice but saves upto 5\% time in embedding \METAPOST\ +%D graphics by using the low level \PDF\ converter directly, +%D given of course that we use \PDFTEX. As a result we need to +%D fool around with the object trigger. + +\newtoks\everyinsertMPfile + +%D First we present the reasonable fast alternative that we +%D happily used for some time. +%D +%D \starttyping +%D \def\insertMPfile#1#2% +%D {\ifx\undefined\externalfigure +%D \message{[insert file #1 here]}% +%D \else +%D \bgroup +%D \the\everyinsertMPfile +%D \externalfigure +%D [#1] +%D [\c!type=\c!mps,\c!object=\v!no,% +%D \c!symbol=\v!yes,\c!reset=\v!yes,% +%D \c!maxwidth=,\c!maxheight=,% +%D \c!frame=\v!off,\c!background=,% +%D #2]% +%D \egroup +%D \fi} +%D \stoptyping +%D +%D However, on a 1 Gig Pentium, the next alternative saves +%D us 20 seconds run time for the 300 page \METAFUN\ manual: + +\let\insertMPfileARG\insertMPfile + +\def\insertMPfile#1#2% in context #2 is empty + {\doifelsenothing{#2}{\doinsertMPfile{#1}}{\insertMPfileARG{#1}{#2}}} + +\def\includeMPasEPS#1% untested !! + {\bgroup + \message{[MP as EPS #1]}% + \the\everyinsertMPfile + \dogetEPSboundingbox{#1}\!!widtha\!!heighta\!!widthb\!!heightb + \setbox\scratchbox\vbox to \!!heightb + {\vfill + \let \@@DriverImageType \c!mps + \def \@@DriverImageFile {#1}% + \edef\@@DriverImageWidth {\the\!!widthb }% + \edef\@@DriverImageHeight{\the\!!heightb}% + \doinsertfile}% + \wd\scratchbox\!!widthb + \dp\scratchbox\zeropoint + \box\scratchbox + \egroup} + +\def\includeMPasPDF#1% + {\bgroup + \the\everyinsertMPfile + \ifinobject \else \chardef\makeMPintoPDFobject\plustwo \fi % when needed + \convertMPtoPDF{#1}{1}{1}% no \plusone ! + \egroup} + +%D So, using a low level approach (thereby avoiding the slower +%D figure analysis macros) pays off. This kind of +%D optimizations are a bit tricky since we must make sure that +%D special resources end up in the (PDF) files. Because the +%D \METAPOST\ to \PDF\ can handle objects itself, it is not +%D that complicated. + +%D We hook a couple of initializations into the graphic +%D macros. + +\appendtoks + \let\figuretypes\c!mps + \runutilityfilefalse + \consultutilityfilefalse +\to \everyinsertMPfile + +%D One more: (still needed?) + +\startMPextensions + def initialize_form_numbers = + do_initialize_numbers; + enddef; +\stopMPextensions + +\startMPinitializations + HSize:=\the\hsize ; + VSize:=\the\vsize ; +\stopMPinitializations + +\startMPextensions + vardef ForegroundBox = + unitsquare xysized(HSize,VSize) + enddef ; + vardef PageFraction = + if \lastpage>1: (\realfolio-1)/(\lastpage-1) else: 1 fi + enddef ; +\stopMPextensions + +%D And some more. These are not really needed since we +%D don't use the normal figure inclusion macros any longer. + +\appendtoks + \externalfigurepostprocessors\emptytoks % safeguard +\to \everyinsertMPfile + +%D We also take care of disabling fancy figure features, that +%D can terribly interfere when dealing with symbols, +%D background graphics and running (postponed) graphics. +%D You won't believe me if I tell you what funny side effects +%D can occur. One took me over a day to uncover when +%D processing the screen version of the \METAFUN\ manual. + +%D For my eyes only: + +\def\doifelseMPgraphic#1{\doifdefinedelse{\@@MPG#1}} + +%D \macros +%D {startMPcolor} +%D +%D The following time consuming method uses \METAPOST\ to +%D calculate a color. This enables a match between colors +%D resulting from a complex calculation (e.g. for a title +%D page) and those in the text. + +% \startuseMPgraphic{somecolors} +% color c[] ; c[1] := .7[red,green] ; c[2] := .7[blue,yellow] ; +% \stopuseMPgraphic + +% \startMPcolor[shade-1][t=.2,a=1] +% \includeMPgraphic{somecolors} ; fill fullcircle withcolor c[1] ; +% \stopMPcolor + +% \startMPcolor[shade-2][t=.2,a=1] +% \includeMPgraphic{somecolors} ; fill fullcircle withcolor c[2] ; +% \stopMPcolor + +% \blackrule[width=\hsize,height=4cm,color=shade-1] +% \blackrule[width=\hsize,height=4cm,color=shade-2] + +\def\startMPcolor + {\dodoubleempty\dostartMPcolor} + +\long\def\dostartMPcolor[#1][#2]#3\stopMPcolor % slow but sometimes handy + {\startnointerference + \def\handleMPgraycolor{\expanded{\defineglobalcolor[#1][s=\!MPgMPa1,#2]}}% + \def\handleMPrgbcolor {\expanded{\defineglobalcolor[#1][r=\!MPgMPa1,g=\!MPgMPa2,b=\!MPgMPa3,#2]}}% + \def\handleMPcmykcolor{\expanded{\defineglobalcolor[#1][c=\!MPgMPa1,m=\!MPgMPa2,y=\!MPgMPa3,k=\!MPgMPa4,#2]}}% + \startMPcode#3\stopMPcode + \stopnointerference} + +%D New: + +\definelayerpreset % no dx,dy - else nasty non-mp placement + [mp] + [\c!y=-\MPury bp, + \c!x=\MPllx bp, + \c!method=\v!fit] + +\definelayer + [mp] + [\c!preset=mp] + +%D Usage: +%D +%D \starttyping +%D \defineproperty[one][layer][state=start] +%D \defineproperty[two][layer][state=stop] +%D +%D \startuseMPgraphic{step-1} +%D fill fullcircle scaled 10cm withcolor red ; +%D \stopuseMPgraphic +%D +%D \startuseMPgraphic{step-2} +%D fill fullcircle scaled 5cm withcolor green ; +%D \stopuseMPgraphic +%D +%D \setlayer[mp]{\property[one]{\useMPgraphic{step-1}}} +%D \setlayer[mp]{\property[two]{\useMPgraphic{step-2}}} +%D +%D \ruledhbox{\flushlayer[mp]} +%D \stoptyping +%D +%D Reusing graphics is also possible (now): +%D +%D \starttyping +%D \startreusableMPgraphic{axis} +%D tickstep := 1cm ; ticklength := 2mm ; +%D drawticks unitsquare xscaled 4cm yscaled 3cm shifted (-1cm,-1cm) ; +%D tickstep := tickstep/2 ; ticklength := ticklength/2 ; +%D drawticks unitsquare xscaled 4cm yscaled 3cm shifted (-1cm,-1cm) ; +%D \stopreusableMPgraphic +%D +%D \startuseMPgraphic{demo} +%D drawpoint "1cm,1.5cm" ; +%D \stopuseMPgraphic +%D +%D \definelayer[mp][preset=mp] +%D \setlayer[mp]{\reuseMPgraphic{axis}} +%D \setlayer[mp]{\useMPgraphic{demo}} +%D \ruledhbox{\flushlayer[mp]} +%D \stoptyping + +%D \macros +%D {startstaticMPfigure,useMPstaticfigure} +%D +%D Static figures are processed only when there has been +%D something changed. Here is Aditya Mahajan's testcase: +%D +%D \startbuffer +%D \startstaticMPfigure{circle} +%D fill fullcircle scaled 1cm withcolor blue; +%D \stopstaticMPfigure +%D +%D \startstaticMPfigure{axis} +%D drawarrow (0,0)--(2cm,0) ; +%D drawarrow (0,0)--(0,2cm) ; +%D label.llft(textext("(0,0)") ,origin) ; +%D \stopstaticMPfigure +%D \stopbuffer +%D +%D \typebuffer \getbuffer + +\def\usestaticMPfigure[#1]% + {\dodoubleempty\externalfigure[\jobname-#1.pdf]} + +\def\startstaticMPfigure + {\begingroup + \obeyMPlines + \dostartstaticMPfigure} + +\def\dostartstaticMPfigure#1#2\stopstaticMPfigure + {\startstaticMPgraphic{\jobname-#1}#2\stopstaticMPgraphic + \endgroup} + +% faster, but more tricky +% +% \def\startstaticMPfigure +% {\doifmodeelse{*\v!first} +% {\begingroup +% \obeyMPlines +% \dostartstaticMPfigure} +% {\gobbleuntil\stopstaticMPfigure}} +% +% \def\dostartstaticMPfigure#1#2\stopstaticMPfigure +% {\startMPstaticgraphic{\jobname-#1}#2\stopMPstaticgraphic +% % dirty trick, don't register, so no second main run of texexec: +% \global\advance\nofMPgraphics \minusone +% \endgroup}} + +%D New: + +% \appendtoks \closeMPgraphicfiles \to \everystoptext + +%D New: + +\newconditional\manyMPspecials % when set to true, > 1000 specials can be used + +\settrue \manyMPspecials % per 1/4/2006 + +\prependtoks + _special_div_ := 1000\ifconditional\manyMPspecials0\fi ; +\to \MPextensions + +%D Needed (will become default): + +\prependtoks + \resetlanguagespecifics +\to \everyMPgraphic + +%D Goody for preventing overflows: + +\def\MPdivten[#1]{\withoutpt\the\dimexpr#1pt/10\relax} + +%D Done. + +\protect \endinput + +%D Experimental: + +\appendtoks + \ifrunMPgraphics \ifcase\systemcommandmode \or + \runMPgraphicsfalse + \fi \fi +\to \everyjob + +% also: +% +% linecap := rounded ; +% linejoin := rounded ; +% drawoptions () ; diff --git a/tex/context/base/meta-ini.mkiv b/tex/context/base/meta-ini.mkiv new file mode 100644 index 000000000..61acbca32 --- /dev/null +++ b/tex/context/base/meta-ini.mkiv @@ -0,0 +1,1275 @@ +%D \module +%D [ file=meta-ini, +%D version=2008.03.25, +%D title=\METAPOST\ Graphics, +%D subtitle=Initialization, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{MetaPost Graphics / Initializations} + +\unprotect + +%D Instead of sharing code with \MKII, I decided to copy +%D the code. Otherwise maintainance becomes a pain and after all, +%D the \MKII\ code will not change. + +\let \useMETAFUNformattrue\relax \let \useMETAFUNformatfalse\relax +\let \longMPlinestrue\relax \let \longMPlinesfalse\relax +\let \runMPgraphicstrue\relax \let \runMPgraphicsfalse\relax +\let \runMPTEXgraphicstrue\relax \let \runMPTEXgraphicsfalse\relax +\let \MPstaticgraphictrue\relax \let \MPstaticgraphicfalse\relax +\let\forceMPTEXgraphictrue\relax \let\forceMPTEXgraphicfalse\relax + +\let \obeyMPlines\relax +\let \forceMPTEXcheck\gobbleoneargument +\let\maxnofMPgraphics\scratchcounter + +\newtoks \MPextensions % mp, once +\newtoks \MPinitializations % tex, each +\newtoks \MPuserinclusions % mp, user +\newtoks \MPfinalizations % mp, user +\newtoks \everyMPgraphic % mp +\newtoks \everyMPTEXgraphic % tex + +\newif\ifMPrun +\def\MPruntimefile{mprun} + +% The next command is, of course, dedicated to Mojca, who +% needs it for gnuplot. Anyway, the whole multiple engine +% mechanism is to keep her gnuplot from interfering. + +\def\startMPdefinitions + {\dosinglegroupempty\dostartMPdefinitions} + +\long\def\dostartMPdefinitions#1#2\stopMPdefinitions + {\edef\currentMPgraphicinstance{#1}% + \ifx\currentMPgraphicinstance\empty + \let\currentMPgraphicinstance\defaultMPgraphicinstance + \fi + \global\MPinstancetoks\expandafter{\the\MPinstancetoks#2}} + +\long\def\startMPextensions#1\stopMPextensions + {\global\MPextensions\expandafter{\the\MPextensions#1}} + +\long\def\startMPinitializations#1\stopMPinitializations + {\global\MPinitializations\expandafter{\the\MPinitializations#1}} + +\long\def\startMPinclusions + {\dosingleempty\dostartMPinclusions} + +\long\def\dostartMPinclusions[#1]#2\stopMPinclusions + {\doifnot{#1}{+}{\global\MPuserinclusions\emptytoks}% + \global\MPuserinclusions\expandafter{\the\MPuserinclusions#2}} + +\def\MPinclusions + {\dosingleempty\doMPinclusions} + +\long\def\doMPinclusions[#1]#2% + {\doifnot{#1}{+}{\global\MPuserinclusions\emptytoks}% + \global\MPuserinclusions\expandafter{\the\MPuserinclusions#2}} + +\def\presetMPdefinitions + {\edef\overlaywidth {\overlaywidth \space}% + \edef\overlayheight {\overlayheight \space}% + \edef\overlaylinewidth{\overlaylinewidth\space}% + \edef\currentwidth {\the\hsize \space}% + \edef\currentheight {\the\vsize \space}} + +\def\currentMPformat{metafun} + +\def\@@MPF{@MPF@} + +\def\MPinstancetoks{\csname\@@MPF::\currentMPgraphicinstance\endcsname} + +\unexpanded\def\defineMPinstance + {\dodoubleargument\dodefineMPinstance} + +\def\dodefineMPinstance[#1][#2]% + {\ifcsname\@@MPF::#1\endcsname\else\expandafter\newtoks\csname\@@MPF::#1\endcsname\fi + \MPinstancetoks\emptytoks % in case we redefine + \getparameters[\@@MPF#1][\s!format=mpost,\s!extensions=\v!no,\s!initializations=\v!no,#2]} + +\def\resetMPinstance[#1]% + {\writestatus\m!metapost{reset will be implemented when needed}} + +\def\defaultMPgraphicinstance{metafun} + +\def\splitMPgraphicname[#1]% + {\dosplitMPgraphicname[#1::::]} + +\def\dosplitMPgraphicname[#1::#2::#3]% instance :: + {\edef\currentMPgraphicname{#2}% + \ifx\currentMPgraphicname\empty + \edef\currentMPgraphicname{#1}% + \let\currentMPgraphicinstance\defaultMPgraphicinstance + \else + \edef\currentMPgraphicinstance{#1}% + \fi + \edef\currentMPgraphicformat + {\ifcsname\@@MPF\currentMPgraphicinstance\s!format\endcsname + \csname\@@MPF\currentMPgraphicinstance\s!format\endcsname + \else + \defaultMPgraphicinstance + \fi}} + +\def\currentMPgraphicinstance{\defaultMPgraphicinstance} +\def\currentMPgraphicformat {\currentMPgraphicinstance} + +\defineMPinstance[metafun] [\s!format=metafun,\s!extensions=\v!yes,\s!initializations=\v!yes] +\defineMPinstance[extrafun][\s!format=metafun,\s!extensions=\v!yes,\s!initializations=\v!yes] +\defineMPinstance[mprun] [\s!format=metafun,\s!extensions=\v!yes,\s!initializations=\v!yes] +\defineMPinstance[metapost][\s!format=mpost] +\defineMPinstance[nofun] [\s!format=mpost] + +\def\beginMPgraphicgroup#1% + {\begingroup + \splitMPgraphicname[#1]} + +\def\endMPgraphicgroup + {\endgroup} + +\newconditional \METAFUNinitialized + +\def\MPaskedfigure{false} + +\def\currentMPinitializations + {\ifconditional\includeMPinitializations\the\MPinitializations;\fi\theMPrandomseed;} + +\def\currentMPpreamble + {\ifconditional\includeMPextensions\the\MPextensions;\the\MPuserinclusions;\fi\the\MPinstancetoks;} + +\def\dostartcurrentMPgraphic + {\begingroup + \enableincludeMPgraphics + \the\everyMPgraphic + \presetMPdefinitions + \setMPrandomseed % this has to change + % we need to preexpand the token lists + \doifelsevalue{\@@MPF\currentMPgraphicinstance\s!extensions}\v!yes + {\settrue \includeMPextensions\letgvalue{\@@MPF\currentMPgraphicinstance\s!extensions}\v!no} + {\setfalse\includeMPextensions}% + \doifelsevalue{\@@MPF\currentMPgraphicinstance\s!initializations}\v!yes + {\settrue \includeMPinitializations}% + {\setfalse\includeMPinitializations}} + +\def\dostopcurrentMPgraphic + {\global\MPinstancetoks\emptytoks + \global\settrue\METAFUNinitialized % becomes obsolete + \endgroup} + +\unexpanded\long\def\processMPgraphic#1% todo: extensions and inclusions outside beginfig + {\dostartcurrentMPgraphic + \forgetall + \setbox\MPgraphicbox\hbox\bgroup + \normalexpanded{\noexpand\ctxlua{metapost.graphic( + "\currentMPgraphicinstance", + "\currentMPgraphicformat", + \!!bs#1\!!es, + \!!bs\currentMPinitializations\!!es, + \!!bs\currentMPpreamble\!!es, + \MPaskedfigure + )}}% + \egroup + \placeMPgraphic + \dostopcurrentMPgraphic} + +\newif\ifsetMPrandomseed \setMPrandomseedtrue % false by default + +\def\setMPrandomseed + {\let\theMPrandomseed\empty + \ifsetMPrandomseed \ifx\getrandomnumber\undefined \else + \getrandomnumber\localMPseed\zerocount{4095}% + \def\theMPrandomseed{randomseed:=\localMPseed}% + \fi\fi} + +%D To be integrated + +\def\@@MPG{@MPG@} + +\def\doifMPgraphicelse#1% + {\ifcsname\@@MPG#1\endcsname\expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments\fi} + +\def\includeMPgraphic#1% + {\executeifdefined{\@@MPG#1};} % ; if not found + +\def\enableincludeMPgraphics + {\let\handleuseMPgraphic \thirdofthreearguments + \let\handlereusableMPgraphic\thirdofthreearguments} + +\let\MPdrawingdata\empty + +\newif\ifMPdrawingdone \MPdrawingdonefalse + +\def\resetMPdrawing + {\globallet\MPdrawingdata\empty + \global\MPdrawingdonefalse} + +\def\pushMPdrawing + {\globalpushmacro\MPdrawingdata + \globallet\MPdrawingdata\empty} + +\def\popMPdrawing + {\globalpopmacro\MPdrawingdata} + +\def\getMPdrawing{\dosinglegroupempty\dogetMPdrawing} + +\def\startMPdrawing + {\dosingleempty\dostartMPdrawing} + +\long\def\dostartMPdrawing[#1]#2\stopMPdrawing + {\relax + \bgroup + \enableincludeMPgraphics + \presetMPdefinitions % in case #2 has measures + \doifelse{#1}{-}{\convertargument#2\to\asciia}{\long\def\asciia{#2}}% + \long\xdef\MPdrawingdata{\MPdrawingdata\asciia}% + \egroup} + +\let\stopMPdrawing\relax + +\let\MPdrawingdata\empty + +\newif\ifMPshiftdrawing \MPshiftdrawingfalse + +\def\resetMPdrawing + {\globallet\MPdrawingdata\empty + \global\MPdrawingdonefalse} + +\def\pushMPdrawing + {\globalpushmacro\MPdrawingdata + \globallet\MPdrawingdata\empty} + +\def\popMPdrawing + {\globalpopmacro\MPdrawingdata} + +\def\getMPdrawing + {\ifMPdrawingdone + \expandafter\processMPgraphic\expandafter{\MPdrawingdata}% is this expansion still needed? + \fi} + +\def\startMPdrawing + {\dosingleempty\dostartMPdrawing} + +\long\def\dostartMPdrawing[#1]#2\stopMPdrawing + {\relax + \bgroup + \enableincludeMPgraphics + \presetMPdefinitions % in case #2 has measures + \doifelse{#1}{-}{\convertargument#2\to\asciia}{\long\def\asciia{#2}}% + \long\xdef\MPdrawingdata{\MPdrawingdata\asciia}% + \egroup} + +\let\stopMPdrawing\relax + +\let\stopMPclip\relax + +\long\def\startMPclip#1#2\stopMPclip % todo: store at the lua end or just store less + {\long\setgvalue{MPC:#1}{#2}} + +\def\grabMPclippath#1#2#3#4#5% #5 is alternative + {\begingroup + \edef\width {#3\space}\let\overlaywidth \width + \edef\height{#4\space}\let\overlayheight\height + \ifcsname MPC:#1\endcsname + \dostartcurrentMPgraphic + \xdef\MPclippath{\normalexpanded{\noexpand\ctxlua{metapost.theclippath( + "\currentMPgraphicinstance", + "\currentMPgraphicformat", + \!!bs\getvalue{MPC:#1}\!!es, + \!!bs\currentMPinitializations\!!es, + \!!bs\currentMPpreamble\!!es + )}}}% + \dostopcurrentMPgraphic + \ifx\MPclippath\empty\xdef\MPclippath{#5}\fi + \else + \xdef\MPclippath{#5}% + \fi + % #2 : method is obsolete, only pdf now, we can always + % gsub the result to ps + \endgroup} + +%D Next we will use these support macros. + +\startMPextensions + if unknown context_tool: input mp-tool; fi; + if unknown context_spec: input mp-spec; fi; + if unknown context_grph: input mp-grph; fi; +\stopMPextensions + +%D Since we want lables to follow the document settings, we +%D also set the font related variables. + +\startMPinitializations % scale is not yet ok + defaultfont:="\truefontname{Regular}"; + defaultscale:=\the\bodyfontsize/10pt; +\stopMPinitializations + +% watch out, this is a type1 font because mp can only handle 8 bit fonts + +\startMPinitializations % scale is not yet ok + defaultfont:="rm-lmtt10"; +\stopMPinitializations + +%D A signal that we're in combines \CONTEXT||\METAFUN mode: + +\startMPextensions + string contextversion; + contextversion:="\contextversion"; +\stopMPextensions + +%D Some safeguards: +%D +%D \starttyping +%D \appendtoks \cleanupfeatures \to \everyMPgraphic +%D \stoptyping +%D +%D No, we don't want that (else we loose UTF etc). + +%D Another one: + +\prependtoks \MPstaticgraphictrue \to \everyoverlay +\prependtoks \MPstaticgraphictrue \to \everypagebody + +%D \macros +%D {setupMPvariables} +%D +%D When we build collections of \METAPOST\ graphics, like +%D background and buttons, the need for passing settings +%D arises. By (mis|)|using the local prefix that belongs to +%D \type {\framed}, we get a rather natural interface to +%D backgrounds. To prevent conflicts, we will use the \type +%D {-} in \METAPOST\ specific variables, like: +%D +%D \starttyping +%D \setupMPvariables[meta:button][size=20pt] +%D \stoptyping + +\def\@@meta{meta:} + +\unexpanded\def\setupMPvariables + {\dodoubleempty\dosetupMPvariables} + +\def\dosetupMPvariables[#1][#2]% + {\ifsecondargument + \getrawparameters[#1:][#2]% brr, todo: [\@@meta#1:] + \else + \getrawparameters[\@@meta][#1]% + \fi} + +\let\@@framed\s!unknown + +\def\MPvariable#1% + {\csname + \ifcsname\@@framed\@@meta#1\endcsname\@@framed\fi\@@meta#1% + \endcsname} + +\let\MPvar\MPvariable + +\let\setMPvariables\setupMPvariables + +\def\MPrawvar#1#2{\csname#1:#2\endcsname} + +\def\presetMPvariable + {\dodoubleargument\dopresetMPvariable} + +\def\dopresetMPvariable[#1][#2=#3]% + {\ifcsname#1:#2\endcsname\else\setvalue{#1:#2}{#3}\fi} + +\def\useMPvariables + {\dodoubleargument\douseMPvariables} + +\def\douseMPvariables[#1][#2]% + {\def\@@meta{#1:}% + \prepareMPvariables{#2}} + +%D \macros +%D {startuniqueMPgraphic, uniqueMPgraphic} +%D +%D This macros is probably of most use to myself, since I like +%D to use graphics that adapt themselves. The next \METAPOST\ +%D kind of graphic is both unique and reused when possible. +%D +%D \starttyping +%D \defineoverlay[example][\uniqueMPgraphic{test}] +%D +%D \startuniqueMPgraphic {test} +%D draw unitsquare xscaled \overlaywidth yscaled \overlayheight ; +%D \stopuniqueMPgraphic +%D \stoptyping + +\def\overlaystamp % watch the \MPcolor, since colors can be redefined + {\overlaywidth:\overlayheight:\overlaydepth:\MPcolor\overlaycolor:\MPcolor\overlaylinecolor} + +%D A better approach is to let additional variables play a role +%D in determining the uniqueness. In the next macro, the +%D second, optional, argument is used to guarantee the +%D uniqueness, as well as prepare variables for passing them to +%D \METAPOST. +%D +%D \starttyping +%D \startuniqueMPgraphic{meta:hash}{gap,angle,...} +%D \stoptyping +%D +%D The calling macro also accepts a second argument. For +%D convenient use in overlay definitions, we use \type {{}} +%D instead of \type {[]}. +%D +%D \starttyping +%D \uniqueMPgraphic{meta:hash}{gap=10pt,angle=30} +%D \stoptyping + +\newcount\MPobjectcounter +\newbox \MPgraphicbox + +\chardef\MPboxmode\zerocount + +\def\doobeyMPboxdepth % mode = 1 + {\setbox\MPgraphicbox\hbox{\hskip\MPllx\onebasepoint\raise\MPlly\onebasepoint\box\MPgraphicbox}} + +\def\doignoreMPboxdepth % mode = 2 + {\normalexpanded + {\noexpand\doobeyMPboxdepth + \wd\MPgraphicbox\the\wd\MPgraphicbox + \ht\MPgraphicbox\the\ht\MPgraphicbox + \dp\MPgraphicbox\the\dp\MPgraphicbox}} + +\def\obeyMPboxdepth {\chardef\MPboxmode\plusone} +\def\ignoreMPboxdepth{\chardef\MPboxmode\plustwo} +\def\normalMPboxdepth{\chardef\MPboxmode\zerocount} + +% compatibility hack: + +\let\MPshiftdrawingtrue \ignoreMPboxdepth +\let\MPshiftdrawingfalse\normalMPboxdepth + +\unexpanded\def\placeMPgraphic + {\ifcase\MPboxmode + \or % 1 + \doobeyMPboxdepth + \or % 2 + \doignoreMPboxdepth + \fi + \box\MPgraphicbox} + +\def\reuseMPbox#1#2#3#4#5% space delimiting would save some tokens + {\xdef\MPllx{#2}% but it's not worth the effort and looks + \xdef\MPlly{#3}% ugly as well + \xdef\MPurx{#4}% + \xdef\MPury{#5}% + \hbox{\forcecolorhack\getobject{MP}{#1}}} % else no proper color intent + +\long\def\handleuniqueMPgraphic#1#2#3% + {\begingroup + \def\@@meta{#1:}% + \extendMPoverlaystamp{#2}% incl prepare + \ifcsname\@@MPG\overlaystamp:#1\endcsname\else + \enableincludeMPgraphics % redundant + \global\advance\MPobjectcounter\plusone + \setobject{MP}{\number\MPobjectcounter}\hbox{\processMPgraphic{#3}}% was vbox, graphic must end up as hbox + \setxvalue{\@@MPG\overlaystamp:#1}{\noexpand\reuseMPbox{\number\MPobjectcounter}{\MPllx}{\MPlly}{\MPurx}{\MPury}}% + \fi + \getvalue{\@@MPG\overlaystamp:#1}% + \endgroup} + +\long\unexpanded\def\startuniqueMPgraphic + {\dodoublegroupempty\dostartuniqueMPgraphic} + +\long\def\dostartuniqueMPgraphic#1#2#3\stopuniqueMPgraphic% + {\long\setgvalue{\@@MPG#1}{\handleuniqueMPgraphic{#1}{#2}{#3}}} + +\unexpanded\def\uniqueMPgraphic + {\dodoublegroupempty\douniqueMPgraphic} + +\def\douniqueMPgraphic#1#2% + {\beginMPgraphicgroup{#1}% + \setupMPvariables[\currentMPgraphicname][#2]% + \getvalue{\@@MPG\currentMPgraphicname}\empty + \endMPgraphicgroup} + +\let\stopuniqueMPcode \relax % so that we can use it in \expanded + +\long\def\handleuseMPgraphic#1#2#3% + {\begingroup + \def\@@meta{#1:}% + \prepareMPvariables{#2}% + \enableincludeMPgraphics % redundant + \processMPgraphic{#3}% + \endgroup} + +\long\unexpanded\def\startuseMPgraphic + {\dodoublegroupempty\dostartuseMPgraphic} + +\long\def\dostartuseMPgraphic#1#2#3\stopuseMPgraphic + {\long\setgvalue{\@@MPG#1}{\handleuseMPgraphic{#1}{#2}{#3}}} + +\long\unexpanded\def\startusableMPgraphic % redundant but handy + {\dodoublegroupempty\dostartusableMPgraphic} + +\long\def\dostartusableMPgraphic#1#2#3\stopusableMPgraphic + {\long\setgvalue{\@@MPG#1}{\handleuseMPgraphic{#1}{#2}{#3}}} + +\let\stopuseMPgraphic \relax % so that we can use it in \expanded +\let\stopusableMPgraphic \relax % so that we can use it in \expanded + +\long\def\handlereusableMPgraphic#1#2#3% + {\begingroup + \def\@@meta{#1:}% + \prepareMPvariables{#2}% + \enableincludeMPgraphics % redundant + \global\advance\MPobjectcounter\plusone + \setobject{MP}{\number\MPobjectcounter}\hbox{\processMPgraphic{#3}}% was vbox, graphic must end up as hbox + \setxvalue{\@@MPG#1}{\noexpand\reuseMPbox{\number\MPobjectcounter}{\MPllx}{\MPlly}{\MPurx}{\MPury}}% + \getvalue{\@@MPG#1}% + \endgroup} + +\long\unexpanded\def\startreusableMPgraphic + {\dodoublegroupempty\dostartreusableMPgraphic} + +\long\def\dostartreusableMPgraphic#1#2#3\stopreusableMPgraphic + {\long\setgvalue{\@@MPG#1}{\handlereusableMPgraphic{#1}{#2}{#3}}} + +\let\stopreusableMPgraphic \relax % so that we can use it in \expanded + +\unexpanded\def\useMPgraphic + {\dodoublegroupempty\douseMPgraphic} + +\def\douseMPgraphic#1#2% + {\beginMPgraphicgroup{#1}% + \doifsomething{#2}{\setupMPvariables[\currentMPgraphicname][#2]}% + \getvalue{\@@MPG\currentMPgraphicname}\empty + \endMPgraphicgroup} + +\let\reuseMPgraphic \useMPgraphic % we can save a setup here if needed +\let\reusableMPgraphic\reuseMPgraphic % we can save a setup here if needed + +\let\stopuseMPcode \relax % so that we can use it in \expanded +\let\stopusableMPcode \relax % so that we can use it in \expanded +\let\stopreusableMPcode \relax % so that we can use it in \expanded +\let\stopuniqueMPcode \relax % so that we can use it in \expanded + +\def\enableincludeMPgraphics + {\let\handleuseMPgraphic \thirdofthreearguments + \let\handlereusableMPgraphic\thirdofthreearguments} + +%D \macros +%D {startuniqueMPpagegraphic,uniqueMPpagegraphic} +%D +%D Experimental. + +\def\MPpageprefix{\doifoddpageelse oe:} + +\def\overlaypagestamp + {\MPpageprefix\overlaywidth:\overlayheight:\overlaydepth:\MPcolor\overlaycolor:\MPcolor\overlaylinecolor} + +\long\unexpanded\def\startuniqueMPpagegraphic + {\dodoublegroupempty\dostartuniqueMPpagegraphic} + +\long\def\dostartuniqueMPpagegraphic#1#2#3\stopuniqueMPpagegraphic + {\long\setgvalue{\@@MPG o:#1}{\handleuniqueMPgraphic{o:#1}{#2}{#3}}% + \long\setgvalue{\@@MPG e:#1}{\handleuniqueMPgraphic{e:#1}{#2}{#3}}} + +\unexpanded\def\uniqueMPpagegraphic + {\dodoublegroupempty\douniqueMPpagegraphic} + +\def\douniqueMPpagegraphic#1#2% + {\beginMPgraphicgroup{#1}% + \let\overlaystamp\overlaypagestamp + \setupMPvariables[\MPpageprefix\currentMPgraphicname][#2]% prefix is new here + \getvalue{\@@MPG\MPpageprefix\currentMPgraphicname}{}% + \endMPgraphicgroup} + +%D One way of defining a stamp is: +%D +%D \starttyping +%D \def\extendMPoverlaystamp#1% +%D {\def\docommand##1% +%D {\edef\overlaystamp{\overlaystamp:\MPvariable{##1}}}% +%D \processcommalist[#1]\docommand} +%D \stoptyping + +%D Since we need to feed \METAPOST\ with expanded dimensions, +%D we introduce a dedicated expansion engine. + +\def\prepareMPvariable#1% + {\ifcsname\@@framed\@@meta#1\endcsname + \doprepareMPvariable{\@@framed\@@meta#1}% + \else + \doprepareMPvariable{\@@meta#1}% + \fi} + +% \startlines +% \def\xxx{\lineheight} \doprepareMPvariable{xxx} \xxx +% \def\xxx{2pt} \doprepareMPvariable{xxx} \xxx +% \def\xxx{2} \doprepareMPvariable{xxx} \xxx +% \def\xxx{\scratchcounter} \doprepareMPvariable{xxx} \xxx +% \def\xxx{red} \doprepareMPvariable{xxx} \xxx +% \def\xxx{0.4} \doprepareMPvariable{xxx} \xxx +% \stoplines + +\def\doprepareMPvariable#1% + {\edef\theMPvariable{\getvalue{#1}}% + \doifelsenothing\theMPvariable + {\setevalue{#1}{\MPcolor{black}}} + {\defconvertedcommand\ascii\theMPvariable % otherwise problems + \doifcolorelse \ascii % with 2\bodyfontsize + {\setevalue{#1}{\MPcolor\theMPvariable}} + {% can be aux macro + \setbox\scratchbox\hbox{\scratchdimen\theMPvariable sp}% + \ifdim\wd\scratchbox=\zeropoint + % \scratchcounter\theMPvariable + % \setevalue{#1}{\the\scratchcounter}% + % also accepts 0.number : + \setevalue{#1}{\number\theMPvariable}% + \else + \scratchdimen\theMPvariable + \setevalue{#1}{\the\scratchdimen}% + \fi}}} + +%D We redefine \type {\extendMPoverlaystamp} to preprocess +%D variables using \type {\prepareMPvariable}. + +\def\doextendMPoverlaystamp#1% + {\prepareMPvariable{#1}% + \edef\overlaystamp{\overlaystamp:\MPvariable{#1}}} + +\def\extendMPoverlaystamp#1% + {\processcommalist[#1]\doextendMPoverlaystamp} + +\def\prepareMPvariables#1% + {\processcommalist[#1]\prepareMPvariable} + +%D \macros +%D {MPdatafile} +%D +%D We redefine a macro from \type {supp-mps.tex}: + +\def\MPdataMPDfile{\jobname-mpgraph.mpd} +\def\MPdataMPOfile{\jobname-mpgraph.mpo} +\def\MPdataMPYfile{\jobname-mpgraph.mpy} + +\startMPextensions + boolean collapse_data; collapse_data:=true; + def data_mpd_file = "\MPdataMPDfile" enddef ; + def data_mpo_file = "\MPdataMPOfile" enddef ; + def data_mpy_file = "\MPdataMPYfile" enddef ; +\stopMPextensions + +\chardef\currentMPgraphic\plusone + +\def\getMPdata + {\let\MPdata\secondoftwoarguments + \startreadingfile + % \startnointerference % no, else we need to do all data global + \readlocfile\MPdataMPDfile\donothing\donothing + % \stopnointerference + \stopreadingfile} + +%D \macros +%D {MPrunfile} +%D +%D This one is more abstract and does not assume knowledge +%D of buffer prefixes. + +\def\MPrunfile#1% + {\bufferprefix mprun.#1} + +%D For the moment, the next one is a private macro: + +\def\processMPbuffer + {\dosingleempty\doprocessMPbuffer} + +\def\doprocessMPbuffer[#1]% + {\doifelsenothing{#1} + {\dodoprocessMPbuffer{\jobname}} + {\dodoprocessMPbuffer{#1}}} + +% we need to go via a toks because we have no multiline print in +% luatex (i.e. tex.sprint does not interpret lines) and therefore +% omits all after a comment token + +\newtoks\mpbuffertoks + +\def\doprocessMPbuffer[#1]% + {\doifelsenothing{#1} + {\doprocessMPbuffer[\jobname]} + {\beginMPgraphicgroup{#1}% + % we need this trick because tex.sprint does not interprets newlines and the scanner + % stops at a newline; also, we do need to flush the buffer under a normal catcode + % regime in order to expand embedded tex macros; #1 can be a list + \processMPgraphic{\ctxlua{buffers.feedback("\currentMPgraphicname")}}% + \endMPgraphicgroup}} + +\def\runMPbuffer + {\dosingleempty\dorunMPbuffer} + +\def\dorunMPbuffer[#1]% processing only + {\startnointerference\doprocessMPbuffer[#1]\stopnointerference} + +%D \macros +%D {startMPenvironment, resetMPenvironment} +%D +%D In order to synchronize the main \TEX\ run and the runs +%D local to \METAPOST, environments can be passed. + +\ifx\everyMPTEXgraphic\undefined + \newtoks\everyMPTEXgraphic +\fi + +%D A more general way of passing environments is: + +\def\startMPenvironment % second arg gobbles spaces, so that reset gives \emptytoks + {\dodoubleempty\dostartMPenvironment} + +\long\def\dostartMPenvironment[#1][#2]#3\stopMPenvironment + {\doif{#1}\s!reset\resetMPenvironment % reset mp toks + \doif{#1}\v!global{#3}% % use in main doc too + \doif{#1}+{#3}% % use in main doc too + \ctxlua{metapost.tex.set(\!!bs\detokenize{#3}\!!es)}} + +\def\resetMPenvironment + {\ctxlua{metapost.tex.reset()}} + +\resetMPenvironment + +\def\useMPenvironmentbuffer[#1]% + {\ctxlua{metapost.tex.set(buffers.content("#1"))}} + +%D This command takes \type {[reset]} as optional +%D argument. +%D +%D \starttyping +%D \startMPenvironment +%D \setupbodyfont[pos,14.4pt] +%D \stopMPenvironment +%D +%D \startMPcode +%D draw btex \sl Hans Hagen etex scaled 5 ; +%D \stopMPcode +%D \stoptyping +%D +%D The most simple case: + +\def\startMPcode{\dosinglegroupempty\dostartMPcode} + +\def\dostartMPcode + {\iffirstargument + \expandafter\dodostartMPcode + \else + \expandafter\nodostartMPcode + \fi} + +\def\dodostartMPcode#1#2\stopMPcode + {\beginMPgraphicgroup{#1::\s!dummy}% name does not matter + \processMPgraphic{#2}% + \endMPgraphicgroup} + +\def\nodostartMPcode#1#2\stopMPcode + {\processMPgraphic{#2}} + +\let\stopMPcode\relax + +% a bit nasty (also needed for compatibility: + +% \startMPrun input mp-www.mp ; \stopMPrun +% \externalfigure[mprun.3][width=10cm,height=8cm] + +% \startMPrun{mprun} input mp-www.mp ; \stopMPrun % instance +% \externalfigure[mprun.4][width=10cm,height=8cm] + +\let\MPruninstance\defaultMPgraphicinstance + +\def\useMPrun#1#2% name n + {\begingroup + \def\MPaskedfigure{#2}% + \doifelsenothing{#1} + {\useMPgraphic{mprun}}% + {\useMPgraphic{#1}}% + \endgroup} + +\def\startMPrun + {\dosinglegroupempty\dostartMPrun} + +\long\def\dostartMPrun#1#2\stopMPrun + {\iffirstargument + \startuseMPgraphic{#1}#2\stopuseMPgraphic + \else + \startuseMPgraphic{mprun}#2\stopuseMPgraphic + \fi} + +% for old time sake + +\def\dostartMPgraphic + {\iffirstargument + \expandafter\dodostartMPgraphic + \else + \expandafter\nodostartMPgraphic + \fi} + +\def\dodostartMPgraphic#1#2\stopMPgraphic + {\beginMPgraphicgroup{#1::\s!dummy}% name does not matter + \processMPgraphic{#2}% + \endMPgraphicgroup} + +\def\nodostartMPgraphic#1#2\stopMPcode + {\processMPgraphic{#2}} + +\let\stopMPcode\relax + +%D The \type {\resetMPenvironment} is a quick way to erase +%D the token list. +%D +%D You should be aware of independencies. For instance, if you use a font +%D in a graphic that is not used in the main document, you need to load the +%D typescript at the outer level (either directly or by using the global +%D option). +%D +%D \starttyping +%D \usetypescript[palatino][texnansi] +%D +%D \startMPenvironment +%D \usetypescript[palatino][texnansi] +%D \enableregime[utf] +%D \setupbodyfont[palatino] +%D \stopMPenvironment +%D +%D \startMPpage +%D draw btex aap‒noot coördinatie – één etex ; +%D \stopMPpage +%D \stoptyping + +%D Loading specific \METAPOST\ related definitions is +%D accomplished by: + +\def\douseMPlibrary#1% + {\ifcsname\c!file\f!metapostprefix#1\endcsname\else + \letvalueempty{\c!file\f!metapostprefix#1}% + \makeshortfilename[\truefilename{\f!metapostprefix#1}]% + \startreadingfile + \readsysfile\shortfilename{\showmessage\m!metapost1{#1}}\donothing + \stopreadingfile + \fi} + +\def\useMPlibrary[#1]% + {\processcommalist[#1]\douseMPlibrary} + +%D \macros +%D {setMPtext, MPtext, MPstring, MPbetex} +%D +%D To be documented: +%D +%D \starttyping +%D \setMPtext{identifier}{text} +%D +%D \MPtext {identifier} +%D \MPstring{identifier} +%D \MPbetex {identifier} +%D \stoptyping + +\def\@@MPT{@MPT@} + +\def\forceMPTEXgraphic + {\long\def\checkMPTEXgraphic##1{\global\MPTEXgraphictrue}} + +\def\setMPtext#1#2% todo : #1 must be made : safe + {%\forceMPTEXgraphic + \defconvertedargument\ascii{#2}% + \dodoglobal\letvalue{\@@MPT#1}\ascii} + +\def\MPtext #1{\executeifdefined{\@@MPT#1}\empty} +\def\MPstring #1{"\executeifdefined{\@@MPT#1}\empty"} +\def\MPbetex #1{btex \executeifdefined{\@@MPT#1}\empty\space etex} + +%D Unfortunately \METAPOST\ does not have \CMYK\ support +%D built in, but by means of specials we can supply the +%D information needed to handle them naturaly. + +% \newif\ifMPcmykcolors \MPcmykcolorstrue +% \newif\ifMPspotcolors \MPspotcolorstrue + +\startMPinitializations + cmykcolors:=\ifMPcmykcolors true\else false\fi; + spotcolors:=\ifMPspotcolors true\else false\fi; +\stopMPinitializations + +%D In order to communicate conveniently with the \TEX\ +%D engine, we introduce some typesetting variables. + +\startMPextensions + color OverlayColor,OverlayLineColor; +\stopMPextensions + +\startMPinitializations + OverlayWidth:=\overlaywidth; + OverlayHeight:=\overlayheight; + OverlayDepth:=\overlayheight; + OverlayColor:=\MPcolor{\overlaycolor}; + OverlayLineWidth:=\overlaylinewidth; + OverlayLineColor:=\MPcolor{\overlaylinecolor}; + % + BaseLineSkip:=\the\baselineskip; + LineHeight:=\the\baselineskip; + BodyFontSize:=\the\bodyfontsize; + % + TopSkip:=\the\topskip; + StrutHeight:=\strutheight; + StrutDepth:=\strutdepth; + % + CurrentWidth:=\the\hsize; + CurrentHeight:=\the\vsize; + % + EmWidth:=\the\emwidth; + ExHeight:=\the\exheight; + % + PageNumber:=\the\pageno; + RealPageNumber:=\the\realpageno; + LastPageNumber:= \lastpage; +\stopMPinitializations + +\appendtoks + \disablediscretionaries + \disablecompoundcharacters +\to \everyMPgraphic + +\appendtoks % before color + %\normalexpanded{\noexpand\definecolor[currentcolor][\currentcolorname]}% + \doregistercolor{currentcolor}\currentcolorname +\to \everyMPgraphic + +% \color[green]{abc \startMPcode +% fill fullcircle scaled 3cm withoutcolor; +% fill fullcircle scaled 2cm withcolor \MPcolor{currentcolor} ; +% fill fullcircle scaled 1cm withcolor \MPcolor{red} ; +% \stopMPcode def} + +% \appendtoks +% \doactivatecolor\s!black\forcecolorhack % we can also move this to the backend +% \to \everyMPgraphic + +\appendtoks + \baselineskip1\baselineskip + \lineheight 1\lineheight + \topskip 1\topskip +\to \everyMPgraphic + +\appendtoks + \let \# \letterhash + \let \_ \letterunderscore + \let \& \letterampersand + \let \{ \letteropenbrace + \let \} \letterclosebrace +\to \everyMPgraphic + +\startMPinitializations + prologues:=0; + mpprocset:=1; +\stopMPinitializations + +%D \macros +%D {PDFMPformoffset} +%D +%D In \PDF, forms are clipped and therefore we have to take +%D precautions to get this right. Since this is related to +%D objects, we use the same offset as used there. + +\def\PDFMPformoffset{\objectoffset} + +% %D \macros +% %D {insertMPfile} +% %D +% %D Bypassing the special driver and figure mechanism is not +% %D that nice but saves upto 5\% time in embedding \METAPOST\ +% %D graphics by using the low level \PDF\ converter directly, +% %D given of course that we use \PDFTEX. As a result we need to +% %D fool around with the object trigger. + +\newtoks\everyinsertMPfile + +% removed in backend: +% +% \def\doinsertMPfile#1% +% {\doiffileelse{./#1}{\includeMPasPDF{./#1}}{\message{[MP #1]}}} +% +% \let\insertMPfileARG\insertMPfile +% +% \def\insertMPfile#1#2% in context #2 is empty +% {\doifelsenothing{#2}{\doinsertMPfile{#1}}{\insertMPfileARG{#1}{#2}}} +% +% \def\includeMPasEPS#1% untested !! +% {\bgroup +% \message{[MP as EPS #1]}% +% \the\everyinsertMPfile +% \dogetEPSboundingbox{#1}\!!widtha\!!heighta\!!widthb\!!heightb +% \setbox\scratchbox\vbox to \!!heightb +% {\vfill +% \let \@@DriverImageType \c!mps +% \def \@@DriverImageFile {#1}% +% \edef\@@DriverImageWidth {\the\!!widthb }% +% \edef\@@DriverImageHeight{\the\!!heightb}% +% \doinsertfile}% +% \wd\scratchbox\!!widthb +% \dp\scratchbox\zeropoint +% \box\scratchbox +% \egroup} +% +% \def\includeMPasPDF#1% +% {\bgroup +% \the\everyinsertMPfile +% \ifinobject \else \chardef\makeMPintoPDFobject\plustwo \fi % when needed +% \convertMPtoPDF{#1}{1}{1}% no \plusone ! +% \egroup} +% +% %D So, using a low level approach (thereby avoiding the slower +% %D figure analysis macros) pays off. This kind of +% %D optimizations are a bit tricky since we must make sure that +% %D special resources end up in the (PDF) files. Because the +% %D \METAPOST\ to \PDF\ can handle objects itself, it is not +% %D that complicated. +% +% %D We hook a couple of initializations into the graphic +% %D macros. +% +% \appendtoks +% \let\figuretypes\c!mps +% \runutilityfilefalse +% \consultutilityfilefalse +% \to \everyinsertMPfile +% +% %D One more: (still needed?) + +\startMPextensions + def initialize_form_numbers = + do_initialize_numbers; + enddef; +\stopMPextensions + +\startMPinitializations + HSize:=\the\hsize ; + VSize:=\the\vsize ; +\stopMPinitializations + +\startMPextensions + vardef ForegroundBox = + unitsquare xysized(HSize,VSize) + enddef ; + vardef PageFraction = + if \lastpage>1: (\realfolio-1)/(\lastpage-1) else: 1 fi + enddef ; +\stopMPextensions + +%D And some more. These are not really needed since we +%D don't use the normal figure inclusion macros any longer. + +\appendtoks + \externalfigurepostprocessors\emptytoks % safeguard +\to \everyinsertMPfile + +%D We also take care of disabling fancy figure features, that +%D can terribly interfere when dealing with symbols, +%D background graphics and running (postponed) graphics. +%D You won't believe me if I tell you what funny side effects +%D can occur. One took me over a day to uncover when +%D processing the screen version of the \METAFUN\ manual. + +%D For my eyes only: + +\def\doifelseMPgraphic#1{\doifdefinedelse{\@@MPG#1}} + +%D \macros +%D {startMPcolor} + +\long\unexpanded\def\startMPcolor#1\stopMPcolor + {\writestatus \m!metapost % eventually this placeholder will go away + {\string\startMPcolor...\stopMPcolor\space is obsolete,\space + use \string\defineintermediatecolor\space instead}} + +\let\stopMPcolor\relax + +%D New: + +\definelayerpreset % no dx,dy - else nasty non-mp placement + [mp] + [\c!y=-\MPury bp, + \c!x=\MPllx bp, + \c!method=\v!fit] + +\definelayer + [mp] + [\c!preset=mp] + +%D Usage: +%D +%D \starttyping +%D \defineproperty[one][layer][state=start] +%D \defineproperty[two][layer][state=stop] +%D +%D \startuseMPgraphic{step-1} +%D fill fullcircle scaled 10cm withcolor red ; +%D \stopuseMPgraphic +%D +%D \startuseMPgraphic{step-2} +%D fill fullcircle scaled 5cm withcolor green ; +%D \stopuseMPgraphic +%D +%D \setlayer[mp]{\property[one]{\useMPgraphic{step-1}}} +%D \setlayer[mp]{\property[two]{\useMPgraphic{step-2}}} +%D +%D \ruledhbox{\flushlayer[mp]} +%D \stoptyping +%D +%D Reusing graphics is also possible (now): +%D +%D \starttyping +%D \startreusableMPgraphic{axis} +%D tickstep := 1cm ; ticklength := 2mm ; +%D drawticks unitsquare xscaled 4cm yscaled 3cm shifted (-1cm,-1cm) ; +%D tickstep := tickstep/2 ; ticklength := ticklength/2 ; +%D drawticks unitsquare xscaled 4cm yscaled 3cm shifted (-1cm,-1cm) ; +%D \stopreusableMPgraphic +%D +%D \startuseMPgraphic{demo} +%D drawpoint "1cm,1.5cm" ; +%D \stopuseMPgraphic +%D +%D \definelayer[mp][preset=mp] +%D \setlayer[mp]{\reuseMPgraphic{axis}} +%D \setlayer[mp]{\useMPgraphic{demo}} +%D \ruledhbox{\flushlayer[mp]} +%D \stoptyping + +%D \macros +%D {startstaticMPfigure,useMPstaticfigure} +%D +%D Static figures are processed only when there has been +%D something changed. Here is Aditya Mahajan's testcase: +%D +%D \startbuffer +%D \startstaticMPfigure{circle} +%D fill fullcircle scaled 1cm withcolor blue; +%D \stopstaticMPfigure +%D +%D \startstaticMPfigure{axis} +%D drawarrow (0,0)--(2cm,0) ; +%D drawarrow (0,0)--(0,2cm) ; +%D label.llft(textext("(0,0)") ,origin) ; +%D \stopstaticMPfigure +%D \stopbuffer +%D +%D \typebuffer \getbuffer + +\def\usestaticMPfigure + {\dodoubleempty\dousestaticMPfigure} + +\def\dousestaticMPfigure[#1][#2]% + {\ifsecondargument + \scale[#2]{\reuseMPgraphic{\@@MPG#1@S@}}% + \else + \reuseMPgraphic{\@@MPG#1@S@}% + \fi} + +\unexpanded\def\startstaticMPfigure#1#2\stopstaticMPfigure + {\startreusableMPgraphic{\@@MPG#1@S@}#2\stopreusableMPgraphic} + +\long\unexpanded\def\startstaticMPgraphic + {\dodoublegroupempty\dostartstaticMPgraphic} + +\long\def\dostartstaticMPgraphic#1#2#3\stopstaticMPgraphic + {\long\setgvalue{\@@MPG#1@S@}{\handlereusableMPgraphic{#1}{#2}{#3}}} + +%D New: + +\newconditional\manyMPspecials % when set to true, > 1000 specials can be used + +\settrue \manyMPspecials % per 1/4/2006 + +\prependtoks + _special_div_ := 1000\ifconditional\manyMPspecials0\fi ; +\to \MPextensions + +%D Needed too. + +\let\initializeMPgraphics\relax + +%D Goody for preventing overflows: + +\def\MPdivten[#1]{\withoutpt\the\dimexpr#1pt/10\relax} + +%D There is no way to distinguish the black color that you get when +%D you issue a \type {draw} without color specification from a color +%D that has an explicit black specification unless you set the +%D variable \type {defaultcolormodel} to 1. Hoewever, in that case +%D you cannot distinguish that draw from one with a \type +%D {withoutcolor} specification. This means that we have to provide +%D multiple variants of inheritance. +%D +%D In any case we need to tell the converter what the inherited color +%D is to start with. Case~3 is kind of unpredictable as it closely +%D relates to the order in which paths are flushed. If you want to +%Dinherit automatically from the surrounding, you can best stick to +%D variant 1. Variant 0 (an isolated graphic) is the default. +%D +%D \startbuffer +%D \startuseMPgraphic{test} +%D drawoptions(withpen pencircle scaled 1pt) ; +%D def shift_cp = currentpicture := currentpicture shifted (-15pt,0) ; enddef ; +%D draw fullcircle scaled 10pt withoutcolor ; shift_cp ; +%D fill fullcircle scaled 10pt ; shift_cp ; +%D draw fullcircle scaled 10pt withoutcolor ; shift_cp ; +%D fill fullcircle scaled 10pt withcolor red ; shift_cp ; +%D draw fullcircle scaled 10pt withoutcolor ; shift_cp ; +%D fill fullcircle scaled 10pt ; shift_cp ; +%D \stopuseMPgraphic +%D +%D \starttabulate +%D \NC 0\quad \NC \chardef\MPcolormethod0 \green XX\quad \useMPgraphic{test}\quad XX \NC \NR +%D \NC 1\quad \NC \chardef\MPcolormethod1 \green XX\quad \useMPgraphic{test}\quad XX \NC \NR +%D \NC 2\quad \NC \chardef\MPcolormethod2 \green XX\quad \useMPgraphic{test}\quad XX \NC \NR +%D \NC 3\quad \NC \chardef\MPcolormethod3 \green XX\quad \useMPgraphic{test}\quad XX \NC \NR +%D \stoptabulate +%D \stopbuffer +%D +%D \typebuffer \getbuffer + +\chardef\MPcolormethod\zerocount + +% can be faster, just + +\appendtoks + \ctxlua{metapost.set_outer_color(\number\MPcolormethod,\number\currentcolormodel,\number\dogetattribute{color},\number\dogetattribute{transparency})}% +\to \everyMPgraphic + +\startMPinitializations + defaultcolormodel := \ifcase\MPcolormethod1\or1\or3\else3\fi; +\stopMPinitializations + +%D \macros +%D {setupMPgraphics} +%D +%D Here is a generic setup command: + +\newtoks \everysetupMPgraphics + +\unexpanded\def\setupMPgraphics[#1]% + {\getparameters[\??mp][#1]% + \the\everysetupMPgraphics} + +%D Here we hook in the outer color. When \type {color} is set to \type +%D {global} we get the outer color automatically. If you change this +%D setting, you should do it grouped in order not to make other graphics +%D behave in unexpected ways. + +\appendtoks + \doifelse\@@mpcolor\v!global{\chardef\MPcolormethod\plusone}{\chardef\MPcolormethod\zerocount}% +\to \everysetupMPgraphics + +\setupMPgraphics + [\c!color=\v!local] + +%D Done. + +\protect \endinput diff --git a/tex/context/base/meta-mis.tex b/tex/context/base/meta-mis.tex new file mode 100644 index 000000000..29ab43007 --- /dev/null +++ b/tex/context/base/meta-mis.tex @@ -0,0 +1,54 @@ +%D \module +%D [ file=meta-mis, +%D version=2006.06.06, +%D title=\METAPOST\ Graphics, +%D subtitle=Misc Test Graphics, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\unprotect + +% p/s 1/false 1/true 2/false 2/true +% +% MKII 8.5 8.0 8.8 8.5 +% MKIV 16.1 7.2 16.3 7.4 + +\startuseMPgraphic{mptopdf-test} + prologues := 2; + mpprocset := 1 ; + fill fullcircle scaled 3cm withcolor red ; + fill fullcircle scaled 2cm withcolor green ; + fill fullcircle scaled 1cm withcolor blue ; + currentpicture := currentpicture shifted (-4cm,0) ; + fill fullcircle scaled 3cm withcolor cmyk(0,0,1,0) ; + fill fullcircle scaled 2cm withcolor cmyk(0,1,0,0) ; + fill fullcircle scaled 1cm withcolor cmyk(1,0,0,0) ; + currentpicture := currentpicture shifted (-4cm,0) ; + draw fullcircle scaled 3cm dashed evenly ; + draw fullcircle scaled 2cm dashed withdots ; + draw origin withpen pencircle scaled 3mm; + currentpicture := currentpicture shifted (-4cm,0) ; + fill fullcircle scaled 2cm shifted (-.5cm,+.5cm) withcolor transparent(1,.5,red); + fill fullcircle scaled 2cm shifted (-.5cm,-.5cm) withcolor transparent(1,.5,red); + fill fullcircle scaled 2cm shifted (+.5cm,+.5cm) withcolor transparent(1,.5,green); + fill fullcircle scaled 2cm shifted (+.5cm,-.5cm) withcolor transparent(1,.5,cmyk(1,0,1,.5)); + currentpicture := currentpicture shifted (12cm,-4cm) ; + draw "o e p s" infont defaultfont scaled 2 shifted (-1cm,0) ; + currentpicture := currentpicture shifted (-4cm,0) ; + % bug: shift + draw fullcircle scaled 3cm withpen pencircle yscaled 3mm xscaled 2mm rotated 30 ; + draw fullcircle scaled 2cm withpen pencircle yscaled 3mm xscaled 2mm rotated 20 withcolor red ; + filldraw fullcircle scaled 1cm withpen pencircle yscaled 3mm xscaled 2mm rotated 10 withcolor green ; + currentpicture := currentpicture shifted (-4cm,0) ; + % shade cannot handle shift + circular_shade(fullcircle scaled 3cm,0,.2red,.9green) ; + circular_shade(fullcircle scaled 3cm shifted(+4cm,0),0,cmyk(1,0,0,0),cmyk(0,1,0,0)) ; + filldraw boundingbox currentpicture enlarged (-bbheight(currentpicture)/2+2.5mm) withpen pencircle scaled 1pt withcolor .5white ; +\stopuseMPgraphic + +\protect \endinput diff --git a/tex/context/base/meta-nav.tex b/tex/context/base/meta-nav.tex new file mode 100644 index 000000000..9c1cbb4db --- /dev/null +++ b/tex/context/base/meta-nav.tex @@ -0,0 +1,65 @@ +%D \module +%D [ file=meta-nav, +%D version=2003.03.28, +%D title=\METAPOST\ Graphics, +%D subtitle=Navigational Graphics, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\startuniqueMPgraphic{navplus}{size,color,type,mode} + color c ; numeric s, t ; path p ; + t := \MPvar{type} ; c := \MPvar{color} ; s := \MPvar{size} ; + if \MPvar{mode} = 1 : c := .5[c,white] fi ; + if t = 1 : + p := ((0,0)--(1/2,0)--(1,1/3)--(1,1)--(0,1)--(0,0)--cycle) + xyscaled (3,4) ; + elseif t = 2 : + p := ((0,0)--(1,0)--(1,1)--(0,1)--(1/2,2/5)--(1,1)--(0,1)--cycle) + xyscaled (4,3) ; + else : + p := fullsquare xyscaled (3,3) ; + fi ; + draw p withpen pencircle scaled (1/2) withcolor .75white ; + fill p withcolor c ; + draw p withpen pencircle scaled (1/3) withcolor .5c ; + currentpicture := currentpicture scaled s ; + currentpicture := currentpicture shifted -center currentpicture ; +\stopuniqueMPgraphic + +\setupMPvariables + [navplus] + [size=1ex, + color=black, + type=1, + mode=0] + +\definepalet + [navplus] + [attach=darkred, + comment=darkblue] + +\definesymbol + [comment-normal] + [\uniqueMPgraphic{navplus}{type=1,color=navplus:comment}] +\definesymbol + [comment-down] + [\uniqueMPgraphic{navplus}{type=1,color=navplus:comment,mode=1}] + +\definesymbol + [attach-normal] + [\uniqueMPgraphic{navplus}{type=2,color=navplus:attach}] +\definesymbol + [attach-down] + [\uniqueMPgraphic{navplus}{type=2,color=navplus:attach,mode=1}] + +\unprotect + +\setupcomment [\c!symbol={comment-normal,comment-down}] +\setupattachments[\c!symbol={attach-normal,attach-down}] + +\protect \endinput diff --git a/tex/context/base/meta-pag.mkii b/tex/context/base/meta-pag.mkii new file mode 100644 index 000000000..000e56a2e --- /dev/null +++ b/tex/context/base/meta-pag.mkii @@ -0,0 +1,226 @@ +%D \module +%D [ file=meta-pag, +%D version=1999.07.10, +%D title=\METAPOST\ Graphics, +%D subtitle=Initialization, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D These definitions used to be part of the old \type +%D {core-mps} file, later changed into \type {meta-ini}, but +%D keeping them separate is cleaner. + +\writestatus{loading}{MetaPost Graphics / Page Data Management} + +\unprotect + +\startMPextensions + if unknown context_page: input mp-page; fi; +\stopMPextensions + +%D The next few macros tell \METAPOST\ how the \CONTEXT\ +%D pagebody looks. + +\startMPextensions + boolean PageStateAvailable,OnRightPage,InPageBody; + PageStateAvailable:=true; +\stopMPextensions + +\startMPinitializations + OnRightPage:=true; + InPageBody:=\ifinpagebody true \else false \fi; +\stopMPinitializations + +\startMPinitializations + def LoadPageState = + OnRightPage:=\MPonrightpage; + OnOddPage:=\MPonoddpage; + RealPageNumber:=\the\realpageno; + PageNumber:=\the\pageno; + NOfPages:=\lastpage; + PaperHeight:=\the\paperheight; + PaperWidth:=\the\paperwidth; + PrintPaperHeight:=\the\printpaperheight; + PrintPaperWidth:=\the\printpaperwidth; + TopSpace:=\the\topspace; + BottomSpace:=\the\bottomspace; + BackSpace:=\the\backspace; + CutSpace:=\the\cutspace; + MakeupHeight:=\the\makeupheight; + MakeupWidth:=\the\makeupwidth; + TopHeight:=\the\topheight; + TopDistance:=\the\topdistance; + HeaderHeight:=\the\headerheight; + HeaderDistance:=\the\headerdistance; + TextHeight:=\the\textheight; + FooterDistance:=\the\footerdistance; + FooterHeight:=\the\footerheight; + BottomDistance:=\the\bottomdistance; + BottomHeight:=\the\bottomheight; + LeftEdgeWidth:=\the\leftedgewidth; + LeftEdgeDistance:=\the\leftedgedistance; + LeftMarginWidth:=\the\leftmarginwidth; + LeftMarginDistance:=\the\leftmargindistance; + TextWidth:=\the\textwidth ; + RightMarginDistance:=\the\rightmargindistance; + RightMarginWidth:=\the\rightmarginwidth; + RightEdgeDistance:=\the\rightedgedistance; + RightEdgeWidth:=\the\rightedgewidth; + InnerMarginDistance:=\the\innermargindistance; + InnerMarginWidth:=\the\innermarginwidth; + OuterMarginDistance:=\the\outermargindistance; + OuterMarginWidth:=\the\outermarginwidth; + InnerEdgeDistance:=\the\inneredgedistance; + InnerEdgeWidth:=\the\inneredgewidth; + OuterEdgeDistance:=\the\outeredgedistance; + OuterEdgeWidth:=\the\outeredgewidth; + PageOffset:=\the\pageoffset; + PageDepth:=\the\pagedepth; + LayoutColumns:=\the\layoutcolumns; + LayoutColumnDistance:=\the\layoutcolumndistance; + LayoutColumnWidth:=\the\layoutcolumnwidth; + enddef; +\stopMPinitializations + +\def\MPonrightpage{true} +\def\MPonoddpage {true} + +\def\freezeMPpagelayout + {\doifbothsides + {\def\MPonrightpage{true}} + {\def\MPonrightpage{true}} + {\def\MPonrightpage{false}}% + \edef\MPonoddpage{\doifoddpageelse{true}{false}}} + +\let\freezeMPlayout\relax % obsolete + +%D We need to freeze the pagelayout before the backgrounds +%D are build, because the overlay will temporarily become +%D zero (overlay). + +\appendtoks + \freezeMPpagelayout +\to \everybeforepagebody + +%D By freezing these value every graphic, we can use layout +%D variables that change halfways a page, whatever use that +%D has. + +\prependtoks + \calculatereducedvsizes % this is really needed + \freezeMPpagelayout + \freezeMPlayout % to be used grouped +\to \everyMPgraphic + +%D The next feature provides information about for instance +%D column positions. This is an experimental feature, +%D introduced when we needed backgrounds in columns (fill||in +%D questions as implemented in a private module). +%D +%D See \type {mp-page.mp} for the definition of the macros: +%D +%D \starttabulate[|tl|l|p|] +%D \NC ResetTextAreas \NC no arguments \NC +%D reset areas on page \NC \NR +%D \NC RegisterTextArea \NC x, y, w, h \NC +%D adds area to the list \NC \NR +%D \NC TextAreaX,Y,W,H,XY,WH \NC x and/or y \NC +%D reports offsets and dimensions \NC \NR +%D \stoptabulate +%D +%D The \type {TextArea*} macros can be used to determine +%D overlap. + +\newcount\currentMPtextareadata + +\newtoks\MPsavedtextareadata +\newtoks\MPtextareadata +\newtoks\MPlocaltextareadata + +% optimaliseren voor herhaling + +\def\registerMPtextarea#1% + {\ifpositioning + \bgroup + \global\advance\currentMPtextareadata\plusone + %\hpos{gbd:\the\currentMPtextareadata}{#1}% + \hpos{gbd:\the\currentMPtextareadata}% + {\iftracetextareas\boxrulewidth1.5pt\ruledhbox\fi{#1}}% + \edef\!!stringa{gbd:\the\currentMPtextareadata}% + \edef\!!stringa{RegisterTextArea(% + \MPx\!!stringa,\MPy\!!stringa,% + \MPw\!!stringa,\MPh\!!stringa,\MPd\!!stringa);}% + \@EA \doglobal \@EA \appendtoks \!!stringa \to \MPtextareadata + \egroup + \else + \hbox{#1}% + \fi} + +\def\registerMPlocaltextarea#1% + {\ifpositioning + \bgroup + \global\advance\currentMPtextareadata\plusone + %\hpos{gbd:\the\currentMPtextareadata}{#1}% + \hpos{gbd:\the\currentMPtextareadata}% + {\iftracetextareas\boxrulewidth3pt\ruledhbox\fi{#1}}% + \edef\!!stringa{gbd:\the\currentMPtextareadata}% + \edef\!!stringa{RegisterLocalTextArea(% + \MPx\!!stringa,\MPy\!!stringa,% + \MPw\!!stringa,\MPh\!!stringa,\MPd\!!stringa);}% + \global\MPlocaltextareadata\@EA{\!!stringa}% + \egroup + \else + \hbox{#1}% + \fi} + +% better, so that we can force a key and share with e.g. renumbering +% +% \let\namedtextarea\empty +% +% \def\registerMPlocaltextarea#1% +% {\ifpositioning +% \bgroup +% \ifx\namedtextarea\empty +% \global\advance\currentMPtextareadata\plusone +% \edef\namedtextarea{gbd:\the\currentMPtextareadata}% +% \fi +% \hpos\namedtextarea{\iftracetextareas\boxrulewidth3pt\ruledhbox\fi{#1}}% +% \edef\ascii{RegisterLocalTextArea(% +% \MPx\namedtextarea,\MPy\namedtextarea,% +% \MPw\namedtextarea,\MPh\namedtextarea,\MPd\namedtextarea);}% +% \global\MPlocaltextareadata\@EA{\ascii}% +% \egroup +% \else +% \hbox{#1}% +% \fi} + +\def\resetMPlocaltextarea + {\global\MPlocaltextareadata\emptytoks} + +\startMPextensions + path PlainTextArea; +\stopMPextensions + +\startMPinitializations + ResetTextAreas; + \the\MPsavedtextareadata; + SaveTextAreas; + ResetTextAreas; + \the\MPtextareadata; + \the\MPlocaltextareadata; + PlainTextArea:=boundingbox(\MPxy{text:\realfolio}--\MPxy{text:\realfolio} + shifted (\MPw{text:\realfolio},\MPh{text:\realfolio})); +\stopMPinitializations + +\appendtoks + \global\MPsavedtextareadata\MPtextareadata + \global\MPtextareadata \emptytoks + \global\MPlocaltextareadata\emptytoks +\to \everyshipout + +\protect \endinput diff --git a/tex/context/base/meta-pag.mkiv b/tex/context/base/meta-pag.mkiv new file mode 100644 index 000000000..ef3817721 --- /dev/null +++ b/tex/context/base/meta-pag.mkiv @@ -0,0 +1,223 @@ +%D \module +%D [ file=meta-pag, +%D version=1999.07.10, +%D title=\METAPOST\ Graphics, +%D subtitle=Initialization, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D These definitions used to be part of the old \type +%D {core-mps} file, later changed into \type {meta-ini}, but +%D keeping them separate is cleaner. + +\writestatus{loading}{MetaPost Graphics / Page Data Management} + +\unprotect + +\startMPextensions + if unknown context_page: input mp-page; fi; +\stopMPextensions + +%D The next few macros tell \METAPOST\ how the \CONTEXT\ +%D pagebody looks. + +\startMPextensions + boolean PageStateAvailable,OnRightPage,InPageBody; + PageStateAvailable:=true; +\stopMPextensions + +\startMPinitializations + OnRightPage:=true; + InPageBody:=\ifinpagebody true \else false \fi; +\stopMPinitializations + +\startMPinitializations + def LoadPageState = + OnRightPage:=\MPonrightpage; + OnOddPage:=\MPonoddpage; + RealPageNumber:=\the\realpageno; + PageNumber:=\the\pageno; + NOfPages:=\lastpage; + PaperHeight:=\the\paperheight; + PaperWidth:=\the\paperwidth; + PrintPaperHeight:=\the\printpaperheight; + PrintPaperWidth:=\the\printpaperwidth; + TopSpace:=\the\topspace; + BottomSpace:=\the\bottomspace; + BackSpace:=\the\backspace; + CutSpace:=\the\cutspace; + MakeupHeight:=\the\makeupheight; + MakeupWidth:=\the\makeupwidth; + TopHeight:=\the\topheight; + TopDistance:=\the\topdistance; + HeaderHeight:=\the\headerheight; + HeaderDistance:=\the\headerdistance; + TextHeight:=\the\textheight; + FooterDistance:=\the\footerdistance; + FooterHeight:=\the\footerheight; + BottomDistance:=\the\bottomdistance; + BottomHeight:=\the\bottomheight; + LeftEdgeWidth:=\the\leftedgewidth; + LeftEdgeDistance:=\the\leftedgedistance; + LeftMarginWidth:=\the\leftmarginwidth; + LeftMarginDistance:=\the\leftmargindistance; + TextWidth:=\the\textwidth ; + RightMarginDistance:=\the\rightmargindistance; + RightMarginWidth:=\the\rightmarginwidth; + RightEdgeDistance:=\the\rightedgedistance; + RightEdgeWidth:=\the\rightedgewidth; + InnerMarginDistance:=\the\innermargindistance; + InnerMarginWidth:=\the\innermarginwidth; + OuterMarginDistance:=\the\outermargindistance; + OuterMarginWidth:=\the\outermarginwidth; + InnerEdgeDistance:=\the\inneredgedistance; + InnerEdgeWidth:=\the\inneredgewidth; + OuterEdgeDistance:=\the\outeredgedistance; + OuterEdgeWidth:=\the\outeredgewidth; + PageOffset:=\the\pageoffset; + PageDepth:=\the\pagedepth; + LayoutColumns:=\the\layoutcolumns; + LayoutColumnDistance:=\the\layoutcolumndistance; + LayoutColumnWidth:=\the\layoutcolumnwidth; + enddef; +\stopMPinitializations + +\def\MPonrightpage{true} +\def\MPonoddpage {true} + +\def\freezeMPpagelayout + {\edef\MPonrightpage{\doifbothsides {tru}{tru}{fals}e}% + \edef\MPonoddpage {\doifoddpageelse {tru}{fals}e}} + +\let\freezeMPlayout\relax % obsolete + +%D We need to freeze the pagelayout before the backgrounds +%D are build, because the overlay will temporarily become +%D zero (overlay). + +\appendtoks + \freezeMPpagelayout +\to \everybeforepagebody + +%D By freezing these value every graphic, we can use layout +%D variables that change halfways a page, whatever use that +%D has. + +\prependtoks + \calculatereducedvsizes % this is really needed + \freezeMPpagelayout + \freezeMPlayout % to be used grouped +\to \everyMPgraphic + +%D The next feature provides information about for instance +%D column positions. This is an experimental feature, +%D introduced when we needed backgrounds in columns (fill||in +%D questions as implemented in a private module). +%D +%D See \type {mp-page.mp} for the definition of the macros: +%D +%D \starttabulate[|tl|l|p|] +%D \NC ResetTextAreas \NC no arguments \NC +%D reset areas on page \NC \NR +%D \NC RegisterTextArea \NC x, y, w, h \NC +%D adds area to the list \NC \NR +%D \NC TextAreaX,Y,W,H,XY,WH \NC x and/or y \NC +%D reports offsets and dimensions \NC \NR +%D \stoptabulate +%D +%D The \type {TextArea*} macros can be used to determine +%D overlap. + +\newcount\currentMPtextareadata + +\newtoks\MPsavedtextareadata +\newtoks\MPtextareadata +\newtoks\MPlocaltextareadata + +% optimaliseren voor herhaling + +\def\registerMPtextarea#1% + {\ifpositioning + \bgroup + \global\advance\currentMPtextareadata\plusone + %\hpos{gbd:\the\currentMPtextareadata}{#1}% + \hpos{gbd:\the\currentMPtextareadata}% + {\iftracetextareas\boxrulewidth1.5pt\ruledhbox\fi{#1}}% + \edef\!!stringa{gbd:\the\currentMPtextareadata}% + \edef\!!stringa{RegisterTextArea(% + \MPx\!!stringa,\MPy\!!stringa,% + \MPw\!!stringa,\MPh\!!stringa,\MPd\!!stringa);}% + \@EA \doglobal \@EA \appendtoks \!!stringa \to \MPtextareadata + \egroup + \else + \hbox{#1}% + \fi} + +\def\registerMPlocaltextarea#1% + {\ifpositioning + \bgroup + \global\advance\currentMPtextareadata\plusone + %\hpos{gbd:\the\currentMPtextareadata}{#1}% + \hpos{gbd:\the\currentMPtextareadata}% + {\iftracetextareas\boxrulewidth3pt\ruledhbox\fi{#1}}% + \edef\!!stringa{gbd:\the\currentMPtextareadata}% + \edef\!!stringa{RegisterLocalTextArea(% + \MPx\!!stringa,\MPy\!!stringa,% + \MPw\!!stringa,\MPh\!!stringa,\MPd\!!stringa);}% + \global\MPlocaltextareadata\@EA{\!!stringa}% + \egroup + \else + \hbox{#1}% + \fi} + +% better, so that we can force a key and share with e.g. renumbering +% +% \let\namedtextarea\empty +% +% \def\registerMPlocaltextarea#1% +% {\ifpositioning +% \bgroup +% \ifx\namedtextarea\empty +% \global\advance\currentMPtextareadata\plusone +% \edef\namedtextarea{gbd:\the\currentMPtextareadata}% +% \fi +% \hpos\namedtextarea{\iftracetextareas\boxrulewidth3pt\ruledhbox\fi{#1}}% +% \edef\ascii{RegisterLocalTextArea(% +% \MPx\namedtextarea,\MPy\namedtextarea,% +% \MPw\namedtextarea,\MPh\namedtextarea,\MPd\namedtextarea);}% +% \global\MPlocaltextareadata\@EA{\ascii}% +% \egroup +% \else +% \hbox{#1}% +% \fi} + +\def\resetMPlocaltextarea + {\global\MPlocaltextareadata\emptytoks} + +\startMPextensions + path PlainTextArea; +\stopMPextensions + +\startMPinitializations + ResetTextAreas; + \the\MPsavedtextareadata; + SaveTextAreas; + ResetTextAreas; + \the\MPtextareadata; + \the\MPlocaltextareadata; + PlainTextArea:=boundingbox(\MPxy{text:\realfolio}--\MPxy{text:\realfolio} + shifted (\MPw{text:\realfolio},\MPh{text:\realfolio})); +\stopMPinitializations + +\appendtoks + \global\MPsavedtextareadata\MPtextareadata + \global\MPtextareadata \emptytoks + \global\MPlocaltextareadata\emptytoks +\to \everyshipout + +\protect \endinput diff --git a/tex/context/base/meta-pdf.lua b/tex/context/base/meta-pdf.lua new file mode 100644 index 000000000..23f8d4de0 --- /dev/null +++ b/tex/context/base/meta-pdf.lua @@ -0,0 +1,553 @@ +if not modules then modules = { } end modules ['meta-pdf'] = { + version = 1.001, + comment = "companion to meta-pdf.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- Finally we used an optimized version. The test code can be found in +-- meta-pdh.lua but since we no longer want to overload functione we use +-- more locals now. This module keeps changing as it is also a testbed. + +local concat, format, gsub, find, byte, gmatch, match = table.concat, string.format, string.gsub, string.find, string.byte, string.gmatch, string.match +local lpegmatch = lpeg.match +local round = math.round +local texsprint, ctxcatcodes = tex.sprint, tex.ctxcatcodes + +local pdfrgbcode = lpdf.rgbcode +local pdfcmykcode = lpdf.cmykcode +local pdfgraycode = lpdf.graycode +local pdfspotcode = lpdf.spotcode +local pdftransparencycode = lpdf.transparencycode +local pdffinishtransparencycode = lpdf.finishtransparencycode + +mptopdf = { } +mptopdf.n = 0 + +local m_path, m_stack, m_texts, m_version, m_date, m_shortcuts = { }, { }, { }, 0, 0, false + +local m_stack_close, m_stack_path, m_stack_concat = false, { }, nil +local extra_path_code, ignore_path = nil, false +local specials = { } + +local function resetpath() + m_stack_close, m_stack_path, m_stack_concat = false, { }, nil +end + +local function resetall() + m_path, m_stack, m_texts, m_version, m_shortcuts = { }, { }, { }, 0, false + extra_path_code, ignore_path = nil, false + specials = { } + resetpath() +end + +resetall() + +-- todo: collect and flush packed using pdfliteral node injection but we're +-- in no hurry as this kind of conversion does not happen that often in mkiv + +local function pdfcode(str) -- could be a node.write instead + texsprint(ctxcatcodes,"\\pdfliteral{",str,"}") +end + +local function texcode(str) + texsprint(ctxcatcodes,str) +end + +function mpscode(str) + if ignore_path then + pdfcode("h W n") + if extra_path_code then + pdfcode(extra_path_code) + extra_path_code = nil + end + ignore_path = false + else + pdfcode(str) + end +end + +-- auxiliary functions + +local function flushconcat() + if m_stack_concat then + mpscode(concat(m_stack_concat," ") .. " cm") + m_stack_concat = nil + end +end + +local function flushpath(cmd) + if #m_stack_path > 0 then + local path = { } + if m_stack_concat then + local sx, sy = m_stack_concat[1], m_stack_concat[4] + local rx, ry = m_stack_concat[2], m_stack_concat[3] + local tx, ty = m_stack_concat[5], m_stack_concat[6] + local d = (sx*sy) - (rx*ry) + for k=1,#m_stack_path do + local v = m_stack_path[k] + local px, py = v[1], v[2] ; v[1], v[2] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[1],v[2]) + if #v == 7 then + local px, py = v[3], v[4] ; v[3], v[4] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[3],v[4]) + local px, py = v[5], v[6] ; v[5], v[6] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[5],v[6]) + end + path[#path+1] = concat(v," ") + end + else + for k=1,#m_stack_path do + path[#path+1] = concat(m_stack_path[k]," ") + end + end + flushconcat() + pdfcode(concat(path," ")) + if m_stack_close then + mpscode("h " .. cmd) + else + mpscode(cmd) + end + end + resetpath() +end + +-- mp interface + +mps = mps or { } + +function mps.creator(a, b, c) + m_version = tonumber(b) +end + +function mps.creationdate(a) + m_date = a +end + +function mps.newpath() + m_stack_path = { } +end + +function mps.boundingbox(llx, lly, urx, ury) + texcode("\\MPSboundingbox{" .. llx .. "}{" .. lly .. "}{" .. urx .. "}{" .. ury .. "}") +end + +function mps.moveto(x,y) + m_stack_path[#m_stack_path+1] = {x,y,"m"} +end + +function mps.curveto(ax, ay, bx, by, cx, cy) + m_stack_path[#m_stack_path+1] = {ax,ay,bx,by,cx,cy,"c"} +end + +function mps.lineto(x,y) + m_stack_path[#m_stack_path+1] = {x,y,"l"} +end + +function mps.rlineto(x,y) + local dx, dy = 0, 0 + if #m_stack_path > 0 then + dx, dy = m_stack_path[#m_stack_path][1], m_stack_path[#m_stack_path][2] + end + m_stack_path[#m_stack_path+1] = {dx,dy,"l"} +end + +function mps.translate(tx,ty) + mpscode("1 0 0 0 1 " .. tx .. " " .. ty .. " cm") +end + +function mps.scale(sx,sy) + m_stack_concat = {sx,0,0,sy,0,0} +end + +function mps.concat(sx, rx, ry, sy, tx, ty) + m_stack_concat = {sx,rx,ry,sy,tx,ty} +end + +function mps.setlinejoin(d) + mpscode(d .. " j") +end + +function mps.setlinecap(d) + mpscode(d .. " J") +end + +function mps.setmiterlimit(d) + mpscode(d .. " M") +end + +function mps.gsave() + mpscode("q") +end + +function mps.grestore() + mpscode("Q") +end + +function mps.setdash(...) -- can be made faster, operate on t = { ... } + local n = select("#",...) + mpscode("[" .. concat({...}," ",1,n-1) .. "] " .. select(n,...) .. " d") +end + +function mps.resetdash() + mpscode("[ ] 0 d") +end + +function mps.setlinewidth(d) + mpscode(d .. " w") +end + +function mps.closepath() + m_stack_close = true +end + +function mps.fill() + flushpath('f') +end + +function mps.stroke() + flushpath('S') +end + +function mps.both() + flushpath('B') +end + +function mps.clip() + flushpath('W n') +end + +function mps.textext(font, scale, str) -- old parser + local dx, dy = 0, 0 + if #m_stack_path > 0 then + dx, dy = m_stack_path[1][1], m_stack_path[1][2] + end + flushconcat() + texcode("\\MPStextext{"..font.."}{"..scale.."}{"..str.."}{"..dx.."}{"..dy.."}") + resetpath() +end + +local handlers = { } + +handlers[1] = function(s) + pdfcode(pdffinishtransparencycode()) + pdfcode(pdfcmykcode(mps.colormodel,s[3],s[4],s[5],s[6])) +end +handlers[2] = function(s) + pdfcode(pdffinishtransparencycode()) + pdfcode(pdfspotcode(mps.colormodel,s[3],s[4],s[5],s[6])) +end +handlers[3] = function(s) + pdfcode(pdfrgbcode(mps.colormodel,s[4],s[5],s[6])) + pdfcode(pdftransparencycode(s[2],s[3])) +end +handlers[4] = function(s) + pdfcode(pdfcmykcode(mps.colormodel,s[4],s[5],s[6],s[7])) + pdfcode(pdftransparencycode(s[2],s[3])) +end +handlers[5] = function(s) + pdfcode(pdfspotcode(mps.colormodel,s[4],s[5],s[6],s[7])) + pdfcode(pdftransparencycode(s[2],s[3])) +end + +-- todo: color conversion + +local nofshades, tn = 0, tonumber + +local function linearshade(colorspace,domain,ca,cb,coordinates) + pdfcode(pdffinishtransparencycode()) + nofshades = nofshades + 1 + local name = format("MpsSh%s",nofshades) + lpdf.linearshade(name,domain,ca,cb,1,colorspace,coordinates) + extra_path_code, ignore_path = format("/%s sh Q",name), true + pdfcode("q /Pattern cs") +end + +local function circularshade(colorspace,domain,ca,cb,coordinates) + pdfcode(pdffinishtransparencycode()) + nofshades = nofshades + 1 + local name = format("MpsSh%s",nofshades) + lpdf.circularshade(name,domain,ca,cb,1,colorspace,coordinates) + extra_path_code, ignore_path = format("/%s sh Q",name), true + pdfcode("q /Pattern cs") +end + +handlers[30] = function(s) + linearshade("DeviceRGB", { tn(s[ 2]), tn(s[ 3]) }, + { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]) }, { tn(s[10]), tn(s[11]), tn(s[12]) }, + { tn(s[ 8]), tn(s[ 9]), tn(s[13]), tn(s[14]) } ) +end + +handlers[31] = function(s) + circularshade("DeviceRGB", { tn(s[ 2]), tn(s[ 3]) }, + { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]) }, { tn(s[11]), tn(s[12]), tn(s[13]) }, + { tn(s[ 8]), tn(s[ 9]), tn(s[10]), tn(s[14]), tn(s[15]), tn(s[16]) } ) +end + +handlers[32] = function(s) + linearshade("DeviceCMYK", { tn(s[ 2]), tn(s[ 3]) }, + { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]), tn(s[ 8]) }, { tn(s[11]), tn(s[12]), tn(s[13]), tn(s[14]) }, + { tn(s[ 9]), tn(s[10]), tn(s[15]), tn(s[16]) } ) +end + +handlers[33] = function(s) + circularshade("DeviceCMYK", { tn(s[ 2]), tn(s[ 3]) }, + { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]), tn(s[ 8]) }, { tn(s[12]), tn(s[13]), tn(s[14]), tn(s[15]) }, + { tn(s[ 9]), tn(s[10]), tn(s[11]), tn(s[16]), tn(s[17]), tn(s[18]) } ) +end + +handlers[34] = function(s) -- todo (after further cleanup) + linearshade("DeviceGray", { tn(s[ 2]), tn(s[ 3]) }, { 0 }, { 1 }, { tn(s[9]), tn(s[10]), tn(s[15]), tn(s[16]) } ) +end + +handlers[35] = function(s) -- todo (after further cleanup) + circularshade("DeviceGray", { tn(s[ 2]), tn(s[ 3]) }, { 0 }, { 1 }, { tn(s[9]), tn(s[10]), tn(s[15]), tn(s[16]) } ) +end + +-- not supported in mkiv , use mplib instead + +handlers[10] = function() logs.report("mptopdf","skipping special %s",10) end +handlers[20] = function() logs.report("mptopdf","skipping special %s",20) end +handlers[50] = function() logs.report("mptopdf","skipping special %s",50) end + +--end of not supported + +function mps.setrgbcolor(r,g,b) -- extra check + r, g = tonumber(r), tonumber(g) -- needed when we use lpeg + if r == 0.0123 and g < 0.1 then + g, b = round(g*10000), round(b*10000) + local s = specials[b] + local h = round(s[#s]) + local handler = handlers[h] + if handler then + handler(s) + else + logs.report("mptopdf","unknown special handler %s (1)",h) + end + elseif r == 0.123 and g < 0.1 then + g, b = round(g*1000), round(b*1000) + local s = specials[b] + local h = round(s[#s]) + local handler = handlers[h] + if handler then + handler(s) + else + logs.report("mptopdf","unknown special handler %s (2)",h) + end + else + pdfcode(pdffinishtransparencycode()) + pdfcode(pdfrgbcode(mps.colormodel,r,g,b)) + end +end + +function mps.setcmykcolor(c,m,y,k) + pdfcode(pdffinishtransparencycode()) + pdfcode(pdfcmykcode(mps.colormodel,c,m,y,k)) +end + +function mps.setgray(s) + pdfcode(pdffinishtransparencycode()) + pdfcode(pdfgrayliteral(mps.colormodel,s)) +end + +function mps.specials(version,signal,factor) -- 2.0 123 1000 +end + +function mps.special(...) -- 7 1 0.5 1 0 0 1 3 + local t = { ... } + local n = tonumber(t[#t-1]) + specials[n] = t +end + +function mps.begindata() +end + +function mps.enddata() +end + +function mps.showpage() +end + +-- lpeg parser + +-- The lpeg based parser is rather optimized for the kind of output +-- that MetaPost produces. It's my first real lpeg code, which may +-- show. Because the parser binds to functions, we define it last. + +local lpegP, lpegR, lpegS, lpegC, lpegCc, lpegCs = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cs + +local digit = lpegR("09") +local eol = lpegS('\r\n')^1 +local sp = lpegP(' ')^1 +local space = lpegS(' \r\n')^1 +local number = lpegS('0123456789.-+')^1 +local nonspace = lpegP(1-lpegS(' \r\n'))^1 + +local spec = digit^2 * lpegP("::::") * digit^2 +local text = lpegCc("{") * ( + lpegP("\\") * ( (digit * digit * digit) / function(n) return "c" .. tonumber(n,8) end) + + lpegP(" ") / function(n) return "\\c32" end + -- never in new mp + lpegP(1) / function(n) return "\\c" .. byte(n) end + ) * lpegCc("}") +local package = lpegCs(spec + text^0) + +function mps.fshow(str,font,scale) -- lpeg parser + mps.textext(font,scale,lpegmatch(package,str)) +end + +local cnumber = lpegC(number) +local cstring = lpegC(nonspace) + +local specials = (lpegP("%%MetaPostSpecials:") * sp * (cstring * sp^0)^0 * eol) / mps.specials +local special = (lpegP("%%MetaPostSpecial:") * sp * (cstring * sp^0)^0 * eol) / mps.special +local boundingbox = (lpegP("%%BoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox +local highresboundingbox = (lpegP("%%HiResBoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox + +local setup = lpegP("%%BeginSetup") * (1 - lpegP("%%EndSetup") )^1 +local prolog = lpegP("%%BeginProlog") * (1 - lpegP("%%EndProlog"))^1 +local comment = lpegP('%')^1 * (1 - eol)^1 + +local curveto = ((cnumber * sp)^6 * lpegP("curveto") ) / mps.curveto +local lineto = ((cnumber * sp)^2 * lpegP("lineto") ) / mps.lineto +local rlineto = ((cnumber * sp)^2 * lpegP("rlineto") ) / mps.rlineto +local moveto = ((cnumber * sp)^2 * lpegP("moveto") ) / mps.moveto +local setrgbcolor = ((cnumber * sp)^3 * lpegP("setrgbcolor") ) / mps.setrgbcolor +local setcmykcolor = ((cnumber * sp)^4 * lpegP("setcmykcolor") ) / mps.setcmykcolor +local setgray = ((cnumber * sp)^1 * lpegP("setgray") ) / mps.setgray +local newpath = ( lpegP("newpath") ) / mps.newpath +local closepath = ( lpegP("closepath") ) / mps.closepath +local fill = ( lpegP("fill") ) / mps.fill +local stroke = ( lpegP("stroke") ) / mps.stroke +local clip = ( lpegP("clip") ) / mps.clip +local both = ( lpegP("gsave fill grestore")) / mps.both +local showpage = ( lpegP("showpage") ) +local setlinejoin = ((cnumber * sp)^1 * lpegP("setlinejoin") ) / mps.setlinejoin +local setlinecap = ((cnumber * sp)^1 * lpegP("setlinecap") ) / mps.setlinecap +local setmiterlimit = ((cnumber * sp)^1 * lpegP("setmiterlimit") ) / mps.setmiterlimit +local gsave = ( lpegP("gsave") ) / mps.gsave +local grestore = ( lpegP("grestore") ) / mps.grestore + +local setdash = (lpegP("[") * (cnumber * sp^0)^0 * lpegP("]") * sp * cnumber * sp * lpegP("setdash")) / mps.setdash +local concat = (lpegP("[") * (cnumber * sp^0)^6 * lpegP("]") * sp * lpegP("concat") ) / mps.concat +local scale = ( (cnumber * sp^0)^6 * sp * lpegP("concat") ) / mps.concat + +local fshow = (lpegP("(") * lpegC((1-lpegP(")"))^1) * lpegP(")") * space * cstring * space * cnumber * space * lpegP("fshow")) / mps.fshow +local fshow = (lpegP("(") * lpegCs( ( lpegP("\\(")/"\\050" + lpegP("\\)")/"\\051" + (1-lpegP(")")) )^1 ) + * lpegP(")") * space * cstring * space * cnumber * space * lpegP("fshow")) / mps.fshow + +local setlinewidth_x = (lpegP("0") * sp * cnumber * sp * lpegP("dtransform truncate idtransform setlinewidth pop")) / mps.setlinewidth +local setlinewidth_y = (cnumber * sp * lpegP("0 dtransform exch truncate exch idtransform pop setlinewidth") ) / mps.setlinewidth + +local c = ((cnumber * sp)^6 * lpegP("c") ) / mps.curveto -- ^6 very inefficient, ^1 ok too +local l = ((cnumber * sp)^2 * lpegP("l") ) / mps.lineto +local r = ((cnumber * sp)^2 * lpegP("r") ) / mps.rlineto +local m = ((cnumber * sp)^2 * lpegP("m") ) / mps.moveto +local vlw = ((cnumber * sp)^1 * lpegP("vlw")) / mps.setlinewidth +local hlw = ((cnumber * sp)^1 * lpegP("hlw")) / mps.setlinewidth + +local R = ((cnumber * sp)^3 * lpegP("R") ) / mps.setrgbcolor +local C = ((cnumber * sp)^4 * lpegP("C") ) / mps.setcmykcolor +local G = ((cnumber * sp)^1 * lpegP("G") ) / mps.setgray + +local lj = ((cnumber * sp)^1 * lpegP("lj") ) / mps.setlinejoin +local ml = ((cnumber * sp)^1 * lpegP("ml") ) / mps.setmiterlimit +local lc = ((cnumber * sp)^1 * lpegP("lc") ) / mps.setlinecap + +local n = lpegP("n") / mps.newpath +local p = lpegP("p") / mps.closepath +local S = lpegP("S") / mps.stroke +local F = lpegP("F") / mps.fill +local B = lpegP("B") / mps.both +local W = lpegP("W") / mps.clip +local P = lpegP("P") / mps.showpage + +local q = lpegP("q") / mps.gsave +local Q = lpegP("Q") / mps.grestore + +local sd = (lpegP("[") * (cnumber * sp^0)^0 * lpegP("]") * sp * cnumber * sp * lpegP("sd")) / mps.setdash +local rd = ( lpegP("rd")) / mps.resetdash + +local s = ( (cnumber * sp^0)^2 * lpegP("s") ) / mps.scale +local t = (lpegP("[") * (cnumber * sp^0)^6 * lpegP("]") * sp * lpegP("t") ) / mps.concat + +-- experimental + +local preamble = ( + prolog + setup + + boundingbox + highresboundingbox + specials + special + + comment +) + +local procset = ( + lj + ml + lc + + c + l + m + n + p + r + + R + C + G + + S + F + B + W + + vlw + hlw + + Q + q + + sd + rd + + t + s + + fshow + + P +) + +local verbose = ( + curveto + lineto + moveto + newpath + closepath + rlineto + + setrgbcolor + setcmykcolor + setgray + + setlinejoin + setmiterlimit + setlinecap + + stroke + fill + clip + both + + setlinewidth_x + setlinewidth_y + + gsave + grestore + + concat + scale + + fshow + + setdash + -- no resetdash + showpage +) + +-- order matters in terms of speed / we could check for procset first + +local captures_old = ( space + verbose + preamble )^0 +--~ local captures_new = ( space + procset + preamble + verbose )^0 +local captures_new = ( space + verbose + procset + preamble )^0 + +local function parse(m_data) + if find(m_data,"%%%%BeginResource: procset mpost") then + lpegmatch(captures_new,m_data) + else + lpegmatch(captures_old,m_data) + end +end + +-- main converter + +local a_colorspace = attributes.private('colormodel') + +function mptopdf.convertmpstopdf(name) + resetall() + local ok, m_data, n = resolvers.loadbinfile(name, 'tex') -- we need a binary load ! + if ok then + mps.colormodel = tex.attribute[a_colorspace] + statistics.starttiming(mptopdf) + mptopdf.n = mptopdf.n + 1 + pdfcode(format("\\letterpercent\\space mptopdf begin: n=%s, file=%s",mptopdf.n,file.basename(name))) + pdfcode("q 1 0 0 1 0 0 cm") + parse(m_data) + pdfcode(pdffinishtransparencycode()) + pdfcode("Q") + pdfcode("\\letterpercent\\space mptopdf end") + resetall() + statistics.stoptiming(mptopdf) + else + commands.writestatus("mptopdf","file '%s' not found",name) + end +end + + +-- status info + +statistics.register("mps conversion time",function() + local n = mptopdf.n + if n > 0 then + return format("%s seconds, %s conversions", statistics.elapsedtime(mptopdf),n) + else + return nil + end +end) diff --git a/tex/context/base/meta-pdf.mkii b/tex/context/base/meta-pdf.mkii new file mode 100644 index 000000000..2099b0d37 --- /dev/null +++ b/tex/context/base/meta-pdf.mkii @@ -0,0 +1,2761 @@ +%D \module +%D [ file=meta-pdf, +%D version=2006.06.07, +%D title=\METAPOST\ Graphics, +%D subtitle=Conversion to \PDF, +%D author=Hans Hagen \& others (see text), +%D date=\currentdate, +%D copyright=\PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D Formerly known as supp-pdf.tex and supp-mpe.tex. + +%D We will clean up the color mess later. + +%D These macros are written as generic as possible. Some +%D general support macro's are loaded from a small module +%D especially made for non \CONTEXT\ use. In this module I +%D use a matrix transformation macro written by Tanmoy +%D Bhattacharya. Thanks to extensive testing by Sebastian +%D Ratz I was able to complete this module within reasonable +%D time. This module has support for \METAPOST\ extensions +%D built in. +%D +%D Daniel H. Luecking came up with a better (more precise) +%D transformation method. You can recognize his comment by +%D his initials. (We keep the old code around because it's a +%D nice illustration on how a module like this evolves.) + +% Beware, we cannot use 0pt here by defaukt since it may be +% defined in the range \dimen 0 - 20 which we happen to use +% as scratch registers; for this reason we start allocating +% scratch registers > 20 + +%D This module handles some \PDF\ conversion and insertions +%D topics. By default, the macros use the \PDFTEX\ primitive +%D \type{\pdfliteral} when available. Since \PDFTEX\ is now the +%D default engine for \TEX\ distributions, we need a more complex +%D test. + +\writestatus{loading}{MetaPost Graphics / MPS to PDF} + +\unprotect + +\ifx\PDFcode \undefined \let\PDFcode \gobbleoneargument \fi +\ifx\PDFcomment\undefined \def\PDFcomment#1{\PDFcode{\letterpercent\space#1}} \fi + +%D First we define a handy constant: + +\bgroup \catcode`\%=\@@other \xdef\letterpercent{\string%} \egroup + +%D \macros +%D {pdfimage,pdfimages,pdfclippedimage} +%D +%D Starting with pdftex version 14, images are included more +%D natural to the form embedding. This enables alternative +%D images to be embedded. +%D +%D \starttyping +%D \pdfimageThe directional helpers and pen analysis are more or less translated from the
+
In the process of cleaning up the lua variant of the parbuilder +we ran into a couple of functions (translated c macros) that were +somewhat inefficient. More convenient is to use hashes although at +the c-end still macros are used. In the process directions.h was +adapted and now has the mappings as comments. This lua file is +based on that file. +]]-- + + +nodes = nodes or { } + +nodes.is_mirrored = { + -- TLT = false, + -- TRT = false, + -- LTL = false, + -- RTT = false, +} + +nodes.is_rotated = { + -- TLT = false, + -- TRT = false, + -- LTL = false, + RTT = true, ["+RTT"] = true, +} + +nodes.textdir_is_parallel = { + TLT = { + TLT = true, ["+TLT"] = true, + TRT = true, ["+TRT"] = true, + -- LTL = false, + -- RTT = false, + }, + TRT= { + TLT = true, ["+TLT"] = true, + TRT = true, ["+TRT"] = true, + -- LTL = false, + -- RTT = false, + }, + LTL = { + -- TLT = false, + -- TRT = false, + LTL = true, ["+LTL"] = true, + RTT = true, ["+RTT"] = true, + }, + RTT = { + -- TLT = false, + -- TRT = false, + LTL = true, ["+LTL"] = true, + RTT = true, ["+RTT"] = true, + } +} + +nodes.pardir_is_parallel = { + TLT = { + TLT = true, ["+TLT"] = true, + TRT = true, ["+TRT"] = true, + -- LTL = false, + -- RTT = false, + }, + TRT = { + TLT = true, ["+TLT"] = true, + TRT = true, ["+TRT"] = true, + -- LTL = false, + -- RTT = false, + }, + LTL = { + -- TLT = false, + -- TRT = false, + LTL = true, ["+LTL"] = true, + RTT = true, ["+RTT"] = true, + }, + RTT = { + -- TLT = false, + -- TRT = false, + LTL = true, ["+LTL"] = true, + RTT = true, ["+RTT"] = true, + }, +} + +nodes.pardir_is_opposite = { + TLT = { + -- TLT = false, + -- TRT = false, + -- LTL = false, + -- RTT = false, + }, + TRT = { + -- TLT = false, + -- TRT = false, + -- LTL = false, + -- RTT = false, + }, + LTL = { + -- TLT = false, + -- TRT = false, + -- LTL = false, + RTT = true, ["+RTT"] = true, + }, + RTT = { + -- TLT = false, + -- TRT = false, + LTL = true, ["+LTL"] = true, + -- RTT = false, + }, +} + +nodes.textdir_is_opposite = { + TLT = { + -- TLT = false, + TRT = true, ["+TRT"] = true, + -- LTL = false, + -- RTT = false, + }, + TRT= { + TLT = true, ["+TLT"] = true, + -- TRT = false, + -- LTL = false, + -- RTT = false, + }, + LTL = { + -- TLT = false, + -- TRT = false, + -- LTL = false, + -- RTT = false, + }, + RTT = { + -- TLT = false, + -- TRT = false, + -- LTL = false, + -- RTT = false, + }, +} + +nodes.glyphdir_is_opposite = { + TLT = { + -- TLT = false, + -- TRT = false, + -- LTL = false, + -- RTT = false, + }, + TRT= { + -- TLT = false, + -- TRT = false, + -- LTL = false, + -- RTT = false, + }, + LTL = { + -- TLT = false, + -- TRT = false, + -- LTL = false, + -- RTT = false, + }, + RTT = { + -- TLT = false, + -- TRT = false, + -- LTL = false, + -- RTT = false, + }, +} + +nodes.pardir_is_equal = { + TLT = { + TLT = true, ["+TLT"] = true, + TRT = true, ["+TRT"] = true, + -- LTL = false, + -- RTT = false, + }, + TRT= { + TLT = true, ["+TLT"] = true, + TRT = true, ["+TRT"] = true, + -- LTL = false, + -- RTT = false, + }, + LTL= { + -- TLT = false, + -- TRT = false, + LTL = true, ["+LTL"] = true, + -- RTT = false, + }, + RTT= { + -- TLT = false, + -- TRT = false, + -- LTL = false, + RTT = true, ["+RTT"] = true, + }, +} + +nodes.textdir_is_equal = { + TLT = { + TLT = true, ["+TLT"] = true, + -- TRT = false, + -- LTL = false, + -- RTT = false, + }, + TRT= { + -- TLT = false, + TRT = true, ["+TRT"] = true, + -- LTL = false, + -- RTT = false, + }, + LTL = { + -- TLT = false, + -- TRT = false, + LTL = true, ["+LTL"] = true, + RTT = true, ["+RTT"] = true, + }, + RTT = { + -- TLT = false, + -- TRT = false, + LTL = true, ["+LTL"] = true, + RTT = true, ["+RTT"] = true, + }, +} + +nodes.glyphdir_is_equal = { + TLT = { + TLT = true, ["+TLT"] = true, + TRT = true, ["+TRT"] = true, + -- LTL = false, + RTT = true, ["+RTT"] = true, + }, + TRT= { + TLT = true, ["+TLT"] = true, + TRT = true, ["+TRT"] = true, + -- LTL = false, + RTT = true, ["+RTT"] = true, + }, + LTL = { + -- TLT = false, + -- TRT = false, + LTL = true, ["+LTL"] = true, + -- RTT = false, + }, + RTT = { + TLT = true, ["+TLT"] = true, + TRT = true, ["+TRT"] = true, + -- LTL = false, + RTT = true, ["+RTT"] = true, + }, +} + +nodes.partextdir_is_equal = { + TLT = { + -- TLT = false, + -- TRT = false, + LTL = true, ["+LTL"] = true, + RTT = true, ["+RTT"] = true, + }, + TRT= { + -- TLT = false, + -- TRT = false, + LTL = true, ["+LTL"] = true, + RTT = true, ["+RTT"] = true, + }, + LTL = { + TLT = true, ["+TLT"] = true, + -- TRT = false, + -- LTL = false, + -- RTT = false, + }, + RTT = { + -- TLT = false, + TRT = true, ["+TRT"] = true, + -- LTL = false, + -- RTT = false, + }, +} + +nodes.textdir_is_is = { + TLT = true, ["+TLT"] = true, + -- TRT = false, + -- LTL = false, + -- RTT = false, +} + +nodes.glyphdir_is_orthogonal = { + TLT = true, ["+TLT"] = true, + TRT = true, ["+TRT"] = true, + LTL = true, ["+LTL"] = true, + -- RTT = false +} + +nodes.dir_is_pop = { + ["-TRT"] = true, + ["-TLT"] = true, + ["-LTL"] = true, + ["-RTT"] = true, +} + +nodes.dir_negation = { + ["-TRT"] = "+TRT", + ["-TLT"] = "+TLT", + ["-LTL"] = "+LTL", + ["-RTT"] = "+RTT", + ["+TRT"] = "-TRT", + ["+TLT"] = "-TLT", + ["+LTL"] = "-LTL", + ["+RTT"] = "-RTT", +} diff --git a/tex/context/base/node-dum.lua b/tex/context/base/node-dum.lua new file mode 100644 index 000000000..f39a0873f --- /dev/null +++ b/tex/context/base/node-dum.lua @@ -0,0 +1,19 @@ +if not modules then modules = { } end modules ['node-dum'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +nodes = nodes or { } + +function nodes.simple_font_handler(head) +-- lang.hyphenate(head) + head = nodes.process_characters(head) + nodes.inject_kerns(head) + nodes.protect_glyphs(head) + head = node.ligaturing(head) + head = node.kerning(head) + return head +end diff --git a/tex/context/base/node-ext.lua b/tex/context/base/node-ext.lua new file mode 100644 index 000000000..5c4eec8de --- /dev/null +++ b/tex/context/base/node-ext.lua @@ -0,0 +1,30 @@ +if not modules then modules = { } end modules ['node-ext'] = { + version = 1.001, + comment = "companion to node-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +--[[ldx-- +
Serializing nodes can be handy for tracing. Also, saving and +loading node lists can come in handy as soon we are going to +use external applications to process node lists.
+--ldx]]-- + +function nodes.show(stack) +-- texio.write_nl(table.serialize(stack)) +end + +function nodes.save(stack,name) -- *.ltn : luatex node file +-- if name then +-- file.savedata(name,table.serialize(stack)) +-- else +-- texio.write_nl('log',table.serialize(stack)) +-- end +end + +function nodes.load(name) +-- return file.loaddata(name) +-- -- todo +end diff --git a/tex/context/base/node-fin.lua b/tex/context/base/node-fin.lua new file mode 100644 index 000000000..c6e3be448 --- /dev/null +++ b/tex/context/base/node-fin.lua @@ -0,0 +1,442 @@ +if not modules then modules = { } end modules ['node-fin'] = { + version = 1.001, + comment = "companion to node-fin.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- this module is being reconstructed + +local next, type, format = next, type, string.format +local texsprint = tex.sprint + +local ctxcatcodes = tex.ctxcatcodes + +local glyph = node.id('glyph') +local glue = node.id('glue') +local rule = node.id('rule') +local whatsit = node.id('whatsit') +local hlist = node.id('hlist') +local vlist = node.id('vlist') + +local has_attribute = node.has_attribute +local copy_node = node.copy + +local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming + +states = states or { } +shipouts = shipouts or { } + +local numbers = attributes.numbers +local trigger = attributes.private('trigger') +local triggering = false + +-- these two will be like trackers + +function states.enabletriggering() + triggering = true +end +function states.disabletriggering() + triggering = false +end + +-- the following code is no longer needed due to the new backend +-- but we keep it around for a while as an example +-- +-- states.collected = states.collected or { } +-- +-- storage.register("states/collected", states.collected, "states.collected") +-- +-- local collected = states.collected +-- +-- function states.collect(str) +-- collected[#collected+1] = str +-- end +-- +-- function states.flush() +-- if #collected > 0 then +-- for i=1,#collected do +-- texsprint(ctxcatcodes,collected[i]) -- we're in context mode anyway +-- end +-- collected = { } +-- states.collected = collected +-- end +-- end +-- +-- function states.check() +-- texio.write_nl(concat(collected,"\n")) +-- end + +-- we used to do the main processor loop here and call processor for each node +-- but eventually this was too much a slow down (1 sec on 23 for 120 pages mk) +-- so that we moved looping to the processor itself; this may lead to a bit of +-- duplicate code once that we have more state handlers + +local function process_attribute(head,plugin) -- head,attribute,enabled,initializer,resolver,processor,finalizer + local namespace = plugin.namespace + if namespace.enabled ~= false then -- this test will go away + starttiming(attributes) + local done, used, ok = false, nil, false + local attribute = namespace.attribute or numbers[plugin.name] -- todo: plugin.attribute + local processor = plugin.processor + if processor then + local initializer = plugin.initializer + local resolver = plugin.resolver + local inheritance = (resolver and resolver()) or nil -- -0x7FFFFFFF -- we can best use nil and skip ! + if initializer then + initializer(namespace,attribute,head) + end + head, ok = processor(namespace,attribute,head,inheritance) + if ok then + local finalizer = plugin.finalizer + if finalizer then + head, ok, used = finalizer(namespace,attribute,head) + if used then + local flusher = plugin.flusher + if flusher then + head = flusher(namespace,attribute,head,used) + end + end + end + done = true + end + end + stoptiming(attributes) + return head, done + else + return head, false + end +end + +nodes.process_attribute = process_attribute + +function nodes.install_attribute_handler(plugin) -- we need to avoid this nested function + return function(head) + return process_attribute(head,plugin) + end +end + +--~ experiment (maybe local to function makes more sense) +--~ +--~ plugindata = { } +--~ +--~ local template = [[ +--~ local plugin = plugindata["%s"] +--~ local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming +--~ local namespace = plugin.namespace +--~ local attribute = namespace.attribute +--~ local processor = plugin.processor +--~ local initializer = plugin.initializer +--~ local resolver = plugin.resolver +--~ local finalizer = plugin.finalizer +--~ local flusher = plugin.flusher +--~ return function (head) +--~ if namespace.enabled then +--~ starttiming(attributes) +--~ local done, used, ok = false, nil, false +--~ if procesxsor then +--~ local inheritance = (resolver and resolver()) or nil -- -0x7FFFFFFF -- we can best use nil and skip ! +--~ if initializer then +--~ initializer(namespace,attribute,head) +--~ end +--~ head, ok = processor(namespace,attribute,head,inheritance) +--~ if ok then +--~ if finalizer then +--~ head, ok, used = finalizer(namespace,attribute,head) +--~ if used and flusher then +--~ head = flusher(namespace,attribute,head,used) +--~ end +--~ end +--~ done = true +--~ end +--~ end +--~ stoptiming(attributes) +--~ return head, done +--~ else +--~ return head, false +--~ end +--~ end +--~ ]] +--~ +--~ function nodes.install_attribute_handler(plugin) -- we need to avoid this nested function +--~ plugindata[plugin.name] = plugin +--~ local str = format(template,plugin.name) +--~ return loadstring(str)() +--~ end + +-- the injectors + +local insert_node_before = node.insert_before +local insert_node_after = node.insert_after + +local nsdata, nsdone, nsforced, nsselector, nstrigger +local current, current_selector, done = 0, 0, false -- nb, stack has a local current ! + +function states.initialize(namespace,attribute,head) + nsdata, nsnone = namespace.data, namespace.none + nsforced, nsselector, nslistwise = namespace.forced, namespace.selector, namespace.listwise + nstrigger = triggering and namespace.triggering and trigger + current, current_selector, done = 0, 0, false -- todo: done cleanup +end + +function states.finalize(namespace,attribute,head) -- is this one ok? + if current > 0 and nsnone then + local id = head.id + if id == hlist or id == vlist then + local list = head.list + if list then + head.list = insert_node_before(list,list,copy_node(nsnone)) + end + else + head = insert_node_before(head,head,copy_node(nsnone)) + end + return head, true, true + end + return head, false, false +end + +local function process(namespace,attribute,head,inheritance,default) -- one attribute + local stack, done = head, false + while stack do + local id = stack.id + -- we need to deal with literals too (reset as well as oval) + -- if id == glyph or (id == whatsit and stack.subtype == 8) or (id == rule and stack.width ~= 0) or (id == glue and stack.leader) then -- or disc + if id == glyph or (id == rule and stack.width ~= 0) or (id == glue and stack.leader) then -- or disc + local c = has_attribute(stack,attribute) + if c then + if default and c == inheritance then + if current ~= default then + head = insert_node_before(head,stack,copy_node(nsdata[default])) + current, done = default, true + end + elseif current ~= c then + head = insert_node_before(head,stack,copy_node(nsdata[c])) + current, done = c, true + end + -- here ? compare selective + if id == glue then --leader + -- same as *list + local content = stack.leader + if content then + local savedcurrent = current + local ci = content.id + if ci == hlist or ci == vlist then + -- else we reset inside a box unneeded, okay, the downside is + -- that we trigger color in each repeated box, so there is room + -- for improvement here + current = 0 + end + local ok = false + if nstrigger and has_attribute(stack,nstrigger) then + local outer = has_attribute(stack,attribute) + if outer ~= inheritance then + stack.leader, ok = process(namespace,attribute,content,inheritance,outer) + else + stack.leader, ok = process(namespace,attribute,content,inheritance,default) + end + else + stack.leader, ok = process(namespace,attribute,content,inheritance,default) + end + current = savedcurrent + done = done or ok + end + end + elseif default and inheritance then + if current ~= default then + head = insert_node_before(head,stack,copy_node(nsdata[default])) + current, done = default, true + end + elseif current > 0 then + head = insert_node_before(head,stack,copy_node(nsnone)) + current, done = 0, true + end + elseif id == hlist or id == vlist then + local content = stack.list + if content then + local ok = false + if nstrigger and has_attribute(stack,nstrigger) then + local outer = has_attribute(stack,attribute) + if outer ~= inheritance then + stack.list, ok = process(namespace,attribute,content,inheritance,outer) + else + stack.list, ok = process(namespace,attribute,content,inheritance,default) + end + else + stack.list, ok = process(namespace,attribute,content,inheritance,default) + end + done = done or ok + end + end + stack = stack.next + end + return head, done +end + +states.process = process + +-- we can force a selector, e.g. document wide color spaces, saves a little +-- watch out, we need to check both the selector state (like colorspace) and +-- the main state (like color), otherwise we get into troubles when a selector +-- state changes while the main state stays the same (like two glyphs following +-- each other with the same color but different color spaces e.g. \showcolor) + +local function selective(namespace,attribute,head,inheritance,default) -- two attributes + local stack, done = head, false + while stack do + local id = stack.id + -- we need to deal with literals too (reset as well as oval) + -- if id == glyph or (id == whatsit and stack.subtype == 8) or (id == rule and stack.width ~= 0) or (id == glue and stack.leader) then -- or disc + if id == glyph or (id == rule and stack.width ~= 0) or (id == glue and stack.leader) then -- or disc + local c = has_attribute(stack,attribute) + if c then + if default and c == inheritance then + if current ~= default then + local data = nsdata[default] + head = insert_node_before(head,stack,copy_node(data[nsforced or has_attribute(stack,nsselector) or nsselector])) + current, done = default, true + end + else + local s = has_attribute(stack,nsselector) + if current ~= c or current_selector ~= s then + local data = nsdata[c] + head = insert_node_before(head,stack,copy_node(data[nsforced or has_attribute(stack,nsselector) or nsselector])) + current, current_selector, done = c, s, true + end + end + elseif default and inheritance then + if current ~= default then + local data = nsdata[default] + head = insert_node_before(head,stack,copy_node(data[nsforced or has_attribute(stack,nsselector) or nsselector])) + current, done = default, true + end + elseif current > 0 then + head = insert_node_before(head,stack,copy_node(nsnone)) + current, current_selector, done = 0, 0, true + end + if id == glue then -- leader + -- same as *list + local content = stack.leader + if content then + local savedcurrent = current + local ci = content.id + if ci == hlist or ci == vlist then + -- else we reset inside a box unneeded, okay, the downside is + -- that we trigger color in each repeated box, so there is room + -- for improvement here + current = 0 + end + local ok = false + if nstrigger and has_attribute(stack,nstrigger) then + local outer = has_attribute(stack,attribute) + if outer ~= inheritance then + stack.leader, ok = selective(namespace,attribute,content,inheritance,outer) + else + stack.leader, ok = selective(namespace,attribute,content,inheritance,default) + end + else + stack.leader, ok = selective(namespace,attribute,content,inheritance,default) + end + current = savedcurrent + done = done or ok + end + end + elseif id == hlist or id == vlist then + local content = stack.list + if content then + local ok = false + if nstrigger and has_attribute(stack,nstrigger) then + local outer = has_attribute(stack,attribute) + if outer ~= inheritance then + stack.list, ok = selective(namespace,attribute,content,inheritance,outer) + else + stack.list, ok = selective(namespace,attribute,content,inheritance,default) + end + else + stack.list, ok = selective(namespace,attribute,content,inheritance,default) + end + done = done or ok + end + end + stack = stack.next + end + return head, done +end + +states.selective = selective + +-- Ideally the next one should be merged with the previous but keeping it separate is +-- safer. We deal with two situations: efficient boxwise (layoutareas) and mixed layers +-- (as used in the stepper). In the stepper we cannot use the box branch as it involves +-- paragraph lines and then getsmixed up. A messy business (esp since we want to be +-- efficient). + +local function stacked(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise + local stack, done = head, false + local current, depth = default or 0, 0 + while stack do + local id = stack.id + if id == glyph or (id == rule and stack.width ~= 0) or (id == glue and stack.leader) then -- or disc + local c = has_attribute(stack,attribute) + if c then + if current ~= c then + head = insert_node_before(head,stack,copy_node(nsdata[c])) + depth = depth + 1 + current, done = c, true + end + if id == glue then + local content = stack.leader + if content then -- unchecked + local ok = false + stack.leader, ok = stacked(namespace,attribute,content,current) + done = done or ok + end + end + elseif default then + -- + elseif current > 0 then + head = insert_node_before(head,stack,copy_node(nsnone)) + depth = depth - 1 + current, done = 0, true + end + elseif id == hlist or id == vlist then + local content = stack.list + if content then + -- the problem is that broken lines gets the attribute which can be a later one + if nslistwise then + local c = has_attribute(stack,attribute) + if c and current ~= c and nslistwise[c] then -- viewerlayer + local p = current + current, done = c, true + head = insert_node_before(head,stack,copy_node(nsdata[c])) + stack.list = stacked(namespace,attribute,content,current) + head, stack = insert_node_after(head,stack,copy_node(nsnone)) + current = p + else + local ok = false + stack.list, ok = stacked(namespace,attribute,content,current) + done = done or ok + end + else + local ok = false + stack.list, ok = stacked(namespace,attribute,content,current) + done = done or ok + end + end + end + stack = stack.next + end + while depth > 0 do + head = insert_node_after(head,stack,copy_node(nsnone)) + depth = depth -1 + end + return head, done +end + +states.stacked = stacked + +-- -- -- + +statistics.register("attribute processing time", function() + return statistics.elapsedseconds(attributes,"front- and backend") +end) diff --git a/tex/context/base/node-fin.mkiv b/tex/context/base/node-fin.mkiv new file mode 100644 index 000000000..ea9e83f16 --- /dev/null +++ b/tex/context/base/node-fin.mkiv @@ -0,0 +1,74 @@ +%D \module +%D [ file=attr-ini, +%D version=2007.06.06, % probably a bit older +%D title=\CONTEXT\ Node Macros, +%D subtitle=Finalizing, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA-ADE] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Node Support / Finalizing} + +% Objects are processed indepently \unknown\ actually we may +% need a proper callback. + +\unprotect + +\registerctxluafile{node-fin}{1.001} % we might generalize this one + +\definesystemattribute[trigger] \chardef\triggerattribute \dogetattributeid{trigger} % feature inheritance + +\def\finalizeobjectbox #1{\ctxlua{nodes.process_page(tex.box[\number#1])}} +\def\finalizeshipoutbox#1{\ctxlua{nodes.process_page(tex.box[\number#1])}} + +% tricky stuff: + +% THIS MIGHT BECOME OBSOLETE. + +\newcount\attributeboxcount + +\edef\startinheritattributes{\attribute\triggerattribute\plusone} +\edef\stopinheritattributes {\attribute\triggerattribute\attributeunsetvalue} + +\def\doattributedcopy{\afterassignment\dodoattributedcopy\attributeboxcount} +\def\doattributedbox {\afterassignment\dodoattributedbox \attributeboxcount} + +\def\dodoattributedcopy + {\startinheritattributes + \ifvbox\attributeboxcount + \vbox{\unvcopy\attributeboxcount}% + \else + \hbox{\unhcopy\attributeboxcount}% + \fi + \stopinheritattributes} + +\def\dodoattributedbox + {\startinheritattributes + \ifvbox\attributeboxcount + \vbox{\unvbox\attributeboxcount}% + \else + \hbox{\unhbox\attributeboxcount}% + \fi + \stopinheritattributes} + +\def\enableattributeinheritance + {\ctxlua{states.enabletriggering()}% + \let\attributedcopy\doattributedcopy + \let\attributedbox \doattributedbox} + +\def\disableattributeinheritance + {\ctxlua{states.disabletriggering()}% + \let\attributedcopy\copy + \let\attributedbox \box} + +\disableattributeinheritance + +% \appendtoks +% \enableattributeinheritance % will become default +% \to\everyjob + +\protect \endinput diff --git a/tex/context/base/node-fnt.lua b/tex/context/base/node-fnt.lua new file mode 100644 index 000000000..b0d073425 --- /dev/null +++ b/tex/context/base/node-fnt.lua @@ -0,0 +1,207 @@ +if not modules then modules = { } end modules ['node-fnt'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local next, type = next, type + +local trace_characters = false trackers.register("nodes.characters", function(v) trace_characters = v end) + +local glyph = node.id('glyph') + +local traverse_id = node.traverse_id +local has_attribute = node.has_attribute + +local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming + +fonts = fonts or { } +fonts.tfm = fonts.tfm or { } +fonts.ids = fonts.ids or { } + +local fontdata = fonts.ids + +-- some tests with using an array of dynamics[id] and processes[id] demonstrated +-- that there was nothing to gain (unless we also optimize other parts) +-- +-- maybe getting rid of the intermediate shared can save some time + +-- potential speedup: check for subtype < 256 so that we can remove that test +-- elsewhere, danger: injected nodes will not be dealt with but that does not +-- happen often; we could consider processing sublists but that might need mor +-- checking later on; the current approach also permits variants + +if tex.attribute[0] < 0 then + + texio.write_nl("log","!") + texio.write_nl("log","! Attribute 0 is reserved for ConTeXt's font feature management and has to be") + texio.write_nl("log","! set to zero. Also, some attributes in the range 1-255 are used for special") + texio.write_nl("log","! purposed so setting them at the TeX end might break the font handler.") + texio.write_nl("log","!") + + tex.attribute[0] = 0 -- else no features + +end + +-- this will be redone and split in a generic one and a context one + +function nodes.process_characters(head) + -- either next or not, but definitely no already processed list + starttiming(nodes) + local usedfonts, attrfonts, done = { }, { }, false + local a, u, prevfont, prevattr = 0, 0, nil, 0 + for n in traverse_id(glyph,head) do + local font, attr = n.font, has_attribute(n,0) -- zero attribute is reserved for fonts in context + if attr and attr > 0 then + if font ~= prevfont or attr ~= prevattr then + local used = attrfonts[font] + if not used then + used = { } + attrfonts[font] = used + end + if not used[attr] then + -- we do some testing outside the function + local tfmdata = fontdata[font] + local shared = tfmdata.shared + if shared then + local dynamics = shared.dynamics + if dynamics then + local d = shared.set_dynamics(font,dynamics,attr) -- still valid? + if d then + used[attr] = d + a = a + 1 + end + end + end + end + prevfont, prevattr = font, attr + end + elseif font ~= prevfont then + prevfont, prevattr = font, 0 + local used = usedfonts[font] + if not used then + local tfmdata = fontdata[font] + if tfmdata then + local shared = tfmdata.shared -- we need to check shared, only when same features + if shared then + local processors = shared.processes + if processors and #processors > 0 then + usedfonts[font] = processors + u = u + 1 + end + end + else + -- probably nullfont + end + end + else + prevattr = attr + end + end + -- we could combine these and just make the attribute nil + if u == 1 then + local font, processors = next(usedfonts) + local n = #processors + if n > 0 then + local h, d = processors[1](head,font,false) + head, done = h or head, done or d + if n > 1 then + for i=2,n do + local h, d = processors[i](head,font,false) + head, done = h or head, done or d + end + end + end + elseif u > 0 then + for font, processors in next, usedfonts do + local n = #processors + local h, d = processors[1](head,font,false) + head, done = h or head, done or d + if n > 1 then + for i=2,n do + local h, d = processors[i](head,font,false) + head, done = h or head, done or d + end + end + end + end + if a == 1 then + local font, dynamics = next(attrfonts) + for attribute, processors in next, dynamics do -- attr can switch in between + local n = #processors + local h, d = processors[1](head,font,attribute) + head, done = h or head, done or d + if n > 1 then + for i=2,n do + local h, d = processors[i](head,font,attribute) + head, done = h or head, done or d + end + end + end + elseif a > 0 then + for font, dynamics in next, attrfonts do + for attribute, processors in next, dynamics do -- attr can switch in between + local n = #processors + local h, d = processors[1](head,font,attribute) + head, done = h or head, done or d + if n > 1 then + for i=2,n do + local h, d = processors[i](head,font,attribute) + head, done = h or head, done or d + end + end + end + end + end + stoptiming(nodes) + if trace_characters then + nodes.report(head,done) + end + return head, true +end + +if node.protect_glyphs then + + nodes.protect_glyphs = node.protect_glyphs + nodes.unprotect_glyphs = node.unprotect_glyphs + +else do + + -- initial value subtype : X000 0001 = 1 = 0x01 = char + -- + -- expected before linebreak : X000 0000 = 0 = 0x00 = glyph + -- X000 0010 = 2 = 0x02 = ligature + -- X000 0100 = 4 = 0x04 = ghost + -- X000 1010 = 10 = 0x0A = leftboundary lig + -- X001 0010 = 18 = 0x12 = rightboundary lig + -- X001 1010 = 26 = 0x1A = both boundaries lig + -- X000 1100 = 12 = 0x1C = leftghost + -- X001 0100 = 20 = 0x14 = rightghost + + function nodes.protect_glyphs(head) + local done = false + for g in traverse_id(glyph,head) do + local s = g.subtype + if s == 1 then + done, g.subtype = true, 256 + elseif s <= 256 then + done, g.subtype = true, 256 + s + end + end + return done + end + + function nodes.unprotect_glyphs(head) + local done = false + for g in traverse_id(glyph,head) do + local s = g.subtype + if s > 256 then + done, g.subtype = true, s - 256 + end + end + return done + end + +end end diff --git a/tex/context/base/node-ini.lua b/tex/context/base/node-ini.lua new file mode 100644 index 000000000..36e240238 --- /dev/null +++ b/tex/context/base/node-ini.lua @@ -0,0 +1,244 @@ +if not modules then modules = { } end modules ['node-ini'] = { + version = 1.001, + comment = "companion to node-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +--[[ldx-- +Most of the code that had accumulated here is now separated in +modules.
+--ldx]]-- + +-- this module is being reconstructed + +local utf = unicode.utf8 +local next, type = next, type +local format, concat, match, utfchar = string.format, table.concat, string.match, utf.char + +local chardata = characters and characters.data + +--[[ldx-- +We start with a registration system for atributes so that we can use the +symbolic names later on.
+--ldx]]-- + +attributes = attributes or { } + +attributes.names = attributes.names or { } +attributes.numbers = attributes.numbers or { } +attributes.list = attributes.list or { } +attributes.unsetvalue = -0x7FFFFFFF + +storage.register("attributes/names", attributes.names, "attributes.names") +storage.register("attributes/numbers", attributes.numbers, "attributes.numbers") +storage.register("attributes/list", attributes.list, "attributes.list") + +local names, numbers, list = attributes.names, attributes.numbers, attributes.list + +function attributes.define(name,number) -- at the tex end + if not numbers[name] then + numbers[name], names[number], list[number] = number, name, { } + end +end + +--[[ldx-- +We can use the attributes in the range 127-255 (outside user space). These
+are only used when no attribute is set at the \TEX\ end which normally
+happens in
Access to nodes is what gives
When manipulating node lists in
First of all, we noticed that the bottleneck is more with excessive
+callbacks (some gets called very often) and the conversion from and to
+
This resulted in two special situations in passing nodes back to
+
Insertion is handled (at least in
When we collapse (something that we only do when really needed), we +also ignore the empty nodes. [This is obsolete!]
+--ldx]]-- + +nodes = nodes or { } + +local hlist = node.id('hlist') +local vlist = node.id('vlist') +local glyph = node.id('glyph') +local glue = node.id('glue') +local penalty = node.id('penalty') +local kern = node.id('kern') +local whatsit = node.id('whatsit') + +local traverse_id = node.traverse_id +local traverse = node.traverse +local free_node = node.free +local remove_node = node.remove +local insert_node_before = node.insert_before +local insert_node_after = node.insert_after + +function nodes.remove(head, current, free_too) + local t = current + head, current = remove_node(head,current) + if t then + if free_too then + free_node(t) + t = nil + else + t.next, t.prev = nil, nil + end + end + return head, current, t +end + +function nodes.delete(head,current) + return nodes.remove(head,current,true) +end + +nodes.before = insert_node_before +nodes.after = insert_node_after + +-- we need to test this, as it might be fixed now + +function nodes.before(h,c,n) + if c then + if c == h then + n.next = h + n.prev = nil + h.prev = n + else + local cp = c.prev + n.next = c + n.prev = cp + if cp then + cp.next = n + end + c.prev = n + return h, n + end + end + return n, n +end + +function nodes.after(h,c,n) + if c then + local cn = c.next + if cn then + n.next = cn + cn.prev = n + else + n.next = nil + end + c.next = n + n.prev = c + return h, n + end + return n, n +end + +-- local h, c = nodes.replace(head,current,new) +-- local c = nodes.replace(false,current,new) +-- local c = nodes.replace(current,new) + +function nodes.replace(head,current,new) -- no head returned if false + if not new then + head, current, new = false, head, current + end + local prev, next = current.prev, current.next + if next then + new.next, next.prev = next, new + end + if prev then + new.prev, prev.next = prev, new + end + if head then + if head == current then + head = new + end + free_node(current) + return head, new + else + free_node(current) + return new + end +end + +-- will move + +local function count(stack,flat) + local n = 0 + while stack do + local id = stack.id + if not flat and id == hlist or id == vlist then + local list = stack.list + if list then + n = n + 1 + count(list) -- self counts too + else + n = n + 1 + end + else + n = n + 1 + end + stack = stack.next + end + return n +end + +nodes.count = count + +-- new, will move + +function attributes.ofnode(n) + local a = n.attr + if a then + local names = attributes.names + a = a.next + while a do + local number, value = a.number, a.value + texio.write_nl(format("%s : attribute %3i, value %4i, name %s",tostring(n),number,value,names[number] or '?')) + a = a.next + end + end +end + +local left, space = lpeg.P("<"), lpeg.P(" ") + +nodes.filterkey = left * (1-left)^0 * left * space^0 * lpeg.C((1-space)^0) diff --git a/tex/context/base/node-ini.mkiv b/tex/context/base/node-ini.mkiv new file mode 100644 index 000000000..787259316 --- /dev/null +++ b/tex/context/base/node-ini.mkiv @@ -0,0 +1,90 @@ +%D \module +%D [ file=node-ini, +%D version=2006.08.20, +%D title=\CONTEXT\ Node Macros, +%D subtitle=Initialization, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Node Support / Initialization} + +\unprotect + +\newcount\filterstate \filterstate\plusone + +\registerctxluafile{node-ini}{1.001} +\registerctxluafile{node-dir}{1.001} +\registerctxluafile{node-aux}{1.001} +\registerctxluafile{node-tst}{1.001} +\registerctxluafile{node-tra}{1.001} % we might split it off (module) +\registerctxluafile{node-seq}{1.001} % we might generalize this one +\registerctxluafile{node-tsk}{1.001} +\registerctxluafile{node-tex}{1.001} +\registerctxluafile{node-res}{1.001} +\registerctxluafile{node-pro}{1.001} +\registerctxluafile{node-shp}{1.001} +\registerctxluafile{node-ser}{1.001} +\registerctxluafile{node-ext}{1.001} +\registerctxluafile{node-inj}{1.001} % we might split it off +\registerctxluafile{node-typ}{1.001} % experimental + +\newtoks \attributesresetlist + +\ifdefined \v!global \else \def\v!global{global} \fi % for metatex + +\unexpanded\def\defineattribute + {\dodoubleempty\dodefineattribute} + +\def\dodefineattribute[#1][#2]% alternatively we can let lua do the housekeeping + {\expandafter\newattribute\csname @attr@#1\endcsname + \expandafter \xdef\csname :attr:#1\endcsname{\number\lastallocatedattribute}% + \ctxlua{attributes.define("#1",\number\lastallocatedattribute)}% + %\writestatus\m!systems{defining attribute #1 with number \number\lastallocatedattribute}% + \doifnotinset\v!global{#2}{\appendetoks\csname @attr@#1\endcsname\attributeunsetvalue\to\attributesresetlist}} + +\unexpanded\def\definesystemattribute + {\dodoubleempty\dodefinesystemattribute} + +\def\dodefinesystemattribute[#1][#2]% alternatively we can let lua do the housekeeping + {\scratchcounter\ctxlua{tex.print(attributes.private("#1"))}\relax + \global\expandafter\attributedef\csname @attr@#1\endcsname\scratchcounter + \expandafter \xdef\csname :attr:#1\endcsname{\number\scratchcounter}% + %\writestatus\m!systems{defining system attribute #1 with number \number\scratchcounter}% + \doifnotinset\v!global{#2}{\appendetoks\csname @attr@#1\endcsname\attributeunsetvalue\to\attributesresetlist}} + +% expandable so we can \edef them for speed + +\def\dosetattribute#1#2{\csname @attr@#1\endcsname#2\relax} +\def\doresetattribute#1{\csname @attr@#1\endcsname\attributeunsetvalue} +\def\dogetattribute #1{\number\csname @attr@#1\endcsname} +\def\dogetattributeid#1{\csname :attr:#1\endcsname} + +\let\dompattribute\gobbletwoarguments + +\def\resetallattributes{\the\attributesresetlist} + +\newcount\shownodescounter + +\def\shownextnodes {\afterassignment\doshownodes\shownextnodescounter} +\def\showflatnodes {\afterassignment\doshownodes\showflatnodescounter} +\def\doshownextnodes {\ctxlua{texio.write_nl('log',nodes.serializebox(\number\shownodescounter,false,true))}} +\def\doshowflatnodes {\ctxlua{texio.write_nl('log',nodes.serializebox(\number\shownodescounter,true, true))}} +\def\visualizenextnodes{\dowithnextbox{\ctxlua{nodes.visualizebox(\number\nextbox,false,true)}}} +\def\visualizeflatnodes{\dowithnextbox{\ctxlua{nodes.visualizebox(\number\nextbox,true,true)}}} + +\unexpanded\def\starttracingnodes[#1]{\ctxlua{nodes.tracers.characters.start("#1")}} +\unexpanded\def\stoptracingnodes {\ctxlua{nodes.tracers.characters.stop()}} + +% \starttext +% \starttracingnodes[characters] +% \input tufte \par +% \input tufte \par +% \stoptracingnodes +% \stoptext + +\protect \endinput diff --git a/tex/context/base/node-inj.lua b/tex/context/base/node-inj.lua new file mode 100644 index 000000000..9c4612a22 --- /dev/null +++ b/tex/context/base/node-inj.lua @@ -0,0 +1,466 @@ +if not modules then modules = { } end modules ['node-inj'] = { + version = 1.001, + comment = "companion to node-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- tricky ... fonts.ids is not yet defined .. to be solved (maybe general tex ini) + +-- This is very experimental (this will change when we have luatex > .50 and +-- a few pending thingies are available. Also, Idris needs to make a few more +-- test fonts. Btw, future versions of luatex will have extended glyph properties +-- that can be of help. + +local next = next + +local trace_injections = false trackers.register("nodes.injections", function(v) trace_injections = v end) + +fonts = fonts or { } +fonts.tfm = fonts.tfm or { } +fonts.ids = fonts.ids or { } + +local fontdata = fonts.ids + +local glyph = node.id('glyph') +local kern = node.id('kern') + +local traverse_id = node.traverse_id +local has_attribute = node.has_attribute +local set_attribute = node.set_attribute +local insert_node_before = node.insert_before +local insert_node_after = node.insert_after + +local newkern = nodes.kern + +local markbase = attributes.private('markbase') +local markmark = attributes.private('markmark') +local markdone = attributes.private('markdone') +local cursbase = attributes.private('cursbase') +local curscurs = attributes.private('curscurs') +local cursdone = attributes.private('cursdone') +local kernpair = attributes.private('kernpair') + +local cursives = { } +local marks = { } +local kerns = { } + +-- currently we do gpos/kern in a bit inofficial way but when we +-- have the extra fields in glyphnodes to manipulate ht/dp/wd +-- explicitly i will provide an alternative; also, we can share +-- tables + +-- for the moment we pass the r2l key ... volt/arabtype tests + +function nodes.set_cursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) + local dx, dy = factor*(exit[1]-entry[1]), factor*(exit[2]-entry[2]) + local ws, wn = tfmstart.width, tfmnext.width + local bound = #cursives + 1 + set_attribute(start,cursbase,bound) + set_attribute(nxt,curscurs,bound) + cursives[bound] = { rlmode, dx, dy, ws, wn } + return dx, dy, bound +end + +function nodes.set_pair(current,factor,rlmode,r2lflag,spec,tfmchr) + local x, y, w, h = factor*spec[1], factor*spec[2], factor*spec[3], factor*spec[4] + -- dy = y - h + if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then + local bound = has_attribute(current,kernpair) + if bound then + local kb = kerns[bound] + -- inefficient but singles have less, but weird anyway, needs checking + kb[2], kb[3], kb[4], kb[5] = (kb[2] or 0) + x, (kb[3] or 0) + y, (kb[4] or 0)+ w, (kb[5] or 0) + h + else + bound = #kerns + 1 + set_attribute(current,kernpair,bound) + kerns[bound] = { rlmode, x, y, w, h, r2lflag, tfmchr.width } + end + return x, y, w, h, bound + end + return x, y, w, h -- no bound +end + +function nodes.set_kern(current,factor,rlmode,x,tfmchr) + local dx = factor*x + if dx ~= 0 then + local bound = #kerns + 1 + set_attribute(current,kernpair,bound) + kerns[bound] = { rlmode, dx } + end + return dx, bound +end + +function nodes.set_mark(start,base,factor,rlmode,ba,ma,index) --ba=baseanchor, ma=markanchor + local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2]) + local bound = has_attribute(base,markbase) + if bound then + local mb = marks[bound] + if mb then + if not index then index = #mb + 1 end + mb[index] = { dx, dy } + set_attribute(start,markmark,bound) + set_attribute(start,markdone,index) + return dx, dy, bound + else + logs.report("nodes mark", "possible problem, U+%04X is base without data (id: %s)",base.char,bound) + end + end + index = index or 1 + bound = #marks + 1 + set_attribute(base,markbase,bound) + set_attribute(start,markmark,bound) + set_attribute(start,markdone,index) + marks[bound] = { [index] = { dx, dy } } + return dx, dy, bound +end + +function nodes.trace_injection(head) + local function dir(n) + return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or ("unset") + end + local function report(...) + logs.report("nodes finisher",...) + end + report("begin run") + for n in traverse_id(glyph,head) do + if n.subtype < 256 then + local kp = has_attribute(n,kernpair) + local mb = has_attribute(n,markbase) + local mm = has_attribute(n,markmark) + local md = has_attribute(n,markdone) + local cb = has_attribute(n,cursbase) + local cc = has_attribute(n,curscurs) + report("char U+%05X, font=%s",n.char,n.font) + if kp then + local k = kerns[kp] + if k[3] then + report(" pairkern: dir=%s, x=%s, y=%s, w=%s, h=%s",dir(k[1]),k[2] or "?",k[3] or "?",k[4] or "?",k[5] or "?") + else + report(" kern: dir=%s, dx=%s",dir(k[1]),k[2] or "?") + end + end + if mb then + report(" markbase: bound=%s",mb) + end + if mm then + local m = marks[mm] + if mb then + local m = m[mb] + if m then + report(" markmark: bound=%s, index=%s, dx=%s, dy=%s",mm,md or "?",m[1] or "?",m[2] or "?") + else + report(" markmark: bound=%s, missing index",mm) + end + else + m = m[1] + report(" markmark: bound=%s, dx=%s, dy=%s",mm,m[1] or "?",m[2] or "?") + end + end + if cb then + report(" cursbase: bound=%s",cb) + end + if cc then + local c = cursives[cc] + report(" curscurs: bound=%s, dir=%s, dx=%s, dy=%s",cc,dir(c[1]),c[2] or "?",c[3] or "?") + end + end + end + report("end run") +end + +-- todo: reuse tables (i.e. no collection), but will be extra fields anyway + +function nodes.inject_kerns(head,where,keep) + local has_marks, has_cursives, has_kerns = next(marks), next(cursives), next(kerns) + if has_marks or has_cursives then +--~ if has_marks or has_cursives or has_kerns then + if trace_injections then + nodes.trace_injection(head) + end + -- in the future variant we will not copy items but refs to tables + local done, ky, rl, valid, cx, wx, mk = false, { }, { }, { }, { }, { }, { } + if has_kerns then -- move outside loop + local nf, tm = nil, nil + for n in traverse_id(glyph,head) do + if n.subtype < 256 then + valid[#valid+1] = n + if n.font ~= nf then + nf = n.font + tm = fontdata[nf].marks + end + mk[n] = tm[n.char] + local k = has_attribute(n,kernpair) + if k then + local kk = kerns[k] + if kk then + local x, y, w, h = kk[2] or 0, kk[3] or 0, kk[4] or 0, kk[5] or 0 + local dy = y - h + if dy ~= 0 then + ky[n] = dy + end + if w ~= 0 or x ~= 0 then + wx[n] = kk + end + rl[n] = kk[1] -- could move in test + end + end + end + end + else + local nf, tm = nil, nil + for n in traverse_id(glyph,head) do + if n.subtype < 256 then + valid[#valid+1] = n + if n.font ~= nf then + nf = n.font + tm = fontdata[nf].marks + end + mk[n] = tm[n.char] + end + end + end + if #valid > 0 then + -- we can assume done == true because we have cursives and marks + local cx = { } + if has_kerns and next(ky) then + for n, k in next, ky do + n.yoffset = k + end + end + -- todo: reuse t and use maxt + if has_cursives then + local p_cursbase, p = nil, nil + -- since we need valid[n+1] we can also use a "while true do" + local t, d, maxt = { }, { }, 0 + for i=1,#valid do -- valid == glyphs + local n = valid[i] + if not mk[n] then + local n_cursbase = has_attribute(n,cursbase) + if p_cursbase then + local n_curscurs = has_attribute(n,curscurs) + if p_cursbase == n_curscurs then + local c = cursives[n_curscurs] + if c then + local rlmode, dx, dy, ws, wn = c[1], c[2], c[3], c[4], c[5] + if rlmode >= 0 then + dx = dx - ws + else + dx = dx + wn + end + if dx ~= 0 then + cx[n] = dx + rl[n] = rlmode + end + -- if rlmode and rlmode < 0 then + dy = -dy + -- end + maxt = maxt + 1 + t[maxt] = p + d[maxt] = dy + else + maxt = 0 + end + end + elseif maxt > 0 then + local ny = n.yoffset + for i=maxt,1,-1 do + ny = ny + d[i] + local ti = t[i] + ti.yoffset = ti.yoffset + ny + end + maxt = 0 + end + if not n_cursbase and maxt > 0 then + local ny = n.yoffset + for i=maxt,1,-1 do + ny = ny + d[i] + local ti = t[i] + ti.yoffset = ny + end + maxt = 0 + end + p_cursbase, p = n_cursbase, n + end + end + if maxt > 0 then + local ny = n.yoffset + for i=maxt,1,-1 do + ny = ny + d[i] + local ti = t[i] + ti.yoffset = ny + end + maxt = 0 + end + if not keep then + cursives = { } + end + end + if has_marks then + for i=1,#valid do + local p = valid[i] + local p_markbase = has_attribute(p,markbase) + if p_markbase then + local mrks = marks[p_markbase] + for n in traverse_id(glyph,p.next) do + local n_markmark = has_attribute(n,markmark) + if p_markbase == n_markmark then + local index = has_attribute(n,markdone) or 1 + local d = mrks[index] + if d then + -- local rlmode = d[3] -- not used + -- if rlmode and rlmode > 0 then + -- todo + -- else + local k = wx[p] + if k then + n.xoffset = p.xoffset - d[1] - k[2] + else + n.xoffset = p.xoffset - d[1] + end + -- end + if mk[p] then + n.yoffset = p.yoffset + d[2] + else + n.yoffset = n.yoffset + p.yoffset + d[2] + end + end + else + break + end + end + end + end + if not keep then + marks = { } + end + end + -- todo : combine + if next(wx) then + for n, k in next, wx do + -- only w can be nil, can be sped up when w == nil + local rl, x, w, r2l = k[1], k[2] or 0, k[4] or 0, k[6] + local wx = w - x +--~ if rl < 0 then +--~ if r2l then +--~ if wx ~= 0 then +--~ insert_node_before(head,n,newkern(wx)) +--~ end +--~ if x ~= 0 then +--~ insert_node_after (head,n,newkern(x)) +--~ end +--~ else +--~ if x ~= 0 then +--~ insert_node_before(head,n,newkern(x)) +--~ end +--~ if wx ~= 0 then +--~ insert_node_after(head,n,newkern(wx)) +--~ end +--~ end +--~ else + if r2l then + if wx ~= 0 then + insert_node_before(head,n,newkern(wx)) + end + if x ~= 0 then + insert_node_after (head,n,newkern(x)) + end + else + if x ~= 0 then + insert_node_before(head,n,newkern(x)) + end + if wx ~= 0 then + insert_node_after(head,n,newkern(wx)) + end + end +--~ end + end + end + if next(cx) then + for n, k in next, cx do + if k ~= 0 then + local rln = rl[n] + if rln and rln < 0 then + insert_node_before(head,n,newkern(-k)) + else + insert_node_before(head,n,newkern(k)) + end + end + end + end + if not keep then + kerns = { } + end + return head, true + elseif not keep then + kerns, cursives, marks = { }, { }, { } + end + elseif has_kerns then + if trace_injections then + nodes.trace_injection(head) + end + for n in traverse_id(glyph,head) do + local k = has_attribute(n,kernpair) + if k then + local kk = kerns[k] + if kk then + local rl, x, y, w = kk[1], kk[2] or 0, kk[3], kk[4] + if y and y ~= 0 then + n.yoffset = y -- todo: h ? + end + if w then + -- copied from above + local r2l = kk[6] + local wx = w - x +--~ if rl < 0 then +--~ if r2l then +--~ if x ~= 0 then +--~ insert_node_before(head,n,newkern(x)) +--~ end +--~ if wx ~= 0 then +--~ insert_node_after(head,n,newkern(wx)) +--~ end +--~ else +--~ if wx ~= 0 then +--~ insert_node_before(head,n,newkern(wx)) +--~ end +--~ if x ~= 0 then +--~ insert_node_after (head,n,newkern(x)) +--~ end +--~ end +--~ else + if r2l then + if wx ~= 0 then + insert_node_before(head,n,newkern(wx)) + end + if x ~= 0 then + insert_node_after (head,n,newkern(x)) + end + else + if x ~= 0 then + insert_node_before(head,n,newkern(x)) + end + if wx ~= 0 then + insert_node_after(head,n,newkern(wx)) + end + end +--~ end + else + -- simple (e.g. kernclass kerns) + if x ~= 0 then + insert_node_before(head,n,newkern(x)) + end + end + end + end + end + if not keep then + kerns = { } + end + return head, true + else + -- no tracing needed + end + return head, false +end diff --git a/tex/context/base/node-mig.lua b/tex/context/base/node-mig.lua new file mode 100644 index 000000000..f9f0ad231 --- /dev/null +++ b/tex/context/base/node-mig.lua @@ -0,0 +1,120 @@ +if not modules then modules = { } end modules ['node-mig'] = { + version = 1.001, + comment = "companion to node-mig.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local format = string.format + +local hlist = node.id('hlist') +local vlist = node.id('vlist') +local insert = node.id('ins') +local mark = node.id('mark') + +local has_attribute = node.has_attribute +local set_attribute = node.set_attribute +local remove_nodes = nodes.remove + +local migrated = attributes.private("migrated") + +local trace_migrations = false + +trackers.register("nodes.migrations", function(v) trace_migrations = v end) + +local migrate_inserts, migrate_marks + +local t_inserts, t_marks, t_sweeps = 0, 0, 0 + +local function locate(head,first,last,ni,nm) + local current = head + while current do + local id = current.id + if id == vlist or id == hlist then + current.list, first, last, ni, nm = locate(current.list,first,last,ni,nm) + current= current.next + elseif migrate_inserts and id == insert then + local insert + head, current, insert = remove_nodes(head,current) + insert.next = nil + if first then + insert.prev, last.next = last, insert + else + insert.prev, first = nil, insert + end + last, ni = insert, ni + 1 + elseif migrate_marks and id == mark then + local mark + head, current, mark = remove_nodes(head,current) + mark.next = nil + if first then + mark.prev, last.next = last, mark + else + mark.prev, first = nil, mark + end + last, nm = mark, nm + 1 + else + current= current.next + end + end + return head, first, last, ni, nm +end + +function nodes.migrate_outwards(head,where) + local done = false + if head then + local current = head + while current do + local id = current.id + if id == vlist or id == hlist and not has_attribute(current,migrated) then + set_attribute(current,migrated,1) + t_sweeps = t_sweeps + 1 + local h = current.list + local first, last, ni, nm + while h do + local id = h.id + if id == vlist or id == hlist then + h, first, last, ni, nm = locate(h,first,last,0,0) + end + h = h.next + end + if first then + t_inserts, t_marks = t_inserts + ni, t_marks + nm + if trace_migrations and (ni > 0 or nm > 0) then + logs.report("nodes","sweep %s, %s inserts and %s marks migrated outwards",t_sweeps,ni,nm) + end + -- inserts after head + local n = current.next + if n then + last.next, n.prev = n, last + end + current.next, first.prev = first, current + done, current = true, last + end + end + current = current.next + end + return head, done + end +end + +experiments.register("marks.migrate", function(v) + if v then + tasks.enableaction("mvlbuilders", "nodes.migrate_outwards") + end + migrate_marks = v +end) + +experiments.register("inserts.migrate", function(v) + if v then + tasks.enableaction("mvlbuilders", "nodes.migrate_outwards") + end + migrate_inserts = v +end) + +statistics.register("node migrations", function() + if trace_migrations and t_sweeps > 0 then + return format("%s sweeps, %s inserts moved, %s marks moved",t_sweeps,t_inserts,t_marks) + end +end) diff --git a/tex/context/base/node-mig.mkiv b/tex/context/base/node-mig.mkiv new file mode 100644 index 000000000..cd731b96f --- /dev/null +++ b/tex/context/base/node-mig.mkiv @@ -0,0 +1,60 @@ +%D \module +%D [ file=node-mig, +%D version=2010.01.19, +%D title=\CONTEXT\ Node Macros, +%D subtitle=Inserts, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Node Support / Migrations} + +%D This is very experimental and although it is used in a +%D real project it mostly serves a testbed for Taco and me so +%D as prelude to extending the inserts mechanism. +%D +%D \starttyping +%D \automigrateinserts +%D +%D \hbox{\vbox{x\footnote{1}x\footnote{2}x\footnote{3}x}} +%D \vbox{x\footnote{4}x} +%D \hbox{\vbox{x\footnote{5}x}} +%D +%D \hbox{x} +%D \hbox{x\footnote{6}x} +%D \hbox{x} +%D +%D \starttabulate +%D \NC test \NC test \footnote{test} \input tufte \NC \NR +%D \NC test \NC test \footnote{test} \input tufte \NC \NR +%D \NC test \NC test \footnote{test} \input tufte \NC \NR +%D \NC test \NC test \footnote{test} \input tufte \NC \NR +%D \NC test \NC test \footnote{test} \input tufte \NC \NR +%D \NC test \NC test \footnote{test} \input tufte \NC \NR +%D \stoptabulate +%D \stoptyping + +\unprotect + +\registerctxluafile{node-mig}{1.001} + +\newtoks \everyautomigratefootnotes +\newtoks \everyautomigratemarks + +\def\automigrateinserts{\the\everyautomigratefootnotes} +\def\automigratemarks {\the\everyautomigratemarks} + +\appendtoks + \let\postponenotes\relax + \enableexperiments[inserts.migrate]% +\to \everyautomigratefootnotes + +\appendtoks + \enableexperiments[marks.migrate]% +\to \everyautomigratemarks + +\protect diff --git a/tex/context/base/node-pag.lua b/tex/context/base/node-pag.lua new file mode 100644 index 000000000..4bc53c6dd --- /dev/null +++ b/tex/context/base/node-pag.lua @@ -0,0 +1,30 @@ +if not modules then modules = { } end modules ['node-pag'] = { + version = 1.001, + comment = "companion to node-pag.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- this callback might disappear and come back in the same way +-- as par builders + +pagebuilders = pagebuilders or { } + +local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming + +local actions = tasks.actions("pagebuilders",5) + +local function processor(head,groupcode,size,packtype,maxdepth,direction) + starttiming(pagebuilders) + local _, done = actions(head,groupcode,size,packtype,maxdepth,direction) + stoptiming(pagebuilders) + return (done and head) or true +-- return vpack(head) +end + +--~ callbacks.register('pre_output_filter', processor, "preparing output box") + +--~ statistics.register("output preparation time", function() +--~ return statistics.elapsedseconds(pagebuilders) +--~ end) diff --git a/tex/context/base/node-pag.mkiv b/tex/context/base/node-pag.mkiv new file mode 100644 index 000000000..487901ad0 --- /dev/null +++ b/tex/context/base/node-pag.mkiv @@ -0,0 +1,20 @@ +%D \module +%D [ file=node-pag, +%D version=2008.09.30, +%D title=\CONTEXT\ Node Macros, +%D subtitle=Page Building, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Node Macros / Page Building} + +\unprotect + +\registerctxluafile{node-pag}{1.001} + +\protect \endinput diff --git a/tex/context/base/node-par.lua b/tex/context/base/node-par.lua new file mode 100644 index 000000000..7be7e7917 --- /dev/null +++ b/tex/context/base/node-par.lua @@ -0,0 +1,101 @@ +if not modules then modules = { } end modules ['node-par'] = { + version = 1.001, + comment = "companion to node-par.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +parbuilders = parbuilders or { } +parbuilders.constructors = parbuilders.constructors or { } +parbuilders.names = parbuilders.names or { } +parbuilders.numbers = parbuilders.numbers or { } +parbuilders.attribute = attributes.numbers['parbuilder'] or 999 + +storage.register("parbuilders.names", parbuilders.names, "parbuilders.names") +storage.register("parbuilders.numbers", parbuilders.numbers, "parbuilders.numbers") + +local constructors, names, numbers, p_attribute = parbuilders.constructors, parbuilders.names, parbuilders.numbers, parbuilders.attribute + +local has_attribute = node.has_attribute +local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming + +local mainconstructor = nil -- not stored in format + +function parbuilders.register(name,number) + parbuilders.names[number] = name + parbuilders.numbers[name] = number +end + +function parbuilders.setmain(name) + mainconstructor = numbers[name] +end + +-- return values: +-- +-- true : tex will break itself +-- false : idem but dangerous +-- head : list of valid vmode nodes with last being hlist + +function parbuilders.constructor(head,followed_by_display) + if type(head) == "boolean" then + return head + else + local attribute = has_attribute(head,p_attribute) or mainconstructor + if attribute then + local constructor = names[attribute] + if constructor then + local handler = constructor and constructors[constructor] + if handler then + return handler(head,followed_by_display) + else + logs.report("parbuilders","handler '%s' is not defined",tostring(constructor)) + return true -- let tex break + end + end + end + return true -- let tex break + end +end + +-- just for testing + +function parbuilders.constructors.default(head,followed_by_display) + return true -- let tex break +end + +-- also for testing (no surrounding spacing done) + +function parbuilders.constructors.oneline(head,followed_by_display) + return node.hpack(head) +end + +-- It makes no sense to have a sequence here as we already have +-- pre and post hooks and only one parbuilder makes sense, so no: +-- +-- local actions = tasks.actions("parbuilders",1) + +-- todo: enable one as main + +local actions = parbuilders.constructor +local enabled = false + +function parbuilders.enable () enabled = true end +function parbuilders.disable() enabled = false end + +local function processor(head,followed_by_display) + if enabled then + starttiming(parbuilders) + local head = actions(head,followed_by_display) + stoptiming(parbuilders) + return head + else + return true -- let tex do the work + end +end + +callbacks.register('linebreak_filter', processor, "breaking paragraps into lines") + +statistics.register("linebreak processing time", function() + return statistics.elapsedseconds(parbuilders) +end) diff --git a/tex/context/base/node-par.mkiv b/tex/context/base/node-par.mkiv new file mode 100644 index 000000000..cb53e643d --- /dev/null +++ b/tex/context/base/node-par.mkiv @@ -0,0 +1,78 @@ +%D \module +%D [ file=node-par, +%D version=2008.09.30, +%D title=\CONTEXT\ Node Macros, +%D subtitle=Paragraph Building, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Node Macros / Paragraph Building} + +%D This is very experimental, undocumented, subjected to changes, etc. just as +%D the underlying interfaces. + +% \enableparbuilders +% +% \startparbuilder[default] +% \input tufte \par +% \startparbuilder[oneline] +% \input tufte \par +% \stopparbuilder +% \input tufte \par +% \stopparbuilder + +\unprotect + +\registerctxluafile{node-par}{1.001} + +\definesystemattribute[parbuilder] \chardef\parbuilderattribute \dogetattributeid{parbuilder} + +% todo auto-enable + +% management (enable/disable) is global and will move to lua + +\newcount\nofparbuilders + +\unexpanded\def\defineparbuilder[#1]% + {\global\advance\nofparbuilders\plusone + \ctxlua{parbuilders.register("#1",\number\nofparbuilders)}% + \setxvalue{\??ng:#1}{\attribute\parbuilderattribute\nofparbuilders}} + +\unexpanded\def\startparbuilder[#1]% + {\edef\@@currentparbuilder{\number\attribute\parbuilderattribute}% + \globalpushmacro\@@currentparbuilder + \getvalue{\??ng:#1}% + \checkparbuilders} + +\unexpanded\def\stopparbuilder + {\ifhmode\par\fi + \globalpopmacro\@@currentparbuilder + \attribute\parbuilderattribute\@@currentparbuilder\relax + \checkparbuilders} + +\def\setmainparbuilder[#1]% + {\ctxlua{parbuilders.setmain("#1")}} + +% no high level interface, after all implementing a linebreaker is not something that +% the average user will do + +\defineparbuilder[default] % just for testing +\defineparbuilder[oneline] % just for testing +\defineparbuilder[basic] % just for testing + +\def\enableparbuilders {\ctxlua{parbuilders.enable ()}} +\def\disableparbuilders{\ctxlua{parbuilders.disable()}} + +\def\checkparbuilders % can be more efficient as we don't want to do this to often + {\ifcase\attribute\parbuilderattribute + \disableparbuilders + \else + \enableparbuilders + \fi} + +\protect \endinput diff --git a/tex/context/base/node-pro.lua b/tex/context/base/node-pro.lua new file mode 100644 index 000000000..4f5b3dcbe --- /dev/null +++ b/tex/context/base/node-pro.lua @@ -0,0 +1,149 @@ +if not modules then modules = { } end modules ['node-pro'] = { + version = 1.001, + comment = "companion to node-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local utf = unicode.utf8 +local format, concat = string.format, table.concat + +local trace_callbacks = false trackers.register("nodes.callbacks", function(v) trace_callbacks = v end) + +local glyph = node.id('glyph') + +local free_node = node.free +local first_character = node.first_character + +nodes.processors = nodes.processors or { } + +-- vbox: grouptype: vbox vtop output split_off split_keep | box_type: exactly|aditional +-- hbox: grouptype: hbox adjusted_hbox(=hbox_in_vmode) | box_type: exactly|aditional + +lists = lists or { } +chars = chars or { } +words = words or { } -- not used yet + +local actions = tasks.actions("processors",4) + +local n = 0 + +local function reconstruct(head) + local t = { } + local h = head + while h do + local id = h.id + if id == glyph then + t[#t+1] = utf.char(h.char) + else + t[#t+1] = "[]" + end + h = h.next + end + return concat(t) +end + +local function tracer(what,state,head,groupcode,before,after,show) + if not groupcode then + groupcode = "unknown" + elseif groupcode == "" then + groupcode = "mvl" + end + n = n + 1 + if show then + logs.report("nodes","%s %s: %s, group: %s, nodes: %s -> %s, string: %s",what,n,state,groupcode,before,after,reconstruct(head)) + else + logs.report("nodes","%s %s: %s, group: %s, nodes: %s -> %s",what,n,state,groupcode,before,after) + end +end + +nodes.processors.enabled = true -- thsi will become a proper state (like trackers) + +function nodes.processors.pre_linebreak_filter(head,groupcode,size,packtype,direction) + local first, found = first_character(head) + if found then + if trace_callbacks then + local before = nodes.count(head,true) + local head, done = actions(head,groupcode,size,packtype,direction) + local after = nodes.count(head,true) + if done then + tracer("pre_linebreak","changed",head,groupcode,before,after,true) + else + tracer("pre_linebreak","unchanged",head,groupcode,before,after,true) + end + return (done and head) or true + else + local head, done = actions(head,groupcode,size,packtype,direction) + return (done and head) or true + end + elseif trace_callbacks then + local n = nodes.count(head,false) + tracer("pre_linebreak","no chars",head,groupcode,n,n) + end + return true +end + +function nodes.processors.hpack_filter(head,groupcode,size,packtype,direction) + local first, found = first_character(head) + if found then + if trace_callbacks then + local before = nodes.count(head,true) + local head, done = actions(head,groupcode,size,packtype,direction) + local after = nodes.count(head,true) + if done then + tracer("hpack","changed",head,groupcode,before,after,true) + else + tracer("hpack","unchanged",head,groupcode,before,after,true) + end + return (done and head) or true + else + local head, done = actions(head,groupcode,size,packtype,direction) + return (done and head) or true + end + elseif trace_callbacks then + local n = nodes.count(head,false) + tracer("hpack","no chars",head,groupcode,n,n) + end + return true +end + +callbacks.register('pre_linebreak_filter', nodes.processors.pre_linebreak_filter,"all kind of horizontal manipulations (before par break)") +callbacks.register('hpack_filter' , nodes.processors.hpack_filter,"all kind of horizontal manipulations") + +local actions = tasks.actions("finalizers",1) -- head, where + +-- beware, these are packaged boxes so no first_character test +-- maybe some day a hash with valid groupcodes +-- +-- beware, much can pass twice, for instance vadjust passes two times + +function nodes.processors.post_linebreak_filter(head,groupcode) +--~ local first, found = first_character(head) +--~ if found then + if trace_callbacks then + local before = nodes.count(head,true) + local head, done = actions(head,groupcode) + local after = nodes.count(head,true) + if done then + tracer("finalizer","changed",head,groupcode,before,after,true) + else + tracer("finalizer","unchanged",head,groupcode,before,after,true) + end + return (done and head) or true + else + local head, done = actions(head,groupcode) + return (done and head) or true + end +--~ elseif trace_callbacks then +--~ local n = nodes.count(head,false) +--~ tracer("finalizer","no chars",head,groupcode,n,n) +--~ end +--~ return true +end + +callbacks.register('post_linebreak_filter', nodes.processors.post_linebreak_filter,"all kind of horizontal manipulations (after par break)") + +statistics.register("h-node processing time", function() + return statistics.elapsedseconds(nodes,"including kernel") -- hm, ok here? +end) diff --git a/tex/context/base/node-ref.lua b/tex/context/base/node-ref.lua new file mode 100644 index 000000000..7128b1a6d --- /dev/null +++ b/tex/context/base/node-ref.lua @@ -0,0 +1,533 @@ +if not modules then modules = { } end modules ['node-bck'] = { + version = 1.001, + comment = "companion to node-bck.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- We supported pdf right from the start and in mkii this has resulted in +-- extensive control over the links. Nowadays pdftex provides a lot more +-- control over margins but as mkii supports multiple backends we stuck to +-- our own mechanisms. In mkiv again we implement our own handling. Eventually +-- we will even disable the pdf primitives. + +-- helper, will end up in luatex + +local cleanupreferences, cleanupdestinations = false, true + +local nodeinjections = backends.nodeinjections +local codeinjections = backends.codeinjections + +local hpack_list = node.hpack +local list_dimensions = node.dimensions + +-- current.glue_set current.glue_sign + +local trace_backend = false trackers.register("nodes.backend", function(v) trace_backend = v end) +local trace_references = false trackers.register("nodes.references", function(v) trace_references = v end) +local trace_destinations = false trackers.register("nodes.destinations", function(v) trace_destinations = v end) + +local hlist = node.id("hlist") +local vlist = node.id("vlist") +local glue = node.id("glue") +local whatsit = node.id("whatsit") + +local new_kern = nodes.kern + +local has_attribute = node.has_attribute +local traverse = node.traverse +local find_node_tail = node.tail or node.slide +local tosequence = nodes.tosequence + +local function dimensions(parent,start,stop) + stop = stop and stop.next + if parent then + if stop then + return list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,start,stop) + else + return list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,start) + end + else + if stop then + return list_dimensions(start,stop) + else + return list_dimensions(start) + end + end +end + +--~ more compact + +local function dimensions(parent,start,stop) + if parent then + return list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,start,stop and stop.next) + else + return list_dimensions(start,stop and stop.next) + end +end + +-- is pardir important at all? + +local function inject_range(head,first,last,reference,make,stack,parent,pardir,txtdir) + local width, height, depth = dimensions(parent,first,last) + if pardir == "TRT" or txtdir == "+TRT" then + width = - width + end + local result, resolved = make(width,height,depth,reference) + if result and resolved then + if head == first then + if trace_backend then + logs.report("backend","head: %04i %s %s %s => w=%s, h=%s, d=%s, c=%s",reference,pardir or "---",txtdir or "----",tosequence(first,last,true),width,height,depth,resolved) + end + result.next = first + first.prev = result + return result, last + else + if trace_backend then + logs.report("backend","middle: %04i %s %s => w=%s, h=%s, d=%s, c=%s",reference,pardir or "---",txtdir or "----",tosequence(first,last,true),width,height,depth,resolved) + end + local prev = first.prev + if prev then + result.next = first + result.prev = prev + prev.next = result + first.prev = result + else + result.next = first + first.prev = result + end + if first == head.next then + head.next = result -- hm, weird + end + return head, last + end + else + return head, last + end +end + +local function inject_list(id,current,reference,make,stack,pardir,txtdir) + local width, height, depth, correction = current.width, current.height, current.depth, 0 + local moveright = false + local first = current.list + if id == hlist then + -- can be either an explicit hbox or a line and there is no way + -- to recognize this; anyway only if ht/dp (then inline) + -- + -- to be tested: 0=unknown, 1=linebreak, 2=hbox +--~ if id.subtype == 1 then + local sr = stack[reference] + if first then + if sr and sr[2] then + local last = find_node_tail(first) + if last.id == glue and last.subtype == 9 then + local prev = last.prev + moveright = first.id == glue and first.subtype == 8 + if prev and prev.id == glue and prev.subtype == 15 then + width = dimensions(current,first,prev.prev) -- maybe not current as we already take care of it + else + if moveright and first.writable then + width = width - first.spec.stretch*current.glue_set * current.glue_sign + end + if last.writable then + width = width - last.spec.stretch*current.glue_set * current.glue_sign + end + end + end + else + -- also weird + end +--~ else +--~ print("!!!!!!!!!!!!!!!!!") + -- simple +--~ end + else + -- ok + end + correction = width + else + correction = height + depth + height, depth = depth, height -- ugly hack, needed because pdftex backend does something funny + end + if pardir == "TRT" then + width = - width + end + local result, resolved = make(width,height,depth,reference) + if result and resolved then + if trace_backend then + logs.report("backend","box: %04i %s %s: w=%s, h=%s, d=%s, c=%s",reference,pardir or "---",txtdir or "----",width,height,depth,resolved) + end + if not first then + current.list = result + elseif moveright then -- brr no prevs done + -- result after first + local n = first.next + result.next = n + first.next = result + result.prev = first + if n then n.prev = result end + else + -- first after result + result.next = first + first.prev = result + current.list = result + end + end +end + +-- skip is somewhat messy + +local function inject_areas(head,attribute,make,stack,done,skip,parent,pardir,txtdir) -- main + if head then + local current, first, last, firstdir, reference = head, nil, nil, nil, nil + pardir = pardir or "===" + txtdir = txtdir or "===" + while current do + local id = current.id + local r = has_attribute(current,attribute) + if id == whatsit then + local subtype = current.subtype + if subtype == 6 then + pardir = current.dir + elseif subtype == 7 then + txtdir = current.dir + end + elseif id == hlist or id == vlist then + if not reference and r and (not skip or r > skip) then + inject_list(id,current,r,make,stack,pardir,txtdir) + end + if r then + done[r] = (done[r] or 0) + 1 + end + local list = current.list + if list then + local _ + current.list, _, pardir, txtdir = inject_areas(list,attribute,make,stack,done,r or skip or 0,current,pardir,txtdir) + end + if r then + done[r] = done[r] - 1 + end + elseif not r then + -- just go on, can be kerns + elseif not reference then + reference, first, last, firstdir = r, current, current, txtdir + elseif r == reference then + last = current + elseif (done[reference] or 0) == 0 then + if not skip or r > skip then + head, current = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir) + reference, first, last, firstdir = nil, nil, nil, nil + end + else + reference, first, last, firstdir = r, current, current, txtdir + end + current = current.next + end + if reference and (done[reference] or 0) == 0 then + head = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir) + end + end + return head, true, pardir, txtdir +end + +local function inject_area(head,attribute,make,stack,done,parent,pardir,txtdir) -- singular ! + if head then + pardir = pardir or "===" + txtdir = txtdir or "===" + local current = head + while current do + local id = current.id + local r = has_attribute(current,attribute) + if id == whatsit then + local subtype = current.subtype + if subtype == 6 then + pardir = current.dir + elseif subtype == 7 then + txtdir = current.dir + end + elseif id == hlist or id == vlist then + if r and not done[r] then + done[r] = true + inject_list(id,current,r,make,stack,pardir,txtdir) + end + current.list = inject_area(current.list,attribute,make,stack,done,current,pardir,txtdir) + elseif r and not done[r] then + done[r] = true + head, current = inject_range(head,current,current,r,make,stack,parent,pardir,txtdir) + end + current = current.next + end + end + return head, true +end + +-- tracing + +local new_rule = nodes.rule +local new_kern = nodes.kern +local set_attribute = node.set_attribute +local register_color = colors.register + +local a_colormodel = attributes.private('colormodel') +local a_color = attributes.private('color') +local a_transparency = attributes.private('transparency') +local u_transparency = nil +local u_colors = { } +local force_gray = true + +local function colorize(width,height,depth,n) + if force_gray then n = 0 end + u_transparency = u_transparency or transparencies.register(nil,2,.65) + local ucolor = u_colors[n] + if not ucolor then + if n == 1 then + u_color = register_color(nil,'rgb',.75,0,0) + elseif n == 2 then + u_color = register_color(nil,'rgb',0,.75,0) + elseif n == 3 then + u_color = register_color(nil,'rgb',0,0,.75) + else + n = 0 + u_color = register_color(nil,'gray',.5) + end + u_colors[n] = u_color + end + local rule = new_rule(width,height,depth) + set_attribute(rule,a_colormodel,1) -- gray color model + set_attribute(rule,a_color,u_color) + set_attribute(rule,a_transparency,u_transparency) + if width < 0 then + local kern = new_kern(width) + rule.width = -width + kern.next = rule + rule.prev = kern + return kern + else + return rule + end +end + +local new_kern = nodes.kern +local texattribute = tex.attribute +local texcount = tex.count + +-- references: + +nodes.references = { + attribute = attributes.private('reference'), + stack = { }, + done = { }, +} + +local stack, done, attribute = nodes.references.stack, nodes.references.done, nodes.references.attribute + +local nofreferences, topofstack = 0, 0 + +local function setreference(n,h,d,r) -- n is just a number, can be used for tracing + topofstack = topofstack + 1 + stack[topofstack] = { n, h, d, codeinjections.prerollreference(r) } -- the preroll permits us to determine samepage (but delayed also has some advantages) +--~ texattribute[attribute] = topofstack -- todo -> at tex end + texcount.lastreferenceattribute = topofstack +end + +nodes.setreference = setreference + +local function makereference(width,height,depth,reference) + local sr = stack[reference] + if sr then + local resolved, ht, dp, set = sr[1], sr[2], sr[3], sr[4] + if ht then + if height < ht then height = ht end + if depth < dp then depth = dp end + end + local annot = nodeinjections.reference(width,height,depth,set) + if annot then + nofreferences = nofreferences + 1 + local result, current + if trace_references then + local step = 65536 + result = hpack_list(colorize(width,height-step,depth-step,2)) -- step subtracted so that we can see seperate links + result.width = 0 + current = result + end + if current then + current.next = annot + else + result = annot + end + result = hpack_list(result,0) + result.width, result.height, result.depth = 0, 0, 0 + if cleanupreferences then stack[reference] = nil end + return result, resolved + else + logs.report("backends","unable to resolve reference annotation %s",reference) + end + else + logs.report("backends","unable to resolve reference attribute %s",reference) + end +end + +function nodes.add_references(head) + if topofstack > 0 then + return inject_areas(head,attribute,makereference,stack,done) + else + return head, false + end +end + +-- destinations (we can clean up once set!) + +nodes.destinations = { + attribute = attributes.private('destination'), + stack = { }, + done = { }, +} + +local stack, done, attribute = nodes.destinations.stack, nodes.destinations.done, nodes.destinations.attribute + +local nofdestinations, topofstack = 0, 0 + +local function setdestination(n,h,d,name,view) -- n = grouplevel, name == table + topofstack = topofstack + 1 + stack[topofstack] = { n, h, d, name, view } + return topofstack +end + +nodes.setdestination = setdestination + +local function makedestination(width,height,depth,reference) + local sr = stack[reference] + if sr then + local resolved, ht, dp, name, view = sr[1], sr[2], sr[3], sr[4], sr[5] + if ht then + if height < ht then height = ht end + if depth < dp then depth = dp end + end + local result, current + if trace_destinations then + local step = 0 + if width == 0 then + step = 4*65536 + width, height, depth = 5*step, 5*step, 0 + end + for n=1,#name do + local rule = hpack_list(colorize(width,height,depth,3)) + rule.width = 0 + if not result then + result, current = rule, rule + else + current.next = rule + rule.prev = current + current = rule + end + width, height = width - step, height - step + end + end + nofdestinations = nofdestinations + 1 + for n=1,#name do + local annot = nodeinjections.destination(width,height,depth,name[n],view) + if not result then + result, current = annot, annot + else + current.next = annot + annot.prev = current + current = annot + end + end + result = hpack_list(result,0) + result.width, result.height, result.depth = 0, 0, 0 + if cleanupdestinations then stack[reference] = nil end + return result, resolved + else + logs.report("backends","unable to resolve destination attribute %s",reference) + end +end + +function nodes.add_destinations(head) + if topofstack > 0 then + return inject_area(head,attribute,makedestination,stack,done) -- singular + else + return head, false + end +end + +-- will move + +function jobreferences.mark(reference,h,d,view) + return setdestination(tex.currentgrouplevel,h,d,reference,view) +end + +function jobreferences.inject(prefix,reference,h,d,highlight,newwindow,layer) -- todo: use currentreference is possible + local set, bug = jobreferences.identify(prefix,reference) + if bug or #set == 0 then + -- unknown ref, just don't set it and issue an error + else + -- check + set.highlight, set.newwindow,set.layer = highlight, newwindow, layer + setreference(tex.currentgrouplevel,h,d,set) -- sets attribute / todo: for set[*].error + end +end + +function jobreferences.injectcurrentset(h,d) -- used inside doifelse + local currentset = jobreferences.currentset + if currentset then + setreference(tex.currentgrouplevel,h,d,currentset) -- sets attribute / todo: for set[*].error + end +end + +-- + +local function checkboth(open,close) + if open and open ~= "" then + local set, bug = jobreferences.identify("",open) + open = not bug and #set > 0 and set + end + if close and close ~= "" then + local set, bug = jobreferences.identify("",close) + close = not bug and #set > 0 and set + end + return open, close +end + +-- expansion is temp hack + +local opendocument, closedocument, openpage, closepage + +local function check(what) + if what and what ~= "" then + local set, bug = jobreferences.identify("",what) + return not bug and #set > 0 and set + end +end + +function jobreferences.checkopendocumentactions (open) opendocument = check(open) end +function jobreferences.checkclosedocumentactions(close) closedocument = check(close) end +function jobreferences.checkopenpageactions (open) openpage = check(open) end +function jobreferences.checkclosepageactions (close) closepage = check(close) end + +function jobreferences.flushdocumentactions() + if opendocument or closedocument then + backends.codeinjections.flushdocumentactions(opendocument,closedocument) -- backend + end +end +function jobreferences.flushpageactions() + if openpage or closepage then + backends.codeinjections.flushpageactions(openpage,closepage) -- backend + end +end + +-- end temp hack + +statistics.register("interactive elements", function() + if nofreferences > 0 or nofdestinations > 0 then + return string.format("%s references, %s destinations",nofreferences,nofdestinations) + else + return nil + end +end) + +function jobreferences.enable_interaction() + tasks.enableaction("shipouts","nodes.add_references") + tasks.enableaction("shipouts","nodes.add_destinations") +end diff --git a/tex/context/base/node-res.lua b/tex/context/base/node-res.lua new file mode 100644 index 000000000..a8ea8745a --- /dev/null +++ b/tex/context/base/node-res.lua @@ -0,0 +1,302 @@ +if not modules then modules = { } end modules ['node-res'] = { + version = 1.001, + comment = "companion to node-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local gmatch, format = string.gmatch, string.format +local copy_node, free_node, free_list, new_node, node_type, node_id = node.copy, node.free, node.flush_list, node.new, node.type, node.id +local tonumber, round = tonumber, math.round + +local glyph_node = node_id("glyph") + +--[[ldx-- +The next function is not that much needed but in
At some point we ran into a problem that the glue specification +of the zeropoint dimension was overwritten when adapting a glue spec +node. This is a side effect of glue specs being shared. After a +couple of hours tracing and debugging Taco and I came to the +conclusion that it made no sense to complicate the spec allocator +and settled on a writable flag. This all is a side effect of the +fact that some glues use reserved memory slots (with the zeropoint +glue being a noticeable one). So, next we wrap this into a function +and hide it for the user. And yes, LuaTeX now gives a warning as +well.
+]]-- + +if tex.luatexversion > 51 then + + function nodes.writable_spec(n) + local spec = n.spec + if not spec then + spec = copy_node(glue_spec) + n.spec = spec + elseif not spec.writable then + spec = copy_node(spec) + n.spec = spec + end + return spec + end + +else + + function nodes.writable_spec(n) + local spec = n.spec + if not spec then + spec = copy_node(glue_spec) + else + spec = copy_node(spec) + end + n.spec = spec + return spec + end + +end + +local cache = { } + +function nodes.usernumber(num) + local n = cache[num] + if n then + return copy_node(n) + else + local n = copy_node(user_n) + if num then n.value = num end + return n + end +end + +function nodes.userlist(list) + local n = copy_node(user_l) + if list then n.value = list end + return n +end + +local cache = { } -- we could use the same cache + +function nodes.userstring(str) + local n = cache[str] + if n then + return copy_node(n) + else + local n = copy_node(user_s) + n.type = 115 + if str then n.value = str end + return n + end +end + +function nodes.usertokens(tokens) + local n = copy_node(user_t) + if tokens then n.value = tokens end + return n +end + +statistics.register("cleaned up reserved nodes", function() + return format("%s nodes, %s lists of %s", nodes.cleanup_reserved(tex.count["lastallocatedbox"])) +end) -- \topofboxstack + +statistics.register("node memory usage", function() -- comes after cleanup ! + return status.node_mem_usage +end) diff --git a/tex/context/base/node-rul.lua b/tex/context/base/node-rul.lua new file mode 100644 index 000000000..9dd89bcda --- /dev/null +++ b/tex/context/base/node-rul.lua @@ -0,0 +1,288 @@ +if not modules then modules = { } end modules ['node-rul'] = { + version = 1.001, + comment = "companion to node-rul.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- this will go to an auxiliary module +-- beware: rules now have a dir field + +local glyph = node.id("glyph") +local disc = node.id("disc") +local rule = node.id("rule") + +function nodes.strip_range(first,last) -- todo: dir + if first and last then -- just to be sure + local current = first + while current and current ~= last do + local id = current.id + if id == glyph or id == disc then + --~ if id == glyph or id == rule or id == disc then + first = current + break + else + current = current.next + end + end + local current = last + while current and current ~= first do + local id = current.id + --~ if id == glyph or id == rule or id == disc then + if id == glyph or id == disc then + last = current + break + else + current = current.prev + end + end + end + return first, last +end + +-- todo: order and maybe other dimensions + +local trace_ruled = false trackers.register("nodes.ruled", function(v) trace_ruled = v end) + +local floor = math.floor +local n_tostring, n_tosequence = nodes.ids_tostring, nodes.tosequence + +local a_ruled = attributes.private('ruled') +local a_color = attributes.private('color') +local a_transparency = attributes.private('transparency') +local a_colorspace = attributes.private('colormodel') + +local glyph = node.id("glyph") +local disc = node.id("disc") +local glue = node.id("glue") +local penalty = node.id("penalty") +local kern = node.id("kern") +local hlist = node.id("hlist") +local vlist = node.id("vlist") +local rule = node.id("rule") +local whatsit = node.id("whatsit") + +local new_rule = nodes.rule +local new_kern = nodes.kern +local new_glue = nodes.glue + +local insert_before, insert_after, strip_range = node.insert_before, node.insert_after, nodes.strip_range +local list_dimensions, has_attribute, set_attribute = node.dimensions, node.has_attribute, node.set_attribute +local hpack_nodes = node.hpack +local dimenfactor = fonts.dimenfactor +local texwrite = tex.write + +local fontdata = fonts.ids +local variables = interfaces.variables + +-- we can use this one elsewhere too +-- +-- todo: functions: word, sentence +-- +-- glyph rule unset whatsit glue margin_kern kern math disc + +local checkdir = true + +-- we assume {glyphruns} and no funny extra kerning, ok, maybe we need +-- a dummy character as start and end; anyway we only collect glyphs + +local function process_words(attribute,data,flush,head,parent) -- we have hlistdir and local dir + local n = head + if n then + local f, l, a, d, i, level + local continue, done, strip = false, false, false + while n do + local id = n.id + if id == glyph or id == rule then + local aa = has_attribute(n,attribute) + if aa then + if aa == a then + if not f then -- ? + f = n + end + l = n + else + -- possible extensions: when in same class then keep spanning + if f then + head, done = flush(head,f,l,d,level,parent,strip), true + end + f, l, a = n, n, aa + level, i = floor(a/1000), a%1000 + d = data[i] + continue = d.continue == variables.yes + end + else + if f then + head, done = flush(head,f,l,d,level,parent,strip), true + end + f, l, a = nil, nil, nil + end + elseif f and (id == disc or (id == kern and n.subtype == 0)) then + l = n + elseif id == hlist or id == vlist then + if f then + head, done = flush(head,f,l,d,level,parent,strip), true + f, l, a = nil, nil, nil + end + local list = n.list + if list then + n.list = process_words(attribute,data,flush,list,n) + end + elseif checkdir and id == whatsit and n.subtype == 7 then -- only changes in dir, we assume proper boundaries + if f and a then + l = n + end + elseif f then + if continue then + if id == penalty or id == kern then + l = n + elseif id == glue then + l = n + end + else + head, done = flush(head,f,l,d,level,parent,strip), true + f, l, a = nil, nil, nil + end + end + n = n.next + end + if f then + head, done = flush(head,f,l,d,level,parent,strip), true + end + return head, true -- todo: done + else + return head, false + end +end + +nodes.process_words = process_words + +-- + +nodes.rules = nodes.rules or { } +nodes.rules.data = nodes.rules.data or { } + +storage.register("nodes/rules/data", nodes.rules.data, "nodes.rules.data") + +local data = nodes.rules.data + +function nodes.rules.define(settings) + data[#data+1] = settings + texwrite(#data) +end + +local function flush_ruled(head,f,l,d,level,parent,strip) -- not that fast but acceptable for this purpose +-- check for f and l + local r, m + if true then + f, l = strip_range(f,l) + end + local w = list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,f,l.next) + local method, offset, continue, dy, rulethickness, unit, order, max, ma, ca, ta = + d.method, d.offset, d.continue, d.dy, d.rulethickness, d.unit, d.order, d.max, d.ma, d.ca, d.ta + local e = dimenfactor(unit,fontdata[f.font]) + local colorspace = (ma > 0 and ma) or has_attribute(f,a_colorspace) or 1 + local color = (ca > 0 and ca) or has_attribute(f,a_color) + local transparency = (ta > 0 and ta) or has_attribute(f,a_transparency) + local foreground = order == variables.foreground + rulethickness= rulethickness/2 + if level > max then + level = max + end + if method == 0 then -- center + offset = 2*offset + m = (offset+(level-1)*dy+rulethickness)*e/2 + else + m = 0 + end + for i=1,level do + local ht = (offset+(i-1)*dy+rulethickness)*e - m + local dp = -(offset+(i-1)*dy-rulethickness)*e + m + local r = new_rule(w,ht,dp) + if color then + set_attribute(r,a_colorspace,colorspace) + set_attribute(r,a_color,color) + end + if transparency then + set_attribute(r,a_transparency,transparency) + end + local k = new_kern(-w) + if foreground then + insert_after(head,l,k) + insert_after(head,k,r) + l = r + else + head, _ = insert_before(head,f,r) + insert_after(head,r,k) + end + if trace_ruled then + logs.report("ruled", "level: %s, width: %i, height: %i, depth: %i, nodes: %s, text: %s", + level,w,ht,dp,n_tostring(f,l),n_tosequence(f,l,true)) + -- level,r.width,r.height,r.depth,n_tostring(f,l),n_tosequence(f,l,true)) + end + end + return head +end + +local process = nodes.process_words + +nodes.rules.process = function(head) return process(a_ruled,data,flush_ruled,head) end + +function nodes.rules.enable() + tasks.enableaction("shipouts","nodes.rules.process") +end + +-- elsewhere: +-- +-- tasks.appendaction ("shipouts", "normalizers", "nodes.rules.process") +-- tasks.disableaction("shipouts", "nodes.rules.process") -- only kick in when used + +local trace_shifted = false trackers.register("nodes.shifted", function(v) trace_shifted = v end) + +local a_shifted = attributes.private('shifted') + +nodes.shifts = nodes.shifts or { } +nodes.shifts.data = nodes.shifts.data or { } + +storage.register("nodes/shifts/data", nodes.shifts.data, "nodes.shifts.data") + +local data = nodes.shifts.data + +function nodes.shifts.define(settings) + data[#data+1] = settings + texwrite(#data) +end + +local function flush_shifted(head,first,last,data,level,parent,strip) -- not that fast but acceptable for this purpose + if true then + first, last = strip_range(first,last) + end + local prev, next = first.prev, last.next + first.prev, last.next = nil, nil + local width, height, depth = list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,first,next) + local list = hpack_nodes(first,width,"exactly") + if first == head then + head = list + end + if prev then + prev.next, list.prev = list, prev + end + if next then + next.prev, list.next = list, next + end + local raise = data.dy * dimenfactor(data.unit,fontdata[first.font]) + list.shift, list.height, list.depth = raise, height, depth + if trace_shifted then + logs.report("shifted", "width: %s, nodes: %s, text: %s",width,n_tostring(first,last),n_tosequence(first,last,true)) + end + return head +end + +local process = nodes.process_words + +nodes.shifts.process = function(head) return process(a_shifted,data,flush_shifted,head) end + +function nodes.shifts.enable() + tasks.enableaction("shipouts","nodes.shifts.process") +end diff --git a/tex/context/base/node-rul.mkiv b/tex/context/base/node-rul.mkiv new file mode 100644 index 000000000..1270eb81d --- /dev/null +++ b/tex/context/base/node-rul.mkiv @@ -0,0 +1,343 @@ +%D \module +%D [ file=node-rul, +%D version=2009.11.03, % 1995.10.10, +%D title=\CONTEXT\ Core Macros, +%D subtitle=Bars, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +% todo: ex and and em traveling with attribute + +\writestatus{loading}{ConTeXt Core Macros / Bars} + +%D The name of this file might change. + +%D \macros +%D {underbar,underbars, +%D overbar,overbars, +%D overstrike,overstrikes, +%D setupbar} +%D +%D In the rare case that we need undelined words, for instance +%D because all font alternatives are already in use, one can +%D use \type{\underbar} and \type{\overstrike} and their plural +%D forms. +%D +%D \startbuffer +%D \underbars{drawing \underbar{bars} under words is a typewriter leftover} +%D \overstrikes{striking words makes them \overstrike{unreadable} but +%D sometimes even \overbar{top lines} come into view.} +%D \stopbuffer +%D +%D \typebuffer +%D +%D \startvoorbeeld +%D \startlines +%D \getbuffer +%D \stoplines +%D \stopvoorbeeld +%D +%D The next macros are derived from the \PLAIN\ \TEX\ one, but +%D also supports nesting. The \type{$} keeps us in horizontal +%D mode and at the same time applies grouping. +%D +%D \showsetup{underbar} +%D \showsetup{underbars} +%D \showsetup{overbar} +%D \showsetup{overbars} +%D \showsetup{overstrike} +%D \showsetup{overstrikes} +%D +%D \showsetup{setupunderbar} + +\unprotect + +%definesystemattribute[ruled] +%definesystemattribute[shifted] + +\registerctxluafile{node-rul}{1.001} + +\newtoks\checkalldefinedbars + +\def\barparameter #1{\csname\dobarparameter\currentbar#1\endcsname} +\def\dobarparameter #1#2{\ifcsname\??on#1#2\endcsname\??on#1#2\else\expandafter\dobarparentparameter\csname\??on#1\s!parent\endcsname#2\fi} +\def\dobarparentparameter#1#2{\ifx#1\relax\s!empty\else\dobarparameter#1#2\fi} + +\unexpanded\def\definebar + {\dotripleempty\dodefinebar} + +\def\dodefinebar[#1][#2][#3]% + {\ifthirdargument + \getparameters[\??on#1][\s!parent=#2,#3]% + \else + \getparameters[\??on#1][\s!parent=,#2]% + \fi + % + %\setvalue{\??on:#1}{0}% + % + \ifcsname\??on:#1:c\endcsname + \csname\??on:#1:c\endcsname\zerocount + \else + \expandafter\newcount\csname\??on:#1:c\endcsname + \fi + \normalexpanded{\checkalldefinedbars{\noexpand\doredefinebar{#1}\the\checkalldefinedbars}}% + \dodefinebarindeed{#1}% + \setuvalue{#1}{\doruled{#1}}} + +\def\dodefinebarindeed#1% + {\bgroup + \def\currentbar{#1}% + \doifsomethingelse{\barparameter\c!color} + {\donetrue\colored[\barparameter\c!color]} + {\donefalse}% + \normalexpanded + {\egroup + \scratchcounter\ctxlua{nodes.rules.define { + method = \barparameter\c!method, + offset = \barparameter\c!offset, + continue = "\barparameter\c!continue", + dy = \barparameter\c!dy, + rulethickness = \barparameter\c!rulethickness, + unit = "\barparameter\c!unit", + order = "\barparameter\c!order", + max = \barparameter\c!max, + ma = \ifdone\the\attribute\colormodelattribute \else0\fi, + ca = \ifdone\the\attribute\colorattribute \else0\fi, + ta = \ifdone\the\attribute\transparencyattribute\else0\fi + }}}% + \setevalue{\??on#1:a}{\the\scratchcounter}} + +\let\doredefinebar\dodefinebarindeed + +\def\doruled#1% + {\groupedcommand{\dodoruled{#1}}\relax} + +\def\dodoruled + {\ctxlua{nodes.rules.enable()}% + \glet\dodoruled\dodoruledindeed + \dodoruled} + +\def\dodoruledindeed#1% + {\advance\csname\??on:#1:c\endcsname\plusone + \scratchcounter\csname\??on:#1:c\endcsname + \attribute\ruledattribute\numexpr1000*\scratchcounter + +\csname\??on#1\ifcsname\??on#1:\number\scratchcounter\s!parent\endcsname:\number\scratchcounter\fi:a\endcsname} + +% ungrouped + +\unexpanded\def\startbar[#1]% + {\begingroup + \dodoruled{#1}} + +\unexpanded\def\stopbar + {\endgroup} + +\newcount\currentbarnesting % todo: same as colors + +\unexpanded\def\pushbar[#1]% + {\global\advance\currentbarnesting\plusone + \expandafter\edef\csname\??on:s:\number\currentbarnesting\endcsname + {\attribute\ruledattribute\the\attribute\ruledattribute}% stack + \dodoruled{#1}} + +\unexpanded\def\popbar + {\csname\??on:s:\number\currentbarnesting\endcsname + \global\advance\currentbarnesting\minusone} + +\unexpanded\def\setupbars + {\dodoubleempty\dosetupbars} + +\def\dosetupbars[#1][#2]% not that efficient + {\ifsecondargument + \getparameters[\??on#1][#2]% + \dodefinebarindeed{#1}% + \else + \getparameters[\??on][#1]% + \the\checkalldefinedbars + \fi} + +\setupbars + [\c!method=0, % new: 0=center nested, 1=stack nested + \c!continue=\v!no, + \c!offset=0, % upwards, replaces: topoffset bottomoffset + \c!dy=0, + \c!max=3, + \c!rulethickness=.1, + \c!order=\v!foreground, + \c!unit=ex, % so now we are relative + \c!color=] % replaces: rulecolor + +% \definebar[touchbar] [\c!method=0,\c!dy=-0.4,\c!offset=-0.0] +% \definebar[touchbars] [touchbar] [\c!continue=\v!yes] + +\definebar[\v!overstrike] [\c!method=0,\c!dy= 0.4,\c!offset= 0.5,\c!continue=\v!yes] +\definebar[\v!underbar] [\c!method=1,\c!dy=-0.4,\c!offset=-0.3,\c!continue=\v!yes] +\definebar[\v!overbar] [\c!method=1,\c!dy= 0.4,\c!offset= 1.8,\c!continue=\v!yes] + +\definebar[\v!overstrikes] [\v!overstrike] [\c!continue=\v!no] +\definebar[\v!underbars] [\v!underbar] [\c!continue=\v!no] +\definebar[\v!overbars] [\v!overbar] [\c!continue=\v!no] + +% we want these always so ... + +\expandafter\let\expandafter\overstrike \csname\v!overstrike \endcsname +\expandafter\let\expandafter\underbar \csname\v!underbar \endcsname +\expandafter\let\expandafter\overbar \csname\v!overbar \endcsname +\expandafter\let\expandafter\overstrikes\csname\v!overstrikes\endcsname +\expandafter\let\expandafter\underbars \csname\v!underbars \endcsname +\expandafter\let\expandafter\overbars \csname\v!overbars \endcsname + +\unexpanded\def\setupunderbar[#1]% too incompatible for the moment + {} + +%D This will move: (a bit duplicated) + +\newtoks\checkalldefinedshifts + +\def\shiftparameter #1{\csname\doshiftparameter\currentshift#1\endcsname} +\def\shiftparameterhash#1{\doshiftparameterhash{\??ra\currentshift}#1} + +\def\doshiftparameter #1#2{\ifcsname\??ra#1#2\endcsname\??ra#1#2\else\expandafter\doshiftparentparameter\csname\??ra#1\s!parent\endcsname#2\fi} +\def\doshiftparameterhash#1#2{\ifcsname#1#2\endcsname#1\else\expandafter\doshiftparentparameterhash\csname#1\s!parent\endcsname#2\fi} + +\def\doshiftparentparameter #1#2{\ifx#1\relax\s!empty\else\doshiftparameter #1#2\fi} +\def\doshiftparentparameterhash#1#2{\ifx#1\relax \else\doshiftparameterhash#1#2\fi} + +\def\dosetshiftattributes#1#2% style color + {\edef\fontattributehash {\shiftparameterhash#1}% + \edef\colorattributehash{\shiftparameterhash#2}% + \ifx\fontattributehash \empty\else\dosetfontattribute \fontattributehash #1\fi + \ifx\colorattributehash\empty\else\dosetcolorattribute\colorattributehash#2\fi} + +\unexpanded\def\defineshift + {\dotripleempty\dodefineshift} + +\def\dodefineshift[#1][#2][#3]% + {\ifthirdargument + \getparameters[\??ra#1][\s!parent=#2,#3]% + \else + \getparameters[\??ra#1][\s!parent=,#2]% + \fi + % + %\setvalue{\??ra:#1}{0}% + % + \ifcsname\??ra:#1:c\endcsname + \csname\??ra:#1:c\endcsname\zerocount + \else + \expandafter\newcount\csname\??ra:#1:c\endcsname + \fi + \normalexpanded{\checkalldefinedshifts{\noexpand\doredefineshift{#1}\the\checkalldefinedshifts}}% + \dodefineshiftindeed{#1}% + \setuvalue{#1}{\doshifted{#1}}} + +\def\dodefineshiftindeed#1% + {\bgroup + \def\currentshift{#1}% + \normalexpanded + {\egroup + \scratchcounter\ctxlua{nodes.shifts.define { + method = \shiftparameter\c!method, + continue = "\shiftparameter\c!continue", + dy = \shiftparameter\c!dy, + unit = "\shiftparameter\c!unit", + }}}% + \setevalue{\??ra#1:a}{\the\scratchcounter}} + +\let\doredefineshift\dodefineshiftindeed + +\def\doshifted#1% + {\groupedcommand{\dodoshifted{#1}}\relax} + +\def\dodoshifted + {\ctxlua{nodes.shifts.enable()}% + \glet\dodoshifted\dodoshiftedindeed + \dodoshifted} + +% \def\dodoshiftedindeed#1% +% {\def\currentshift{#1}% +% \advance\csname\??ra:#1:c\endcsname\plusone +% \scratchcounter\csname\??ra:#1:c\endcsname +% \attribute\shiftedattribute\numexpr1000*\scratchcounter +% +\csname\??ra#1\ifcsname\??ra#1:\number\scratchcounter\s!parent\endcsname:\number\scratchcounter\fi:a\endcsname +% \setupalign[\shiftparameter\c!align]% +% \dosetshiftattributes\c!style\c!color} + +\def\dostartisolation{\char0 } +\def\dostopisolation {\char0 } +\def\doisolator {\char0 } + +\def\doisolatedgroupedalign#1#2% + {\groupedcommand + {\begingroup\dostartisolation\begingroup#1} + {#2\endgroup\dostopisolation\endgroup}} + +\def\dosetupisolatedalign#1% + {\doisolator + \setupalign[#1]\relax} + +\def\dodoshiftedindeed#1% + {\def\currentshift{#1}% + \advance\csname\??ra:#1:c\endcsname\plusone + \scratchcounter\csname\??ra:#1:c\endcsname + \attribute\shiftedattribute\numexpr1000*\scratchcounter + +\csname\??ra#1\ifcsname\??ra#1:\number\scratchcounter\s!parent\endcsname:\number\scratchcounter\fi:a\endcsname + \dosetshiftattributes\c!style\c!color + \dosetupisolatedalign{\shiftparameter\c!align}} + +\def\doshifted#1% + {\doisolatedgroupedalign{\dodoshifted{#1}}{}} + +\unexpanded\def\startshift[#1]% + {\begingroup + \dodoshifted{#1}} + +\unexpanded\def\stopshift + {\endgroup} + +\unexpanded\def\setupshifts + {\dodoubleempty\dosetupshifts} + +\def\dosetupshifts[#1][#2]% not that efficient + {\ifsecondargument + \getparameters[\??ra#1][#2]% + \dodefineshiftindeed{#1}% + \else + \getparameters[\??ra][#1]% + \the\checkalldefinedshifts + \fi} + +\setupshifts + [\c!method=0, + \c!continue=\v!no, + \c!dy=0, + \c!unit=ex, + \c!align=, + \c!style=, + \c!color=] + +\def\v!shiftup {shiftup} +\def\v!shiftdown{shiftdown} + +\defineshift [\v!shiftup] [\c!method=0,\c!dy=-1,\c!unit=ex,\c!continue=\v!yes,\c!style=\txx,\c!color=] +\defineshift [\v!shiftdown] [\c!method=1,\c!dy=.3,\c!unit=ex,\c!continue=\v!yes,\c!style=\txx,\c!color=] + +% we want these always so ... + +\expandafter\let\expandafter\shiftup \csname\v!shiftup \endcsname +\expandafter\let\expandafter\shiftdown \csname\v!shiftdown \endcsname + +\protect \endinput + +% obsolete: + +\setupunderbar + [\c!alternative=a, + \c!rulethickness=\linewidth, + \c!bottomoffset=1.5pt, + \c!topoffset=2.5pt, + \c!rulecolor=] diff --git a/tex/context/base/node-seq.lua b/tex/context/base/node-seq.lua new file mode 100644 index 000000000..3a2cf5b6e --- /dev/null +++ b/tex/context/base/node-seq.lua @@ -0,0 +1,186 @@ +if not modules then modules = { } end modules ['node-seq'] = { + version = 1.001, + comment = "companion to node-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +--[[ldx-- +Here we implement a mechanism for chaining the special functions
+that we use in
This is rather experimental. We need more control and some of this +might become a runtime module instead. This module will be cleaned up!
+--ldx]]-- + +local utf = unicode.utf8 +local format, match, concat, rep, utfchar = string.format, string.match, table.concat, string.rep, utf.char + +local ctxcatcodes = tex.ctxcatcodes + +fonts = fonts or { } +fonts.tfm = fonts.tfm or { } +fonts.ids = fonts.ids or { } + +nodes = nodes or { } +nodes.tracers = nodes.tracers or { } +nodes.tracers.characters = nodes.tracers.characters or { } +nodes.tracers.steppers = nodes.tracers.steppers or { } + +local glyph = node.id('glyph') +local hlist = node.id('hlist') +local vlist = node.id('vlist') +local disc = node.id('disc') +local glue = node.id('glue') +local kern = node.id('kern') +local rule = node.id('rule') +local whatsit = node.id('whatsit') + +local copy_node_list = node.copy_list +local hpack_node_list = node.hpack +local free_node_list = node.flush_list +local first_character = node.first_character +local node_type = node.type +local traverse_nodes = node.traverse + +local texsprint = tex.sprint +local fontdata = fonts.ids + +function nodes.tracers.characters.collect(head,list,tag,n) + n = n or 0 + local ok, fn = false, nil + while head do + local id = head.id + if id == glyph then + local f = head.font + if f ~= fn then + ok, fn = false, f + end + local c = head.char + local i = fontdata[f].indices[c] or 0 + if not ok then + ok = true + n = n + 1 + list[n] = list[n] or { } + list[n][tag] = { } + end + local l = list[n][tag] + l[#l+1] = { c, f, i } + elseif id == disc then + -- skip + else + ok = false + end + head = head.next + end +end + +function nodes.tracers.characters.equal(ta, tb) + if #ta ~= #tb then + return false + else + for i=1,#ta do + local a, b = ta[i], tb[i] + if a[1] ~= b[1] or a[2] ~= b[2] or a[3] ~= b[3] then + return false + end + end + end + return true +end + +function nodes.tracers.characters.string(t) + local tt = { } + for i=1,#t do + tt[i] = utfchar(t[i][1]) + end + return concat(tt,"") +end + +function nodes.tracers.characters.unicodes(t,decimal) + local tt = { } + for i=1,#t do + local n = t[i][1] + if n == 0 then + tt[i] = "-" + elseif decimal then + tt[i] = n + else + tt[i] = format("U+%04X",n) + end + end + return concat(tt," ") +end + +function nodes.tracers.characters.indices(t,decimal) + local tt = { } + for i=1,#t do + local n = t[i][3] + if n == 0 then + tt[i] = "-" + elseif decimal then + tt[i] = n + else + tt[i] = format("U+%04X",n) + end + end + return concat(tt," ") +end + +function nodes.tracers.characters.start() + local npc = nodes.process_characters + local list = { } + function nodes.process_characters(head) + local n = #list + nodes.tracers.characters.collect(head,list,'before',n) + local h, d = npc(head) + nodes.tracers.characters.collect(head,list,'after',n) + if #list > n then + list[#list+1] = { } + end + return h, d + end + function nodes.tracers.characters.stop() + tracers.list['characters'] = list + local variables = { + ['title'] = 'ConTeXt Character Processing Information', + ['color-background-one'] = lmx.get('color-background-yellow'), + ['color-background-two'] = lmx.get('color-background-purple'), + } + lmx.show('context-characters.lmx',variables) + nodes.process_characters = npc + tasks.restart("processors", "characters") + end + tasks.restart("processors", "characters") +end + +local stack = { } + +function nodes.tracers.start(tag) + stack[#stack+1] = tag + local tracer = nodes.tracers[tag] + if tracer and tracer.start then + tracer.start() + end +end +function nodes.tracers.stop() + local tracer = stack[#stack] + if tracer and tracer.stop then + tracer.stop() + end + stack[#stack] = nil +end + +-- experimental + +local collection, collecting, messages = { }, false, { } + +function nodes.tracers.steppers.start() + collecting = true +end + +function nodes.tracers.steppers.stop() + collecting = false +end + +function nodes.tracers.steppers.reset() + for i=1,#collection do + local c = collection[i] + if c then + free_node_list(c) + end + end + collection, messages = { }, { } +end + +function nodes.tracers.steppers.nofsteps() + return tex.write(#collection) +end + +function nodes.tracers.steppers.glyphs(n,i) + local c = collection[i] + if c then + tex.box[n] = hpack_node_list(copy_node_list(c)) + end +end + +function nodes.tracers.steppers.features() +-- local f = first_character(collection[1]) +-- if f then -- something fishy with first_character + local f = collection[1] + while f do + if f.id == glyph then + local tfmdata, t = fontdata[f.font], { } + for feature, value in table.sortedhash(tfmdata.shared.features) do + if feature == "number" or feature == "features" then + -- private + elseif type(value) == "boolean" then + if value then + t[#t+1] = format("%s=yes",feature) + else + -- skip + end + else + t[#t+1] = format("%s=%s",feature,value) + end + end + if #t > 0 then + texsprint(ctxcatcodes,concat(t,", ")) + else + texsprint(ctxcatcodes,"no features") + end + return + end + f = f.next + end +end + +function nodes.tracers.fontchar(font,char) + local n = nodes.glyph() + n.font, n.char, n.subtype = font, char, 256 + node.write(n) +end + +function nodes.tracers.steppers.codes(i,command) + local c = collection[i] + while c do + local id = c.id + if id == glyph then + if command then + texsprint(ctxcatcodes,format("%s{%s}{%s}",command,c.font,c.char)) + else + texsprint(ctxcatcodes,format("[%s:U+%04X]",c.font,c.char)) + end + elseif id == whatsit and (c.subtype == 6 or c.subtype == 7) then + texsprint(ctxcatcodes,format("[%s]",c.dir)) + else + texsprint(ctxcatcodes,format("[%s]",node_type(id))) + end + c = c.next + end +end + +function nodes.tracers.steppers.messages(i,command,split) + local list = messages[i] -- or { "no messages" } + if list then + for i=1,#list do + local l = list[i] + if split then + local a, b = match(l,"^(.-)%s*:%s*(.*)$") + texsprint(ctxcatcodes,format("%s{%s}{%s}",command,a or l,b or "")) + else + texsprint(ctxcatcodes,format("%s{%s}",command,l)) + end + end + end +end + +-- hooks into the node list processor (see otf) + +function nodes.tracers.steppers.check(head) + if collecting then + nodes.tracers.steppers.reset() + local n = copy_node_list(head) + nodes.inject_kerns(n,nil,"trace",true) + nodes.protect_glyphs(n) -- can be option + collection[1] = n + end +end + +function nodes.tracers.steppers.register(head) + if collecting then + local nc = #collection+1 + if messages[nc] then + local n = copy_node_list(head) + nodes.inject_kerns(n,nil,"trace",true) + nodes.protect_glyphs(n) -- can be option + collection[nc] = n + end + end +end + +function nodes.tracers.steppers.message(str,...) + str = format(str,...) + if collecting then + local n = #collection + 1 + local m = messages[n] + if not m then m = { } messages[n] = m end + m[#m+1] = str + end + return str -- saves an intermediate var in the caller +end + +-- this will be reorganized: + +function nodes.show_list(head, message) + if message then + texio.write_nl(message) + end + for n in traverse_nodes(head) do + texio.write_nl(tostring(n)) + end +end + +function nodes.check_glyphs(head,message) + local t = { } + for g in traverse_id(glyph,head) do + t[#t+1] = format("U+%04X:%s",g.char,g.subtype) + end + if #t > 0 then + logs.report(message or "nodes","%s glyphs: %s",#t,concat(t," ")) + end + return false +end + +function nodes.tosequence(start,stop,compact) + if start then + local t = { } + while start do + local id = start.id + if id == glyph then + local c = start.char + if compact then + if start.components then + t[#t+1] = nodes.tosequence(start.components,nil,compact) + else + t[#t+1] = format("%s",utfchar(c)) + end + else + t[#t+1] = format("U+%04X:%s",c,utfchar(c)) + end + elseif id == whatsit and start.subtype == 6 or start.subtype == 7 then + t[#t+1] = "[" .. start.dir .. "]" + elseif id == rule then + if compact then + t[#t+1] = "|" + else + t[#t+1] = node_type(id) + end + else + if compact then + t[#t+1] = "[]" + else + t[#t+1] = node_type(id) + end + end + if start == stop then + break + else + start = start.next + end + end + if compact then + return concat(t) + else + return concat(t," ") + end + else + return "[empty]" + end +end + +function nodes.report(t,done) + if done then + if status.output_active then + logs.report("nodes","output, changed, %s nodes",nodes.count(t)) + else + texio.write("nodes","normal, changed, %s nodes",nodes.count(t)) + end + else + if status.output_active then + logs.report("nodes","output, unchanged, %s nodes",nodes.count(t)) + else + texio.write("nodes","normal, unchanged, %s nodes",nodes.count(t)) + end + end +end + +function nodes.pack_list(head) + local t = { } + for n in traverse(head) do + t[#t+1] = tostring(n) + end + return t +end + +function nodes.ids_to_string(head,tail) + local t, last_id, last_n = { }, nil, 0 + for n in traverse_nodes(head,tail) do -- hm, does not stop at tail + local id = n.id + if not last_id then + last_id, last_n = id, 1 + elseif last_id == id then + last_n = last_n + 1 + else + if last_n > 1 then + t[#t+1] = format("[%s*%s]",last_n,node_type(last_id) or "?") + else + t[#t+1] = format("[%s]",node_type(last_id) or "?") + end + last_id, last_n = id, 1 + end + if n == tail then + break + end + end + if not last_id then + t[#t+1] = "no nodes" + elseif last_n > 1 then + t[#t+1] = format("[%s*%s]",last_n,node_type(last_id) or "?") + else + t[#t+1] = format("[%s]",node_type(last_id) or "?") + end + return concat(t," ") +end + +nodes.ids_tostring = nodes.ids_to_string + +local function show_simple_list(h,depth,n) + while h do + texio.write_nl(rep(" ",n) .. tostring(h)) + if not depth or n < depth then + local id = h.id + if id == hlist or id == vlist then + show_simple_list(h.list,depth,n+1) + end + end + h = h.next + end +end + +--~ \startluacode +--~ callback.register('buildpage_filter',function() nodes.show_simple_list(tex.lists.contrib_head) end) +--~ \stopluacode +--~ \vbox{b\footnote{n}a} +--~ \startluacode +--~ callback.register('buildpage_filter',nil) +--~ \stopluacode + +nodes.show_simple_list = function(h,depth) show_simple_list(h,depth,0) end + +function nodes.list_to_utf(h,joiner) + local joiner = (joiner ==true and utfchar(0x200C)) or joiner -- zwnj + local w = { } + while h do + if h.id == glyph then -- always true + w[#w+1] = utfchar(h.char) + if joiner then + w[#w+1] = joiner + end + else + w[#w+1] = "[-]" + end + h = h.next + end + return concat(w) +end diff --git a/tex/context/base/node-tsk.lua b/tex/context/base/node-tsk.lua new file mode 100644 index 000000000..206b4a266 --- /dev/null +++ b/tex/context/base/node-tsk.lua @@ -0,0 +1,315 @@ +if not modules then modules = { } end modules ['node-tsk'] = { + version = 1.001, + comment = "companion to node-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- this might move to task-* + +local trace_tasks = false trackers.register("tasks.creation", function(v) trace_tasks = v end) + +tasks = tasks or { } +tasks.data = tasks.data or { } + +function tasks.new(name,list) + local tasklist = sequencer.reset() + tasks.data[name] = { list = tasklist, runner = false } + for l=1,#list do + sequencer.appendgroup(tasklist,list[l]) + end +end + +function tasks.restart(name) + local data = tasks.data[name] + if data then + data.runner = false + end +end + +function tasks.enableaction(name,action) + local data = tasks.data[name] + if data then + sequencer.enableaction(data.list,action) + data.runner = false + end +end + +function tasks.disableaction(name,action) + local data = tasks.data[name] + if data then + sequencer.disableaction(data.list,action) + data.runner = false + end +end + +function tasks.enablegroup(name,group) + local data = tasks.data[name] + if data then + sequencer.enablegroup(data.list,group) + data.runner = false + end +end + +function tasks.disablegroup(name,group) + local data = tasks.data[name] + if data then + sequencer.disablegroup(data.list,group) + data.runner = false + end +end + +function tasks.appendaction(name,group,action,where,kind) + local data = tasks.data[name] + if data then + sequencer.appendaction(data.list,group,action,where,kind) + data.runner = false + end +end + +function tasks.prependaction(name,group,action,where,kind) + local data = tasks.data[name] + if data then + sequencer.prependaction(data.list,group,action,where,kind) + data.runner = false + end +end + +function tasks.removeaction(name,group,action) + local data = tasks.data[name] + if data then + sequencer.removeaction(data.list,group,action) + data.runner = false + end +end + +function tasks.showactions(name,group,action,where,kind) + local data = tasks.data[name] + if data then + logs.report("nodes","task %s, list:\n%s",name,sequencer.nodeprocessor(data.list)) + end +end + +-- Optimizing for the number of arguments makes sense, but getting rid of +-- the nested call (no problem but then we also need to register the +-- callback with this mechanism so that it gets updated) does not save +-- much time (24K calls on mk.tex). + +local created, total = 0, 0 + +statistics.register("node list callback tasks", function() + if total > 0 then + return string.format("%s unique task lists, %s instances (re)created, %s calls",table.count(tasks.data),created,total) + else + return nil + end +end) + +local compile, nodeprocessor = sequencer.compile, sequencer.nodeprocessor + +function tasks.actions(name,n) -- we optimize for the number or arguments (no ...) + local data = tasks.data[name] + if data then + if n == 0 then + return function(head) + local runner = data.runner + total = total + 1 -- will go away + if not runner then + created = created + 1 + if trace_tasks then + logs.report("nodes","creating task runner '%s'",name) + end + runner = compile(data.list,nodeprocessor,0) + data.runner = runner + end + return runner(head) + end + elseif n == 1 then + return function(head,one) + total = total + 1 -- will go away + local runner = data.runner + if not runner then + created = created + 1 + if trace_tasks then + logs.report("nodes","creating task runner '%s' with 1 extra arguments",name) + end + runner = compile(data.list,nodeprocessor,1) + data.runner = runner + end + return runner(head,one) + end + elseif n == 2 then + return function(head,one,two) + total = total + 1 -- will go away + local runner = data.runner + if not runner then + created = created + 1 + if trace_tasks then + logs.report("nodes","creating task runner '%s' with 2 extra arguments",name) + end + runner = compile(data.list,nodeprocessor,2) + data.runner = runner + end + return runner(head,one,two) + end + elseif n == 3 then + return function(head,one,two,three) + total = total + 1 -- will go away + local runner = data.runner + if not runner then + created = created + 1 + if trace_tasks then + logs.report("nodes","creating task runner '%s' with 3 extra arguments",name) + end + runner = compile(data.list,nodeprocessor,3) + data.runner = runner + end + return runner(head,one,two,three) + end + elseif n == 4 then + return function(head,one,two,three,four) + total = total + 1 -- will go away + local runner = data.runner + if not runner then + created = created + 1 + if trace_tasks then + logs.report("nodes","creating task runner '%s' with 4 extra arguments",name) + end + runner = compile(data.list,nodeprocessor,4) + data.runner = runner + end + return runner(head,one,two,three,four) + end + elseif n == 5 then + return function(head,one,two,three,four,five) + total = total + 1 -- will go away + local runner = data.runner + if not runner then + created = created + 1 + if trace_tasks then + logs.report("nodes","creating task runner '%s' with 5 extra arguments",name) + end + runner = compile(data.list,nodeprocessor,5) + data.runner = runner + end + return runner(head,one,two,three,four,five) + end + else + return function(head,...) + total = total + 1 -- will go away + local runner = data.runner + if not runner then + created = created + 1 + if trace_tasks then + logs.report("nodes","creating task runner '%s' with n extra arguments",name) + end + runner = compile(data.list,nodeprocessor,"n") + data.runner = runner + end + return runner(head,...) + end + end + else + return nil + end +end + +function tasks.table(name) --maybe move this to task-deb.lua + local tsk = tasks.data[name] + local lst = tsk and tsk.list + local HL, NC, NR, bold, type = context.HL, context.NC, context.NR, context.bold, context.type + if lst then + local list, order = lst.list, lst.order + if list and order then + context.starttabulate { "|l|l|" } + NC() bold("category") NC() bold("function") NC() NR() + for i=1,#order do + HL() + local o = order[i] + local l = list[o] + if #l == 0 then + NC() type(o) NC() context("unset") NC() NR() + else + local done = false + for k, v in table.sortedhash(l) do + NC() if not done then type(o) done = true end NC() type(v) NC() NR() + end + end + end + context.stoptabulate() + end + end +end + +tasks.new ( + "processors", + { + "before", -- for users + "normalizers", + "characters", + "words", + "fonts", + "lists", + "after", -- for users + } +) + +tasks.new ( + "finalizers", + { + "before", -- for users + "normalizers", +-- "characters", +-- "finishers", + "fonts", + "lists", + "after", -- for users + } +) + +tasks.new ( + "shipouts", + { + "before", -- for users + "normalizers", + "finishers", + "after", -- for users + } +) + +tasks.new ( + "mvlbuilders", + { + "before", -- for users + "normalizers", + "after", -- for users + } +) + +tasks.new ( + "vboxbuilders", + { + "before", -- for users + "normalizers", + "after", -- for users + } +) + +--~ tasks.new ( +--~ "parbuilders", +--~ { +--~ "before", -- for users +--~ "lists", +--~ "after", -- for users +--~ } +--~ ) + +--~ tasks.new ( +--~ "pagebuilders", +--~ { +--~ "before", -- for users +--~ "lists", +--~ "after", -- for users +--~ } +--~ ) diff --git a/tex/context/base/node-tst.lua b/tex/context/base/node-tst.lua new file mode 100644 index 000000000..d7ea96f26 --- /dev/null +++ b/tex/context/base/node-tst.lua @@ -0,0 +1,119 @@ +if not modules then modules = { } end modules ['node-tst'] = { + version = 1.001, + comment = "companion to node-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local glue = node.id("glue") +local penalty = node.id("penalty") +local kern = node.id("kern") +local glyph = node.id("glyph") +local whatsit = node.id("whatsit") +local hlist = node.id("hlist") + +local find_node_tail = node.tail or node.slide + +local chardata = characters.data + +function nodes.the_left_margin(n) -- todo: three values + while n do + local id = n.id + if id == glue then + if n.subtype == 8 then -- 7 in c/web source + return n.spec.width + else + return 0 + end + elseif id == whatsit then + n = n.next + elseif id == hlist then + return n.width + else + break + end + end + return 0 +end + +function nodes.the_right_margin(n) + if n then + n = find_node_tail(n) + while n do + local id = n.id + if id == glue then + if n.subtype == 9 then -- 8 in the c/web source + return n.spec.width + else + return 0 + end + elseif id == whatsit then + n = n.prev + else + break + end + end + end + return false +end + +function nodes.somespace(n,all) + if n then + local id = n.id + if id == glue then + return (all or (n.spec.width ~= 0)) and glue + elseif id == kern then + return (all or (n.kern ~= 0)) and kern + elseif id == glyph then + local category = chardata[n.char].category + -- maybe more category checks are needed + return (category == "zs") and glyph + end + end + return false +end + +function nodes.somepenalty(n,value) + if n then + local id = n.id + if id == penalty then + if value then + return n.penalty == value + else + return true + end + end + end + return false +end + +function nodes.is_display_math(head) + local n = head.prev + while n do + local id = n.id + if id == penalty then + elseif id == glue then + if n.subtype == 6 then -- above_display_short_skip + return true + end + else + break + end + n = n.prev + end + n = head.next + while n do + local id = n.id + if id == penalty then + elseif id == glue then + if n.subtype == 7 then -- below_display_short_skip + return true + end + else + break + end + n = n.next + end + return false +end diff --git a/tex/context/base/node-typ.lua b/tex/context/base/node-typ.lua new file mode 100644 index 000000000..5ab6b6975 --- /dev/null +++ b/tex/context/base/node-typ.lua @@ -0,0 +1,53 @@ +if not modules then modules = { } end modules ['node-typ'] = { + version = 1.001, + comment = "companion to node-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- this will be replaced by blob-ini cum suis so typesetting will go away + +local utfvalues = string.utfvalues + +local newglyph = nodes.glyph +local newglue = nodes.glue + +local hpack, vpack = node.hpack, node.vpack + +typesetting = typesetting or { } + +local function tonodes(str,fontid,spacing) -- don't use this + local head, prev = nil, nil + for s in utfvalues(str) do + local next + if spacing and s == 32 then + next = newglue(spacing or 64*1024*10) + else + next = newglyph(fontid or 1,s) + end + if not head then + head = next + else + prev.next = next + next.prev = prev + end + prev = next + end + return head +end + +typesetting.tonodes = tonodes + +function typesetting.hpack(str,fontid,spacing) + return hpack(tonodes(str,fontid,spacing)) +end + +function typesetting.vpack(str,fontid,spacing) + -- vpack is just a hack, and a proper implentation is on the agenda + -- as it needs more info etc than currently available + return vpack(tonodes(str,fontid,spacing)) +end + +--~ node.write(typesetting.hpack("Hello World!")) +--~ node.write(typesetting.hpack("Hello World!",1,100*1024*10)) diff --git a/tex/context/base/norm-alo.tex b/tex/context/base/norm-alo.tex new file mode 100644 index 000000000..d47f49037 --- /dev/null +++ b/tex/context/base/norm-alo.tex @@ -0,0 +1,36 @@ +%D \module +%D [ file=norm-alo, +%D version=2009.03.19, +%D title=\CONTEXT\ Norm Macros, +%D subtitle=\ALEPH\ and \OMEGA, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D This file will become obsolete! + +% omega primitives + +\let\textdir = \textdir +\let\pagedir = \pagedir +\let\mathdir = \mathdir +\let\pardir = \pardir +\let\bodydir = \bodydir +\let\leftghost = \leftghost +\let\rightghost = \rightghost +\let\localleftbox = \localleftbox +\let\localrightbox = \localrightbox +\let\localinterlinepenalty = \localinterlinepenalty +\let\localbrokenpenalty = \localbrokenpenalty + +% aleph primitives + +\let\boxdir = \boxdir +\let\pagebottomoffset = \pagebottomoffset +\let\pagerightoffset = \pagerightoffset + +\endinput diff --git a/tex/context/base/norm-ctx.tex b/tex/context/base/norm-ctx.tex new file mode 100644 index 000000000..707705d89 --- /dev/null +++ b/tex/context/base/norm-ctx.tex @@ -0,0 +1,16 @@ +%D \module +%D [ file=norm-ctx, +%D version=2009.03.19, +%D title=\CONTEXT\ Norm Macros, +%D subtitle=\ALEPH\ and \OMEGA, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D A few more might end up here (like the weird ones in syst-ini). + +\let\normalreqno = \normaleqno diff --git a/tex/context/base/norm-etx.tex b/tex/context/base/norm-etx.tex new file mode 100644 index 000000000..3edd8e7ef --- /dev/null +++ b/tex/context/base/norm-etx.tex @@ -0,0 +1,79 @@ +%D \module +%D [ file=norm-etx, +%D version=2009.03.19, +%D title=\CONTEXT\ Norm Macros, +%D subtitle=\ETEX, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +% etex primitives + +\let \normalbotmarks = \botmarks +\let \normalclubpenalties = \clubpenalties +\let \normalcurrentgrouplevel = \currentgrouplevel +\let \normalcurrentgrouptype = \currentgrouptype +\let \normalcurrentifbranch = \currentifbranch +\let \normalcurrentiflevel = \currentiflevel +\let \normalcurrentiftype = \currentiftype +\let \normaldetokenize = \detokenize +\let \normaldimexpr = \dimexpr +\let \normaldisplaywidowpenalties = \displaywidowpenalties +\let \normaleTeXVersion = \eTeXVersion +\let \normaleTeXminorversion = \eTeXminorversion +\let \normaleTeXrevision = \eTeXrevision +\let \normaleTeXversion = \eTeXversion +\let \normaleveryeof = \everyeof +\let \normalfirstmarks = \firstmarks +\let \normalfontchardp = \fontchardp +\let \normalfontcharht = \fontcharht +\let \normalfontcharic = \fontcharic +\let \normalfontcharwd = \fontcharwd +\let \normalglueexpr = \glueexpr +\let \normalglueshrink = \glueshrink +\let \normalglueshrinkorder = \glueshrinkorder +\let \normalgluestretch = \gluestretch +\let \normalgluestretchorder = \gluestretchorder +\let \normalgluetomu = \gluetomu +\let \normalifcsname = \ifcsname +\let \normalifdefined = \ifdefined +\let \normaliffontchar = \iffontchar +\let \normalinteractionmode = \interactionmode +\let \normalinterlinepenalties = \interlinepenalties +\let \normallastlinefit = \lastlinefit +\let \normallastnodetype = \lastnodetype +\let \normalmarks = \marks +\let \normalmuexpr = \muexpr +\let \normalmutoglue = \mutoglue +\let \normalnumexpr = \numexpr +\let \normalpagediscards = \pagediscards +\let \normalparshapedimen = \parshapedimen +\let \normalparshapeindent = \parshapeindent +\let \normalparshapelength = \parshapelength +\let \normalpredisplaydirection = \predisplaydirection +\let \normalprotected = \protected +\let \normalreadline = \readline +\let \normalsavinghyphcodes = \savinghyphcodes +\let \normalsavingvdiscards = \savingvdiscards +\let \normalscantokens = \scantokens +\let \normalshowgroups = \showgroups +\let \normalshowifs = \showifs +\let \normalshowtokens = \showtokens +\let \normalsplitbotmarks = \splitbotmarks +\let \normalsplitdiscards = \splitdiscards +\let \normalsplitfirstmarks = \splitfirstmarks +\let \normaltopmarks = \topmarks +\let \normaltracingassigns = \tracingassigns +\let \normaltracinggroups = \tracinggroups +\let \normaltracingifs = \tracingifs +\let \normaltracingnesting = \tracingnesting +\let \normaltracingscantokens = \tracingscantokens +\let \normalunexpanded = \unexpanded +\let \normalunless = \unless +\let \normalwidowpenalties = \widowpenalties + +\endinput diff --git a/tex/context/base/norm-ltx.tex b/tex/context/base/norm-ltx.tex new file mode 100644 index 000000000..a779735dd --- /dev/null +++ b/tex/context/base/norm-ltx.tex @@ -0,0 +1,177 @@ +%D \module +%D [ file=norm-ltx, +%D version=2009.03.19, +%D title=\CONTEXT\ Norm Macros, +%D subtitle=\LUATEX, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D This file will become obsolete! + +% luatex primitives (incomplete) + +\let \normalUdelcode = \Udelcode +\let \normalUdelcodenum = \Udelcodenum +\let \normalUdelimiter = \Udelimiter +\let \normalUmathaccent = \Umathaccent +\let \normalUmathaccents = \Umathaccents +\let \normalUmathaxis = \Umathaxis +\let \normalUmathbinbinspacing = \Umathbinbinspacing +\let \normalUmathbinclosespacing = \Umathbinclosespacing +\let \normalUmathbininnerspacing = \Umathbininnerspacing +\let \normalUmathbinopenspacing = \Umathbinopenspacing +\let \normalUmathbinopspacing = \Umathbinopspacing +\let \normalUmathbinordspacing = \Umathbinordspacing +\let \normalUmathbinpunctspacing = \Umathbinpunctspacing +\let \normalUmathbinrelspacing = \Umathbinrelspacing +\let \normalUmathbotaccent = \Umathbotaccent +\let \normalUmathchar = \Umathchar +\let \normalUmathchardef = \Umathchardef +\let \normalUmathcharnum = \Umathcharnum +\let \normalUmathclosebinspacing = \Umathclosebinspacing +\let \normalUmathcloseclosespacing = \Umathcloseclosespacing +\let \normalUmathcloseinnerspacing = \Umathcloseinnerspacing +\let \normalUmathcloseopenspacing = \Umathcloseopenspacing +\let \normalUmathcloseopspacing = \Umathcloseopspacing +\let \normalUmathcloseordspacing = \Umathcloseordspacing +\let \normalUmathclosepunctspacing = \Umathclosepunctspacing +\let \normalUmathcloserelspacing = \Umathcloserelspacing +\let \normalUmathcode = \Umathcode +\let \normalUmathcodenum = \Umathcodenum +\let \normalUmathconnectoroverlapmin = \Umathconnectoroverlapmin +\let \normalUmathfractiondelsize = \Umathfractiondelsize +\let \normalUmathfractiondenomdown = \Umathfractiondenomdown +\let \normalUmathfractiondenomvgap = \Umathfractiondenomvgap +\let \normalUmathfractionnumup = \Umathfractionnumup +\let \normalUmathfractionnumvgap = \Umathfractionnumvgap +\let \normalUmathfractionrule = \Umathfractionrule +\let \normalUmathinnerbinspacing = \Umathinnerbinspacing +\let \normalUmathinnerclosespacing = \Umathinnerclosespacing +\let \normalUmathinnerinnerspacing = \Umathinnerinnerspacing +\let \normalUmathinneropenspacing = \Umathinneropenspacing +\let \normalUmathinneropspacing = \Umathinneropspacing +\let \normalUmathinnerordspacing = \Umathinnerordspacing +\let \normalUmathinnerpunctspacing = \Umathinnerpunctspacing +\let \normalUmathinnerrelspacing = \Umathinnerrelspacing +\let \normalUmathlimitabovebgap = \Umathlimitabovebgap +\let \normalUmathlimitabovekern = \Umathlimitabovekern +\let \normalUmathlimitabovevgap = \Umathlimitabovevgap +\let \normalUmathlimitdownbgap = \Umathlimitdownbgap +\let \normalUmathlimitdownkern = \Umathlimitdownkern +\let \normalUmathlimitdownvgap = \Umathlimitdownvgap +\let \normalUmathopbinspacing = \Umathopbinspacing +\let \normalUmathopclosespacing = \Umathopclosespacing +\let \normalUmathopenbinspacing = \Umathopenbinspacing +\let \normalUmathopenclosespacing = \Umathopenclosespacing +\let \normalUmathopeninnerspacing = \Umathopeninnerspacing +\let \normalUmathopenopenspacing = \Umathopenopenspacing +\let \normalUmathopenopspacing = \Umathopenopspacing +\let \normalUmathopenordspacing = \Umathopenordspacing +\let \normalUmathopenpunctspacing = \Umathopenpunctspacing +\let \normalUmathopenrelspacing = \Umathopenrelspacing +\let \normalUmathoperatorsize = \Umathoperatorsize +\let \normalUmathopinnerspacing = \Umathopinnerspacing +\let \normalUmathopopenspacing = \Umathopopenspacing +\let \normalUmathopopspacing = \Umathopopspacing +\let \normalUmathopordspacing = \Umathopordspacing +\let \normalUmathoppunctspacing = \Umathoppunctspacing +\let \normalUmathoprelspacing = \Umathoprelspacing +\let \normalUmathordbinspacing = \Umathordbinspacing +\let \normalUmathordclosespacing = \Umathordclosespacing +\let \normalUmathordinnerspacing = \Umathordinnerspacing +\let \normalUmathordopenspacing = \Umathordopenspacing +\let \normalUmathordopspacing = \Umathordopspacing +\let \normalUmathordordspacing = \Umathordordspacing +\let \normalUmathordpunctspacing = \Umathordpunctspacing +\let \normalUmathordrelspacing = \Umathordrelspacing +\let \normalUmathoverbarkern = \Umathoverbarkern +\let \normalUmathoverbarrule = \Umathoverbarrule +\let \normalUmathoverbarvgap = \Umathoverbarvgap +\let \normalUmathoverdelimiterbgap = \Umathoverdelimiterbgap +\let \normalUmathoverdelimitervgap = \Umathoverdelimitervgap +\let \normalUmathpunctbinspacing = \Umathpunctbinspacing +\let \normalUmathpunctclosespacing = \Umathpunctclosespacing +\let \normalUmathpunctinnerspacing = \Umathpunctinnerspacing +\let \normalUmathpunctopenspacing = \Umathpunctopenspacing +\let \normalUmathpunctopspacing = \Umathpunctopspacing +\let \normalUmathpunctordspacing = \Umathpunctordspacing +\let \normalUmathpunctpunctspacing = \Umathpunctpunctspacing +\let \normalUmathpunctrelspacing = \Umathpunctrelspacing +\let \normalUmathquad = \Umathquad +\let \normalUmathradicaldegreeafter = \Umathradicaldegreeafter +\let \normalUmathradicaldegreebefore = \Umathradicaldegreebefore +\let \normalUmathradicaldegreeraise = \Umathradicaldegreeraise +\let \normalUmathradicalkern = \Umathradicalkern +\let \normalUmathradicalrule = \Umathradicalrule +\let \normalUmathradicalvgap = \Umathradicalvgap +\let \normalUmathrelbinspacing = \Umathrelbinspacing +\let \normalUmathrelclosespacing = \Umathrelclosespacing +\let \normalUmathrelinnerspacing = \Umathrelinnerspacing +\let \normalUmathrelopenspacing = \Umathrelopenspacing +\let \normalUmathrelopspacing = \Umathrelopspacing +\let \normalUmathrelordspacing = \Umathrelordspacing +\let \normalUmathrelpunctspacing = \Umathrelpunctspacing +\let \normalUmathrelrelspacing = \Umathrelrelspacing +\let \normalUmathspaceafterscript = \Umathspaceafterscript +\let \normalUmathstackdenomdown = \Umathstackdenomdown +\let \normalUmathstacknumup = \Umathstacknumup +\let \normalUmathstackvgap = \Umathstackvgap +\let \normalUmathsubshiftdown = \Umathsubshiftdown +\let \normalUmathsubshiftdrop = \Umathsubshiftdrop +\let \normalUmathsubsupshiftdown = \Umathsubsupshiftdown +\let \normalUmathsubsupvgap = \Umathsubsupvgap +\let \normalUmathsubtopmax = \Umathsubtopmax +\let \normalUmathsupbottommin = \Umathsupbottommin +\let \normalUmathsupshiftdrop = \Umathsupshiftdrop +\let \normalUmathsupshiftup = \Umathsupshiftup +\let \normalUmathsupsubbottommax = \Umathsupsubbottommax +\let \normalUmathunderbarkern = \Umathunderbarkern +\let \normalUmathunderbarrule = \Umathunderbarrule +\let \normalUmathunderbarvgap = \Umathunderbarvgap +\let \normalUmathunderdelimiterbgap = \Umathunderdelimiterbgap +\let \normalUmathunderdelimitervgap = \Umathunderdelimitervgap +\let \normalUoverdelimiter = \Uoverdelimiter +\let \normalUradical = \Uradical +\let \normalUroot = \Uroot +\let \normalUunderdelimiter = \Uunderdelimiter +\let \normalattribute = \attribute +\let \normalattributedef = \attributedef +\let \normalcatcodetable = \catcodetable +\let \normalclearmarks = \clearmarks +\let \normalcrampeddisplaystyle = \crampeddisplaystyle +\let \normalcrampedscriptscriptstyle = \crampedscriptscriptstyle +\let \normalcrampedscriptstyle = \crampedscriptstyle +\let \normalcrampedtextstyle = \crampedtextstyle +\let \normalformatname = \formatname +\let \normalifabsdim = \ifabsdim +\let \normalifabsnum = \ifabsnum +\let \normalifprimitive = \ifprimitive +\let \normalinitcatcodetable = \initcatcodetable +\let \normallatelua = \latelua +\let \normalluaescapestring = \luaescapestring +\let \normalluastartup = \luastartup +\let \normalluatexdatestamp = \luatexdatestamp +\let \normalluatexrevision = \luatexrevision +\let \normalluatexversion = \luatexversion +\let \normalnokerns = \nokerns +\let \normalnoligs = \noligs +\let \normalpageleftoffset = \pageleftoffset +\let \normalpagetopoffset = \pagetopoffset +\let \normalpostexhyphenchar = \postexhyphenchar +\let \normalposthyphenchar = \posthyphenchar +\let \normalpreexhyphenchar = \preexhyphenchar +\let \normalprehyphenchar = \prehyphenchar +\let \normalprimitive = \primitive +\let \normalsavecatcodetable = \savecatcodetable +\let \normalscantextokens = \scantextokens +\let \normalsuppressfontnotfounderror = \suppressfontnotfounderror +\let \normalsuppressifcsnameerror = \suppressifcsnameerror +\let \normalsuppresslongerror = \suppresslongerror +\let \normalsynctex = \synctex + +\endinput diff --git a/tex/context/base/norm-ptx.tex b/tex/context/base/norm-ptx.tex new file mode 100644 index 000000000..8f911d874 --- /dev/null +++ b/tex/context/base/norm-ptx.tex @@ -0,0 +1,130 @@ +%D \module +%D [ file=norm-ptx, +%D version=2009.03.19, +%D title=\CONTEXT\ Norm Macros, +%D subtitle=\PDFTEX, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\let \normalefcode = \efcode +\let \normalexpanded = \expanded +\let \normalifincsname = \ifincsname +\let \normalifpdfabsdim = \ifpdfabsdim +\let \normalifpdfabsnum = \ifpdfabsnum +\let \normalifpdfprimitive = \ifpdfprimitive +\let \normalleftmarginkern = \leftmarginkern +\let \normalletterspacefont = \letterspacefont +\let \normallpcode = \lpcode +\let \normalpdfadjustspacing = \pdfadjustspacing +\let \normalpdfannot = \pdfannot +\let \normalpdfcatalog = \pdfcatalog +\let \normalpdfcolorstack = \pdfcolorstack +\let \normalpdfcolorstackinit = \pdfcolorstackinit +\let \normalpdfcompresslevel = \pdfcompresslevel +\let \normalpdfcopyfont = \pdfcopyfont +\let \normalpdfcreationdate = \pdfcreationdate +\let \normalpdfdecimaldigits = \pdfdecimaldigits +\let \normalpdfdest = \pdfdest +\let \normalpdfdestmargin = \pdfdestmargin +\let \normalpdfdraftmode = \pdfdraftmode +\let \normalpdfeachlinedepth = \pdfeachlinedepth +\let \normalpdfeachlineheight = \pdfeachlineheight +\let \normalpdfendlink = \pdfendlink +\let \normalpdfendthread = \pdfendthread +\let \normalpdffirstlineheight = \pdffirstlineheight +\let \normalpdffontattr = \pdffontattr +\let \normalpdffontexpand = \pdffontexpand +\let \normalpdffontname = \pdffontname +\let \normalpdffontobjnum = \pdffontobjnum +\let \normalpdffontsize = \pdffontsize +\let \normalpdfforcepagebox = \pdfforcepagebox % obsolete +\let \normalpdfgamma = \pdfgamma +\let \normalpdfgentounicode = \pdfgentounicode +\let \normalpdfglyphtounicode = \pdfglyphtounicode +\let \normalpdfhorigin = \pdfhorigin +\let \normalpdfignoreddimen = \pdfignoreddimen +\let \normalpdfimageapplygamma = \pdfimageapplygamma +\let \normalpdfimagegamma = \pdfimagegamma +\let \normalpdfimagehicolor = \pdfimagehicolor +\let \normalpdfimageresolution = \pdfimageresolution +\let \normalpdfincludechars = \pdfincludechars +\let \normalpdfinclusioncopyfonts = \pdfinclusioncopyfonts +\let \normalpdfinclusionerrorlevel = \pdfinclusionerrorlevel +\let \normalpdfinfo = \pdfinfo +\let \normalpdfinsertht = \pdfinsertht +\let \normalpdflastannot = \pdflastannot +\let \normalpdflastlinedepth = \pdflastlinedepth +\let \normalpdflastlink = \pdflastlink +\let \normalpdflastobj = \pdflastobj +\let \normalpdflastxform = \pdflastxform +\let \normalpdflastximage = \pdflastximage +\let \normalpdflastximagecolordepth = \pdflastximagecolordepth +\let \normalpdflastximagepages = \pdflastximagepages +\let \normalpdflastxpos = \pdflastxpos +\let \normalpdflastypos = \pdflastypos +\let \normalpdflinkmargin = \pdflinkmargin +\let \normalpdfliteral = \pdfliteral +\let \normalpdfmapfile = \pdfmapfile +\let \normalpdfmapline = \pdfmapline +\let \normalpdfminorversion = \pdfminorversion +\let \normalpdfmovechars = \pdfmovechars % obsolete +\let \normalpdfnames = \pdfnames +\let \normalpdfnoligatures = \pdfnoligatures +\let \normalpdfnormaldeviate = \pdfnormaldeviate +\let \normalpdfobj = \pdfobj +\let \normalpdfobjcompresslevel = \pdfobjcompresslevel +\let \normalpdfoptionalwaysusepdfpagebox = \pdfoptionalwaysusepdfpagebox % obsolete +\let \normalpdfoptionpdfinclusionerrorlevel = \pdfoptionpdfinclusionerrorlevel % obsolete +\let \normalpdfoptionpdfminorversion = \pdfoptionpdfminorversion +\let \normalpdfoutline = \pdfoutline +\let \normalpdfoutput = \pdfoutput +\let \normalpdfpageattr = \pdfpageattr +\let \normalpdfpagebox = \pdfpagebox +\let \normalpdfpageheight = \pdfpageheight +\let \normalpdfpageref = \pdfpageref +\let \normalpdfpageresources = \pdfpageresources +\let \normalpdfpagesattr = \pdfpagesattr +\let \normalpdfpagewidth = \pdfpagewidth +\let \normalpdfpkmode = \pdfpkmode +\let \normalpdfpkresolution = \pdfpkresolution +\let \normalpdfprimitive = \pdfprimitive +\let \normalpdfprotrudechars = \pdfprotrudechars +\let \normalpdfpxdimen = \pdfpxdimen +\let \normalpdfrandomseed = \pdfrandomseed +\let \normalpdfrefobj = \pdfrefobj +\let \normalpdfrefxform = \pdfrefxform +\let \normalpdfrefximage = \pdfrefximage +\let \normalpdfreplacefont = \pdfreplacefont +\let \normalpdfrestore = \pdfrestore +\let \normalpdfretval = \pdfretval +\let \normalpdfsave = \pdfsave +\let \normalpdfsavepos = \pdfsavepos +\let \normalpdfsetmatrix = \pdfsetmatrix +\let \normalpdfsetrandomseed = \pdfsetrandomseed +\let \normalpdfstartlink = \pdfstartlink +\let \normalpdfstartthread = \pdfstartthread +\let \normalpdftexbanner = \pdftexbanner +\let \normalpdftexrevision = \pdftexrevision +\let \normalpdftexversion = \pdftexversion +\let \normalpdfthread = \pdfthread +\let \normalpdfthreadmargin = \pdfthreadmargin +\let \normalpdftracingfonts = \pdftracingfonts +\let \normalpdftrailer = \pdftrailer +\let \normalpdfuniformdeviate = \pdfuniformdeviate +\let \normalpdfuniqueresname = \pdfuniqueresname +\let \normalpdfvorigin = \pdfvorigin +\let \normalpdfxform = \pdfxform +\let \normalpdfxformname = \pdfxformname +\let \normalpdfximage = \pdfximage +\let \normalpdfximagebbox = \pdfximagebbox +\let \normalquitvmode = \quitvmode +\let \normalrightmarginkern = \rightmarginkern +\let \normalrpcode = \rpcode +\let \normaltagcode = \tagcode + +\endinput diff --git a/tex/context/base/norm-tex.tex b/tex/context/base/norm-tex.tex new file mode 100644 index 000000000..61f9740ef --- /dev/null +++ b/tex/context/base/norm-tex.tex @@ -0,0 +1,351 @@ +%D \module +%D [ file=norm-etx, +%D version=2009.03.19, +%D title=\CONTEXT\ Norm Macros, +%D subtitle=\TEX, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D Since \LUATEX\ can generate these lists internally it started +%D to make sense to cleanup this \type {\normalstuff} for \MKII\ as +%D well. The tables are generated with a \LUA\ script. + +% tex primitives + +% Beware, we already redefined \dump, \outer and \everyjob ! + +% \normal = \ +% \normal- = \- +% \normal/ = \/ +\let \normalabove = \above +\let \normalabovedisplayshortskip = \abovedisplayshortskip +\let \normalabovedisplayskip = \abovedisplayskip +\let \normalabovewithdelims = \abovewithdelims +\let \normalaccent = \accent +\let \normaladjdemerits = \adjdemerits +\let \normaladvance = \advance +\let \normalafterassignment = \afterassignment +\let \normalaftergroup = \aftergroup +\let \normalatop = \atop +\let \normalatopwithdelims = \atopwithdelims +\let \normalbadness = \badness +\let \normalbaselineskip = \baselineskip +\let \normalbatchmode = \batchmode +\let \normalbegingroup = \begingroup +\let \normalbelowdisplayshortskip = \belowdisplayshortskip +\let \normalbelowdisplayskip = \belowdisplayskip +\let \normalbinoppenalty = \binoppenalty +\let \normalbotmark = \botmark +\let \normalbox = \box +\let \normalboxmaxdepth = \boxmaxdepth +\let \normalbrokenpenalty = \brokenpenalty +\let \normalcatcode = \catcode +\let \normalchar = \char +\let \normalchardef = \chardef +\let \normalcleaders = \cleaders +\let \normalclosein = \closein +\let \normalcloseout = \closeout +\let \normalclubpenalty = \clubpenalty +\let \normalcopy = \copy +\let \normalcount = \count +\let \normalcountdef = \countdef +\let \normalcr = \cr +\let \normalcrcr = \crcr +\let \normalcsname = \csname +\let \normalday = \day +\let \normaldeadcycles = \deadcycles +\let \normaldef = \def +\let \normaldefaulthyphenchar = \defaulthyphenchar +\let \normaldefaultskewchar = \defaultskewchar +\let \normaldelcode = \delcode +\let \normaldelimiter = \delimiter +\let \normaldelimiterfactor = \delimiterfactor +\let \normaldelimitershortfall = \delimitershortfall +\let \normaldimen = \dimen +\let \normaldimendef = \dimendef +\let \normaldirectlua = \directlua +\let \normaldiscretionary = \discretionary +\let \normaldisplayindent = \displayindent +\let \normaldisplaylimits = \displaylimits +\let \normaldisplaystyle = \displaystyle +\let \normaldisplaywidowpenalty = \displaywidowpenalty +\let \normaldisplaywidth = \displaywidth +\let \normaldivide = \divide +\let \normaldoublehyphendemerits = \doublehyphendemerits +\let \normaldp = \dp +% \normaldump = \dump +\let \normaledef = \edef +\let \normalelse = \else +\let \normalemergencystretch = \emergencystretch +\let \normalend = \end +\let \normalendcsname = \endcsname +\let \normalendgroup = \endgroup +\let \normalendinput = \endinput +\let \normalendlinechar = \endlinechar +\let \normaleqno = \eqno +\let \normalerrhelp = \errhelp +\let \normalerrmessage = \errmessage +\let \normalerrorcontextlines = \errorcontextlines +\let \normalerrorstopmode = \errorstopmode +\let \normalescapechar = \escapechar +\let \normaleverycr = \everycr +\let \normaleverydisplay = \everydisplay +\let \normaleveryhbox = \everyhbox +% \normaleveryjob = \everyjob +\let \normaleverymath = \everymath +\let \normaleverypar = \everypar +\let \normaleveryvbox = \everyvbox +\let \normalexhyphenchar = \exhyphenchar +\let \normalexhyphenpenalty = \exhyphenpenalty +\let \normalexpandafter = \expandafter +\let \normalfam = \fam +\let \normalfi = \fi +\let \normalfinalhyphendemerits = \finalhyphendemerits +\let \normalfirstmark = \firstmark +\let \normalfloatingpenalty = \floatingpenalty +\let \normalfont = \font +\let \normalfontdimen = \fontdimen +\let \normalfontname = \fontname +\let \normalfuturelet = \futurelet +\let \normalgdef = \gdef +\let \normalglobal = \global +\let \normalglobaldefs = \globaldefs +\let \normalhalign = \halign +\let \normalhangafter = \hangafter +\let \normalhangindent = \hangindent +\let \normalhbadness = \hbadness +\let \normalhbox = \hbox +\let \normalhfil = \hfil +\let \normalhfill = \hfill +\let \normalhfilneg = \hfilneg +\let \normalhfuzz = \hfuzz +\let \normalhoffset = \hoffset +\let \normalholdinginserts = \holdinginserts +\let \normalhrule = \hrule +\let \normalhsize = \hsize +\let \normalhskip = \hskip +\let \normalhss = \hss +\let \normalht = \ht +\let \normalhyphenation = \hyphenation +\let \normalhyphenchar = \hyphenchar +\let \normalhyphenpenalty = \hyphenpenalty +\let \normalif = \if +\let \normalifcase = \ifcase +\let \normalifcat = \ifcat +\let \normalifdim = \ifdim +\let \normalifeof = \ifeof +\let \normaliffalse = \iffalse +\let \normalifhbox = \ifhbox +\let \normalifhmode = \ifhmode +\let \normalifinner = \ifinner +\let \normalifmmode = \ifmmode +\let \normalifnum = \ifnum +\let \normalifodd = \ifodd +\let \normaliftrue = \iftrue +\let \normalifvbox = \ifvbox +\let \normalifvmode = \ifvmode +\let \normalifvoid = \ifvoid +\let \normalifx = \ifx +\let \normalignorespaces = \ignorespaces +\let \normalimmediate = \immediate +\let \normalindent = \indent +% \normalinput = \input +\let \normalinputlineno = \inputlineno +\let \normalinsert = \insert +\let \normalinsertpenalties = \insertpenalties +\let \normalinterlinepenalty = \interlinepenalty +\let \normaljobname = \jobname +\let \normalkern = \kern +\let \normallanguage = \language +\let \normallastbox = \lastbox +\let \normallastkern = \lastkern +\let \normallastpenalty = \lastpenalty +\let \normallastskip = \lastskip +\let \normallccode = \lccode +\let \normalleaders = \leaders +\let \normalleft = \left +\let \normallefthyphenmin = \lefthyphenmin +\let \normalleftskip = \leftskip +\let \normalleqno = \leqno +\let \normallet = \let +\let \normallimits = \limits +\let \normallinepenalty = \linepenalty +\let \normallineskip = \lineskip +\let \normallineskiplimit = \lineskiplimit +\let \normallong = \long +\let \normallooseness = \looseness +\let \normallower = \lower +\let \normallowercase = \lowercase +\let \normalmag = \mag +\let \normalmark = \mark +\let \normalmathaccent = \mathaccent +\let \normalmathbin = \mathbin +\let \normalmathchar = \mathchar +\let \normalmathchardef = \mathchardef +\let \normalmathchoice = \mathchoice +\let \normalmathclose = \mathclose +\let \normalmathcode = \mathcode +\let \normalmathinner = \mathinner +\let \normalmathop = \mathop +\let \normalmathopen = \mathopen +\let \normalmathord = \mathord +\let \normalmathpunct = \mathpunct +\let \normalmathrel = \mathrel +\let \normalmathsurround = \mathsurround +\let \normalmaxdeadcycles = \maxdeadcycles +\let \normalmaxdepth = \maxdepth +\let \normalmeaning = \meaning +\let \normalmedmuskip = \medmuskip +\let \normalmessage = \message +\let \normalmiddle = \middle +\let \normalmkern = \mkern +\let \normalmonth = \month +\let \normalmoveleft = \moveleft +\let \normalmoveright = \moveright +\let \normalmskip = \mskip +\let \normalmultiply = \multiply +\let \normalmuskip = \muskip +\let \normalmuskipdef = \muskipdef +\let \normalnewlinechar = \newlinechar +\let \normalnoalign = \noalign +\let \normalnoboundary = \noboundary +\let \normalnoexpand = \noexpand +\let \normalnoindent = \noindent +\let \normalnolimits = \nolimits +\let \normalnonscript = \nonscript +\let \normalnonstopmode = \nonstopmode +\let \normalnulldelimiterspace = \nulldelimiterspace +\let \normalnullfont = \nullfont +\let \normalnumber = \number +\let \normalomit = \omit +\let \normalopenin = \openin +\let \normalopenout = \openout +\let \normalor = \or +% \normalouter = \outer +\let \normaloutput = \output +\let \normaloutputpenalty = \outputpenalty +\let \normalover = \over +\let \normaloverfullrule = \overfullrule +\let \normaloverline = \overline +\let \normaloverwithdelims = \overwithdelims +\let \normalpagedepth = \pagedepth +\let \normalpagefilllstretch = \pagefilllstretch +\let \normalpagefillstretch = \pagefillstretch +\let \normalpagefilstretch = \pagefilstretch +\let \normalpagegoal = \pagegoal +\let \normalpageshrink = \pageshrink +\let \normalpagestretch = \pagestretch +\let \normalpagetotal = \pagetotal +\let \normalpar = \par +\let \normalparfillskip = \parfillskip +\let \normalparindent = \parindent +\let \normalparshape = \parshape +\let \normalparskip = \parskip +\let \normalpatterns = \patterns +\let \normalpausing = \pausing +\let \normalpenalty = \penalty +\let \normalpostdisplaypenalty = \postdisplaypenalty +\let \normalpredisplaypenalty = \predisplaypenalty +\let \normalpredisplaysize = \predisplaysize +\let \normalpretolerance = \pretolerance +\let \normalprevdepth = \prevdepth +\let \normalprevgraf = \prevgraf +\let \normalradical = \radical +\let \normalraise = \raise +\let \normalread = \read +\let \normalrelax = \relax +\let \normalrelpenalty = \relpenalty +\let \normalright = \right +\let \normalrighthyphenmin = \righthyphenmin +\let \normalrightskip = \rightskip +\let \normalromannumeral = \romannumeral +\let \normalscriptfont = \scriptfont +\let \normalscriptscriptfont = \scriptscriptfont +\let \normalscriptscriptstyle = \scriptscriptstyle +\let \normalscriptspace = \scriptspace +\let \normalscriptstyle = \scriptstyle +\let \normalscrollmode = \scrollmode +\let \normalsetbox = \setbox +\let \normalsetlanguage = \setlanguage +\let \normalsfcode = \sfcode +\let \normalshipout = \shipout +\let \normalshow = \show +\let \normalshowbox = \showbox +\let \normalshowboxbreadth = \showboxbreadth +\let \normalshowboxdepth = \showboxdepth +\let \normalshowlists = \showlists +\let \normalshowthe = \showthe +\let \normalskewchar = \skewchar +\let \normalskip = \skip +\let \normalskipdef = \skipdef +\let \normalspacefactor = \spacefactor +\let \normalspaceskip = \spaceskip +\let \normalspan = \span +\let \normalspecial = \special +\let \normalsplitbotmark = \splitbotmark +\let \normalsplitfirstmark = \splitfirstmark +\let \normalsplitmaxdepth = \splitmaxdepth +\let \normalsplittopskip = \splittopskip +\let \normalstring = \string +\let \normaltabskip = \tabskip +\let \normaltextfont = \textfont +\let \normaltextstyle = \textstyle +\let \normalthe = \the +\let \normalthickmuskip = \thickmuskip +\let \normalthinmuskip = \thinmuskip +\let \normaltime = \time +\let \normaltoks = \toks +\let \normaltoksdef = \toksdef +\let \normaltolerance = \tolerance +\let \normaltopmark = \topmark +\let \normaltopskip = \topskip +\let \normaltracingcommands = \tracingcommands +\let \normaltracinglostchars = \tracinglostchars +\let \normaltracingmacros = \tracingmacros +\let \normaltracingonline = \tracingonline +\let \normaltracingoutput = \tracingoutput +\let \normaltracingpages = \tracingpages +\let \normaltracingparagraphs = \tracingparagraphs +\let \normaltracingrestores = \tracingrestores +\let \normaltracingstats = \tracingstats +\let \normaluccode = \uccode +\let \normaluchyph = \uchyph +\let \normalunderline = \underline +\let \normalunhbox = \unhbox +\let \normalunhcopy = \unhcopy +\let \normalunkern = \unkern +\let \normalunpenalty = \unpenalty +\let \normalunskip = \unskip +\let \normalunvbox = \unvbox +\let \normalunvcopy = \unvcopy +\let \normaluppercase = \uppercase +\let \normalvadjust = \vadjust +\let \normalvalign = \valign +\let \normalvbadness = \vbadness +\let \normalvbox = \vbox +\let \normalvcenter = \vcenter +\let \normalvfil = \vfil +\let \normalvfill = \vfill +\let \normalvfilneg = \vfilneg +\let \normalvfuzz = \vfuzz +\let \normalvoffset = \voffset +\let \normalvrule = \vrule +\let \normalvsize = \vsize +\let \normalvskip = \vskip +\let \normalvsplit = \vsplit +\let \normalvss = \vss +\let \normalvtop = \vtop +\let \normalwd = \wd +\let \normalwidowpenalty = \widowpenalty +\let \normalwrite = \write +\let \normalxdef = \xdef +\let \normalxleaders = \xleaders +\let \normalxspaceskip = \xspaceskip +\let \normalyear = \year + +\endinput diff --git a/tex/context/base/norm-xtx.tex b/tex/context/base/norm-xtx.tex new file mode 100644 index 000000000..3da944656 --- /dev/null +++ b/tex/context/base/norm-xtx.tex @@ -0,0 +1,18 @@ +%D \module +%D [ file=norm-xtx, +%D version=2009.03.19, +%D title=\CONTEXT\ Norm Macros, +%D subtitle=\XETEX, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +% xetex primitives + +% nothing yet (also defined pdftex primitives) + +\endinput diff --git a/tex/context/base/pack-bar.mkiv b/tex/context/base/pack-bar.mkiv new file mode 100644 index 000000000..05afd32d0 --- /dev/null +++ b/tex/context/base/pack-bar.mkiv @@ -0,0 +1,67 @@ +%D \module +%D [ file=pack-bar, +%D version=2009.06.26, +%D title=\CONTEXT\ Packaging Macros, +%D subtitle=Bars, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Packaging Macros / Bars} + +%D This code has been moved from scrn-int to here (was some old +%D experimental code). It could be in scrn-bar but it's static. + +\unprotect + +%D \startbuffer +%D \dorecurse{10} +%D {\horizontalpositionbar +%D \pos\recurselevel \min1 \max10 +%D \token\framed{\recurselevel}% +%D \\} +%D +%D \hbox to 15em +%D {\hss +%D \dorecurse{10} +%D {\verticalpositionbar\pos\recurselevel\min1\max10\token\blackrule\\ +%D \hss}} +%D \stopbuffer + +\def\horizontalpositionbar\pos#1\min#2\max#3\token#4\\% + {\hbox to \hsize + {\hskip\zeropoint\!!plus #1\!!fill + \hskip\zeropoint\!!plus-#2\!!fill + #4\relax + \hskip\zeropoint\!!plus #3\!!fill + \hskip\zeropoint\!!plus-#1\!!fill}} + +\def\verticalpositionbar\pos#1\min#2\max#3\token#4\\% + {\vbox to \vsize + {\vskip\zeropoint\!!plus #1\!!fill + \vskip\zeropoint\!!plus-#2\!!fill + \hbox{#4}\relax + \vskip\zeropoint\!!plus #3\!!fill + \vskip\zeropoint\!!plus-#1\!!fill}} + +\def\horizontalgrowingbar\pos#1\min#2\max#3\height#4\depth#5\\% + {\hbox to \hsize + {\scratchcounter\numexpr#1-#2+\plusone\relax + \leaders\vrule\hskip\zeropoint\!!plus \scratchcounter\!!fill + \vrule\!!width\zeropoint\!!height#4\!!depth#5% + \hskip\zeropoint\!!plus #3\!!fill + \hskip\zeropoint\!!plus-#1\!!fill}} + +\def\verticalgrowingbar\pos#1\min#2\max#3\width#4\\% + {\vbox to \vsize + {\scratchcounter\numexpr#1-#2+\plusone\relax + \leaders\hrule\vskip\zeropoint\!!plus\scratchcounter\!!fill + \hrule\!!width#4\!!height\zeropoint\!!depth\zeropoint + \vskip\zeropoint\!!plus #3\!!fill + \vskip\zeropoint\!!plus-#1\!!fill}} + +\protect \endinput diff --git a/tex/context/base/pack-box.mkii b/tex/context/base/pack-box.mkii new file mode 100644 index 000000000..8adeedb91 --- /dev/null +++ b/tex/context/base/pack-box.mkii @@ -0,0 +1,954 @@ +%D \module +%D [ file=pack-box, +%D version=2002.04.12, +%D title=\CONTEXT\ Packaging Macros, +%D subtitle=Boxes, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Packaging Macros / Boxes} + +%D This module contains all kind of macros for moving content +%D around. Many macros here come from other modules, but +%D depencies made it more clear to isolate them. + +% \placeornament + +\unprotect + +% \definelayer[\v!tekst-2][\c!positie=\v!ja] +% \definelayer[\v!tekst-1][\c!positie=\v!ja] +% \definelayer[\v!tekst+1][\c!positie=\v!ja] +% \definelayer[\v!tekst+2][\c!positie=\v!ja] + +% we need to set the size, else we get dimensions depending +% on the content, which in itsel fis ok, but can lead to loops +% due to rounding errors (happened in demo-obv) + +\definelayer[\v!text-2][\c!position=\v!yes,\c!width=\overlaywidth,\c!height=\overlayheight] +\definelayer[\v!text-1][\c!position=\v!yes,\c!width=\overlaywidth,\c!height=\overlayheight] +\definelayer[\v!text+1][\c!position=\v!yes,\c!width=\overlaywidth,\c!height=\overlayheight] +\definelayer[\v!text+2][\c!position=\v!yes,\c!width=\overlaywidth,\c!height=\overlayheight] + +\def\internaltextoverlay#1% will become more generic and installable + {\startoverlay % i.e. probably an overlay by itself + {\positionoverlay{\v!text#1}} % see later + {\composedlayer {\v!text#1}} + \stopoverlay} + +%\def\internaltextoverlay#1% +% {\hbox to \zeropoint{\positionoverlay{\v!tekst#1}\hss}% +% \composedlayer{\v!tekst#1}} + +% todo: share info, so that tuo will be smaller + +\defineoverlay[\v!text-2][\internaltextoverlay{-2}] +\defineoverlay[\v!text-1][\internaltextoverlay{-1}] +\defineoverlay[\v!text+1][\internaltextoverlay{+1}] +\defineoverlay[\v!text+2][\internaltextoverlay{+2}] + +% to be documented + +\definelayer[anchor] + +\def\anchor + {\dosingleargument\doanchor} + +\def\doanchor[#1]% + {\ifundefined{\??an#1}\@EA\dodoanchor\else\@EA\nonoanchor\fi[#1]} + +\def\nonoanchor[#1]% + {\getvalue{\??an#1}} + +\def\dodoanchor[#1]% + {\dotripleempty\dododoanchor[#1]} + +\def\dododoanchor + {\ifthirdargument + \expandafter\dodoanchorT + \else + \expandafter\dodoanchorS + \fi} + +\def\dodoanchorS[#1][#2][#3]% + {\dodoanchorT[#1][#2][#2]} + +\def\dodoanchorT[#1][#2][#3]% + {\dowithnextbox + {\bgroup + \checktextbackgrounds + \setbox\scratchbox\null + \wd\scratchbox\nextboxwd + \ht\scratchbox\nextboxht + \dp\scratchbox\nextboxdp + \setlayer + [anchor] + [\c!width=\wd\scratchbox, + \c!height=\ht\scratchbox, + \c!offset=\!!zeropoint, + #2,#3] + {\setlayer[#1]{\flushnextbox}}% + \framed + [#2, + \c!background=anchor, + \c!offset=\v!overlay, + \c!frame=\v!off, + #3] + {\box\scratchbox}% + \egroup}% + \vbox} + +\def\defineanchor + {\doquadrupleempty\dodefineanchor} + +\def\dodefineanchor[#1][#2][#3][#4]% + {\setvalue{\??an#1}{\dodefinedanchor[#2][#3][#4]}} + +\def\dodefinedanchor[#1][#2][#3]% + {\def\docommand[##1][##2]% + {\ifsecondargument + \def\next{\dodoanchorT[#1][#2,##1][#3,##2]}% + \else\iffirstargument + \def\next{\dodoanchorT[#1][#2,##1][#2,##1]}% + \else + \def\next{\dodoanchorT[#1][#2][#3]}% + \fi\fi + \next}% + \dodoubleempty\docommand} + +\def\@@collectorbox{@@collectorbox} + +\def\definecollector + {\dodoubleargument\dodefinecollector} + +\def\dodefinecollector[#1][#2]% + {\ifundefined{\@@collectorbox#1}% + \expandafter\newbox\csname\@@collectorbox#1\endcsname + \fi + \resetcollector[#1]% + \setupcollector + [#1] + [\c!state=\v!start, + \c!x=\!!zeropoint,\c!y=\!!zeropoint, + \c!offset=\!!zeropoint,\c!rotation=, % geen 0 ! + \c!hoffset=\!!zeropoint,\c!voffset=\!!zeropoint, + \c!location=rb,\c!corner=,#2]} + +\def\setupcollector + {\dodoubleargument\dosetupcollector} + +\def\dosetupcollector[#1][#2]% + {\def\docommand##1{\getparameters[\??cb##1][#2]}% + \processcommalist[#1]\docommand} + +\def\setcollector + {\dodoubleargument\dosetcollector} + +\def\dosetcollector[#1][#2]% + {\bgroup + \forgetall + \dontcomplain + \dowithnextbox + {\ifundefined{\@@collectorbox#1}% + \writestatus{collector}{unknown layer #1}% + \else + \dodosetcollector[#1][#2]% + \fi + \egroup} + \hbox} + +\def\collectorparameter#1{\csname\??cb\currentcollector#1\endcsname} + +\def\dodosetcollector[#1][#2]% todo: keep reference point + {\def\currentcollector{#1}% + \mathchardef\collectorbox\csname\@@collectorbox#1\endcsname + \getparameters[\??cb#1][#2]% + \@@layerxsiz\wd\collectorbox + \@@layerysiz\ht\collectorbox + \doifvaluesomething{\??cb#1\c!rotation} + {\setbox\nextbox\hbox + {\rotate + [\c!location=\v!high, + \c!rotation=\collectorparameter\c!rotation] + {\flushnextbox}}}% + \advance\@@layerysiz\dp\collectorbox + \@@layerxpos\collectorparameter\c!x + \advance\@@layerxpos\collectorparameter\c!hoffset + \@@layerypos\collectorparameter\c!y + \advance\@@layerypos\collectorparameter\c!voffset + \doifelse\v!middle{\collectorparameter\c!corner} + {\ifdim\@@layerxsiz>\zeropoint + \advance\@@layerxpos.5\@@layerxsiz + \fi + \ifdim\@@layerysiz>\zeropoint + \advance\@@layerypos.5\@@layerysiz + \fi}% + {\ExpandBothAfter\doifinset\v!bottom{\collectorparameter\c!corner} + {\ifdim\@@layerysiz>\zeropoint + \advance\@@layerypos-\@@layerysiz + \@@layerypos-\@@layerypos + \fi}% + \ExpandBothAfter\doifinset\v!right{\collectorparameter\c!corner} + {\ifdim\@@layerxsiz>\zeropoint + \advance\@@layerxpos-\@@layerxsiz + \@@layerxpos-\@@layerxpos + \fi}}% + \setbox\nextbox\hbox + {\alignedbox[\collectorparameter\c!location]\vbox{\flushnextbox}}% + \boxmaxdepth\zeropoint % really needed, nice example + \global\advance\boxhdisplacement\@@layerxpos + \ifdim\boxhdisplacement<\zeropoint + \global\setbox\collectorbox\hbox + {\hskip-\boxhdisplacement + \box\collectorbox}% + \fi + \global\advance\boxvdisplacement\@@layerypos + \ifdim\boxvdisplacement<\zeropoint + \global\setbox\collectorbox\hbox + {\lower-\boxvdisplacement + \box\collectorbox}% + \fi + \@@layerxsiz\wd\collectorbox + \@@layerysiz\ht\collectorbox + \advance\@@layerysiz\dp\collectorbox + \global\setbox\collectorbox\hbox + {\box\collectorbox + \hskip-\@@layerxsiz + \hskip\@@layerxpos\relax + \ifdim\boxhdisplacement<\zeropoint + \hskip-\boxhdisplacement + \fi + \lower\@@layerypos\hbox + {\ifdim\boxvdisplacement<\zeropoint + \lower-\boxvdisplacement\flushnextbox + \else + \flushnextbox + \fi}}% + % combine height and depth into depth only (later flushed as height) + \global\setbox\collectorbox\hbox + {\lower\ht\collectorbox\box\collectorbox}% + % just to be sure + \ifdim\wd\collectorbox<\@@layerxsiz + \global\wd\collectorbox\@@layerxsiz + \fi} + +\def\flushcollector[#1]% + {\ifundefined{\@@collectorbox#1}% + \writestatus{collector}{unknown collector #1}% + \else + \doifnotvalue{\??cb#1\c!state}\v!stop + {\vbox + {\hbox + {\doifelsevalue{\??cb#1\c!state}\v!repeat + {\let\next\copy}{\let\next\box}% + \raise\dp\csname\@@collectorbox#1\endcsname + \next\csname\@@collectorbox#1\endcsname}}}% + \fi} + +\def\composedcollector#1{\flushcollector[#1]} + +\def\resetcollector[#1]% + {\ifundefined{\@@collectorbox#1}\else + \global\setbox\csname\@@collectorbox#1\endcsname\emptybox + \fi} + +\def\adaptcollector + {\dodoubleargument\doadaptcollector} + +\def\doadaptcollector[#1][#2]% + {\bgroup + \def\currentcollector{#1}% + \mathchardef\collectorbox\csname\@@collectorbox#1\endcsname + \getparameters + [\??cb#1][\c!voffset=\zeropoint,\c!hoffset=\zeropoint,#2]% + \scratchdimen\wd\collectorbox + \advance\scratchdimen\collectorparameter\c!hoffset + \global\wd\collectorbox\scratchdimen + \scratchdimen\ht\collectorbox + \advance\scratchdimen\collectorparameter\c!voffset + \global\ht\collectorbox\scratchdimen + \egroup} + +%\definecollector[test] +%\setcollector[test] +% [location=rb] +% {\externalfigure[koe][frame=on,width=3cm]} +%\setcollector[test] +% [corner={right,bottom},location={left,top}] +% {\framed{gans}} +%\composedcollector{test} + +\definecollector + [caption] + +\def\collectedtext + {\dodoubleempty\docollectedtext} + +\def\docollectedtext[#1][#2]#3% + {\bgroup + \dowithnextbox + {\setcollector + [caption] + {\flushnextbox}% + \setcollector + [caption][#1] + {\getparameters[\??du][#2]% + \dosetfontattribute\??du\c!style\setupinterlinespace + \framed % watch the special setting of kader/overlay + [\c!frame=\v!overlay,#2] + {\doattributes\??du\c!style\c!color{#3}}}% + \composedcollector{caption}% + \egroup}% + \hbox} + +% \collectedtext +% [corner={right,bottom},location={left,top}] +% [background=color,backgroundcolor=white,offset=0pt] +% {gans} +% {\externalfigure[koe][width=3cm]} +% +% \collectedtext +% [rotation=90,corner={right,bottom},location={right,top}] +% [frame=on,offset=0pt] +% {gans} +% {\externalfigure[koe][width=3cm]} +% +% \collectedtext +% [rotation=90,corner={left,bottom},location={left,top}] +% [frame=on,offset=0pt] +% {gans} +% {\externalfigure[koe][width=3cm]} + +\definelayer + [caption] + +\def\layeredtext + {\dodoubleempty\dolayeredtext} + +\def\dolayeredtext[#1][#2]#3% + {\bgroup + \dowithnextbox + {\!!widtha \nextboxwd + \!!heighta\nextboxht + \bgroup % preserve \nextbox + \setuplayer + [caption] + [\c!width=\!!widtha,\c!height=\!!heighta]% + \setlayer + [caption] + [#1] + {\getparameters[\??du][#2]% + \dosetfontattribute\??du\c!style\setupinterlinespace + \framed + [\c!frame=\v!overlay,,#2] + {\doattributes\??du\c!style\c!color{#3}}}% + \egroup + \framed + [\c!offset=\v!overlay, + \c!frame=\v!off, + \c!background={\v!foreground,caption}, + \c!width=\!!widtha, + \c!height=\!!heighta] + {\flushnextbox}% + \egroup}% + \hbox} + +% \layeredtext +% [corner={right,bottom},location={left,top}] +% [background=color,backgroundcolor=white,offset=0pt] +% {gans} +% {\externalfigure[koe][width=3cm]} +% +% \layeredtext +% [rotation=90,corner={right,bottom},location={right,top}] +% [frame=on,offset=0pt] +% {gans} +% {\externalfigure[koe][width=3cm]} +% +% \layeredtext +% [rotation=90,corner={left,bottom},location={left,top}] +% [frame=on,offset=0pt] +% {gans} +% {\externalfigure[koe][width=3cm]} + +\def\ornamenttext + {\dodoubleempty\doornamenttext} + +\def\doornamenttext[#1][#2]% + {\bgroup + \doifassignmentelse{#1} + {\getparameters[\s!dummy][\c!alternative=\v!a,#1]% + \doifelse\dummyalternative\v!a + {\egroup\collectedtext}% + {\egroup\layeredtext }% + [#1][#2]}% + {\egroup\getvalue{#1}}} + +\def\defineornament + {\dotripleempty\dodefineornament} + +\def\dodefineornament[#1][#2][#3]% + {\setvalue{#1}{\doornamenttext[#2][#3]}} + +% \defineornament +% [affiliation] +% [rotation=90,corner={right,bottom},location={right,top}, +% hoffset=-.25ex] +% [frame=on,background=color,backgroundcolor=red,offset=0pt] +% +% \ruledhbox{\affiliation{gans}{\externalfigure[koe][width=3cm]}} +% +% \defineornament +% [affiliation] +% [rotation=90,corner={right,bottom},location={right,top}, +% hoffset=-.25ex,alternative=b] +% [frame=on,background=color,backgroundcolor=red,offset=0pt] +% +% \ruledhbox{\affiliation{gans}{\externalfigure[koe][width=3cm]}} +% +% \defineornament +% [affiliation] +% [rotation=90,corner={right,bottom},location={left,top}, +% hoffset=.25ex,voffset=.25ex,alternative=a] +% [background=color,style=\ss\tfxx,backgroundcolor=white,offset=0pt] +% +% \affiliation{photo}{\externalfigure[molen][width=3cm]} +% +% \defineornament +% [affiliation] +% [rotation=90,corner={right,bottom},location={left,top}, +% hoffset=.25ex,voffset=.25ex,alternative=b] +% [background=color,style=\ss\tfxx,backgroundcolor=white,offset=0pt] +% +% \affiliation{drawing}{\externalfigure[hakker][width=3cm]} + +% pas op: aanpassen aan nieuwe layer hoek ankers en columnset + +\newcount\nofbleeds % per pag + +\def\setupbleeding + {\dodoubleempty\getparameters[\??bg]} + +\setupbleeding + [\c!location=l, + \c!stretch=\v!yes, + \c!width=3cm, + \c!height=3cm, + \c!offset=2mm, + \c!page=\v!no, + \c!voffset=\@@bgoffset, + \c!hoffset=\@@bgoffset] + +\def\bleed + {\dosingleempty\dobleed} + + +\def\bleedwidth {\the\hsize}% +\def\bleedheight{\the\vsize}% + +\def\dobleed[#1]#2% + {\hbox\bgroup + \xdef\bleedwidth {\the\hsize}% + \xdef\bleedheight{\the\vsize}% + \global\advance\nofbleeds\plusone + \getparameters[\??bg][#1]% + \!!doneafalse % left + \!!donebfalse % right + \!!donecfalse % top + \!!donedfalse % bottom + % replace this part ! todo: default location + \processaction + [\@@bglocation] + [ t=>\!!donectrue\let\@@bghoffset\!!zeropoint, + b=>\!!donedtrue\let\@@bghoffset\!!zeropoint, + l=>\!!doneatrue\let\@@bgvoffset\!!zeropoint, + r=>\!!donebtrue\let\@@bgvoffset\!!zeropoint, + bl=>\!!doneatrue\!!donedtrue, + lb=>\!!doneatrue\!!donedtrue, + br=>\!!donebtrue\!!donedtrue, + rb=>\!!donebtrue\!!donedtrue, + tl=>\!!doneatrue\!!donectrue, + lt=>\!!doneatrue\!!donectrue, + tr=>\!!donebtrue\!!donectrue, + rt=>\!!donebtrue\!!donectrue]% + \doifelse\@@bgstretch\v!yes\donetrue\donefalse + \scratchdimen\@@bgwidth + \ifdone + \if!!donea + \advance\scratchdimen\MPx{\??bg:\number\nofbleeds}% + \else\if!!doneb + \scratchdimen\paperwidth + \advance\scratchdimen-\MPx{\??bg:\number\nofbleeds}% + \fi\fi + \fi + \advance\scratchdimen\@@bghoffset + \xdef\bleedwidth{\the\scratchdimen}% + \scratchdimen\@@bgheight + \ifdone + \if!!donec + \scratchdimen\paperheight + \advance\scratchdimen-\MPy{\??bg:\number\nofbleeds}% + \else\if!!doned + \advance\scratchdimen\MPy{\??bg:\number\nofbleeds}% + \fi\fi + \fi + \advance\scratchdimen\@@bgvoffset + \xdef\bleedheight{\the\scratchdimen}% + \hsize\bleedwidth + \vsize\bleedheight + \setbox\scratchbox\hbox{#2}% + \doif\@@bgpage\v!yes + {\setbox\scratchbox\topskippedbox{\box\scratchbox}}% + \setbox\scratchbox\hbox to \@@bgwidth + {\if!!donea\hss\fi\box\scratchbox\if!!doneb\hss\fi}% + \if!!doned + \setbox\scratchbox\hbox + {\lower\bleedheight\hbox{\raise\@@bgheight\box\scratchbox}}% + \fi + \wd\scratchbox\@@bgwidth + \ht\scratchbox\@@bgheight + \dp\scratchbox\zeropoint + \ifdone + \hpos{\??bg:\number\nofbleeds}{\box\scratchbox}% + \else + \box\scratchbox + \fi + \egroup} + +\setupbleeding[\c!stretch=\v!yes] + +\defineexternalfigure[bleed][\c!width=\bleedwidth,\c!height=\bleedheight] + +% \placefigure[left]{none} +% {\bleed[width=5cm,height=3cm,location=lt]{\externalfigure[koe][bleed]}} +% +% \input tufte +% +% \placefigure[left]{none} +% {\bleed[width=5cm,height=3cm,location=l]{\externalfigure[koe][bleed]}} +% +% \input tufte +% +% \placefigure[right]{none} +% {\bleed[width=5cm,height=3cm,location=r]{\externalfigure[koe][bleed]}} +% +% \input tufte +% +% \placesomefloat[right]{none} +% {\bleed[width=5cm,height=3cm,location=rb]{\externalfigure[koe][bleed]}} +% +% \input tufte +% +% \placefigure +% [top,none] +% {} % no caption +% {\bleed +% [hoffset=-\backspace, +% voffset=3mm, +% width=0cm, +% height=6\lineheight, +% page=yes, % correct for topskip +% location=lt] +% {\externalfigure[koe][bleed][frame=on]}} + +% \setlayerframed[layer id][layer settings][framed setting]{data} +% \setlayerframed[layer id][combined settings]{data} + +\def\setlayerframed + {\dotripleempty\dosetlayerframed} + +\def\dosetlayerframed + {\ifthirdargument + \expandafter\dosetlayerframedT + \else + \expandafter\dosetlayerframedS + \fi} + +\def\dosetlayerframedT[#1][#2][#3]% + {\dowithnextbox{\setlayer[#1][#2]{\flushnextbox}}% + \hbox\framed[#3]} + +\def\dosetlayerframedS[#1][#2][#3]% + {\dowithnextbox + {\setlayer + [#1] + [\c!width=\nextboxwd,\c!height=\nextboxht, + \c!offset=\!!zeropoint,#2] + {\flushnextbox}}% + \hbox\framed[\c!location=\v!normal,#2]} + +\def\setlayertext + {\dotripleempty\dosetlayertext} + +\def\dosetlayertext[#1][#2][#3]% + {\bgroup + \getparameters + [\??lx] + [\c!align=, + \c!width=\hsize, + \c!color=, + \c!style=, + #3]% + \dowithnextboxcontent + {\forgetall + \hsize\@@lxwidth + \expanded{\setupalign[\@@lxalign]}% + \dosetfontattribute\??lx\c!style} + {\setlayer[#1][#2]{\strut\color[\@@lxcolor]{\flushnextbox}}% + \egroup}% + \vtop} + +% \setupbackgrounds +% [page] +% [background=pagefigures] +% +% \definelayer +% [pagefigures] +% [x=-2mm, +% y=-2mm, +% width=\paperwidth, +% height=\paperheight] +% +% \definelayerpreset [lefttop] [corner={left,top},location={right,bottom}] +% \definelayerpreset [righttop] [corner={right,top},location={left,bottom}] +% \definelayerpreset [leftbottom] [corner={left,bottom},location={right,top}] +% \definelayerpreset [rightbottom] [corner={right,bottom},location={left,top}] +% \definelayerpreset [middle] [corner=middle,location=middle] +% +% \setlayer[pagefigures][preset=lefttop] +% \setlayer[pagefigures][preset=righttop] +% \setlayer[pagefigures][preset=leftbottom] +% \setlayer[pagefigures][preset=rightbottom] + +\definelayerpreset + [\v!left\v!top] + [\c!corner={\v!left,\v!top},\c!location={\v!right,\v!bottom}] + +\definelayerpreset + [\v!right\v!top] + [\c!corner={\v!right,\v!top},\c!location={\v!left,\v!bottom}] + +\definelayerpreset + [\v!left\v!bottom] + [\c!corner={\v!left,\v!bottom},\c!location={\v!right,\v!top}] + +\definelayerpreset + [\v!right\v!bottom] + [\c!corner={\v!right,\v!bottom},\c!location={\v!left,\v!top}] + +\definelayerpreset + [\v!middle] + [\c!corner=\v!middle,\c!location=\v!middle] + +% \definelayerpreset +% [\v!middle\v!top] +% [\c!location=\v!bottom,\c!hoffset=.5\layerwidth] + +% \definelayerpreset +% [\v!middle\v!bottom] +% [\c!location=\v!top,\c!hoffset=.5\layerwidth,\c!voffset=\layerheight] + +% \definelayerpreset +% [\v!middle\v!left] +% [\c!location=\v!right,\c!voffset=.5\layerheight] + +% \definelayerpreset +% [\v!middle\v!right] +% [\c!location=\v!left,\c!hoffset=\layerwidth,\c!voffset=.5\layerheight] + +\definelayerpreset + [\v!middle\v!top] + [\c!location=\v!bottom,\c!corner=\v!top,\c!dx=.5\layerwidth] + +\definelayerpreset + [\v!middle\v!bottom] + [\c!location=\v!top,\c!corner=\v!bottom,\c!dx=.5\layerwidth] + +\definelayerpreset + [\v!middle\v!left] + [\c!location=\v!right,\c!corner=\v!left,\c!dy=.5\layerheight] + +\definelayerpreset + [\v!middle\v!right] + [\c!location=\v!left,\c!corner=\v!right,\c!dy=.5\layerheight] + +\def\alignedbox + {\dodoubleempty\doalignedbox[]} + +% \def\doalignedbox[#1][#2]% +% {\bgroup +% %\let\iftraceboxplacement\iftracelayers % ugly +% \dowithnextbox +% {\let\next\middlebox +% \processaction +% [#2] +% [ t=>\let\next\topbox , b=>\let\next\bottombox , +% l=>\let\next\leftbox , r=>\let\next\rightbox , +% bl=>\let\next\bottomleftbox,br=>\let\next\bottomrightbox, +% tl=>\let\next\topleftbox ,tr=>\let\next\toprightbox , +% lt=>\let\next\lefttopbox ,lb=>\let\next\leftbottombox , +% rt=>\let\next\righttopbox ,rb=>\let\next\rightbottombox]% +% \next{\flushnextbox}% +% \egroup}#1} + +\def\doalignedbox[#1][#2]% + {\bgroup + %\let\iftraceboxplacement\iftracelayers % ugly + \dowithnextbox + {\serializecommalist[#2]% + \executeifdefined{\??ab\??ab\serializedcommalist}\middlebox{\flushnextbox}% + \egroup}#1} + +\setvalue{\??ab\??ab }{\middlebox} +\setvalue{\??ab\??ab\v!middle }{\middlebox} +\setvalue{\??ab\??ab\v!left }{\leftbox } +\setvalue{\??ab\??ab\v!right }{\rightbox } +\setvalue{\??ab\??ab\v!bottom }{\bottombox} +\setvalue{\??ab\??ab\v!top }{\topbox } + +\setvalue{\??ab\??ab\v!middle\v!middle}{\middlebox} +\setvalue{\??ab\??ab\v!left \v!top }{\lefttopbox} +\setvalue{\??ab\??ab\v!left \v!bottom}{\leftbottombox} +\setvalue{\??ab\??ab\v!right \v!top }{\righttopbox} +\setvalue{\??ab\??ab\v!right \v!bottom}{\rightbottombox} +\setvalue{\??ab\??ab\v!top \v!left }{\topleftbox} +\setvalue{\??ab\??ab\v!bottom\v!left }{\bottomleftbox} +\setvalue{\??ab\??ab\v!top \v!right }{\toprightbox} +\setvalue{\??ab\??ab\v!bottom\v!right }{\bottomrightbox} + +\setvalue{\??ab\??ab c}{\middlebox} +\setvalue{\??ab\??ab l}{\leftbox} +\setvalue{\??ab\??ab r}{\rightbox} +\setvalue{\??ab\??ab b}{\bottombox} +\setvalue{\??ab\??ab t}{\topbox} + +\setvalue{\??ab\??ab lt}{\lefttopbox} +\setvalue{\??ab\??ab lb}{\leftbottombox} +\setvalue{\??ab\??ab rt}{\righttopbox} +\setvalue{\??ab\??ab rb}{\rightbottombox} +\setvalue{\??ab\??ab tl}{\topleftbox} +\setvalue{\??ab\??ab bl}{\bottomleftbox} +\setvalue{\??ab\??ab tr}{\toprightbox} +\setvalue{\??ab\??ab br}{\bottomrightbox} + +\setvalue{\??ab\??ab m}{\middlebox} + +% The next ones were desparately needed by Vit Zyka (see +% \type {supp-box} for definitions). + +\setvalue{\??ab\??ab g}{\baselinemiddlebox} +\setvalue{\??ab\??ab gl}{\baselineleftbox} +\setvalue{\??ab\??ab gc}{\baselinemiddlebox} +\setvalue{\??ab\??ab gr}{\baselinerightbox} + +\setvalue{\??ab\??ab \v!line }{\baselinemiddlebox} % \v!grid is taken +\setvalue{\??ab\??ab \v!line\v!left }{\baselineleftbox} +\setvalue{\??ab\??ab \v!line\v!middle}{\baselinemiddlebox} +\setvalue{\??ab\??ab \v!line\v!right }{\baselinerightbox} + +\unexpanded\def\offsetbox + {\dodoubleempty\dooffsetbox[]} + +% left/right/top/bottomoffset -> dimensions change +% x/y | method=fixed -> dimensions don't change + +\def\dooffsetbox[#1][#2]% + {\bgroup + \dowithnextbox + {\getparameters[\??ox] + [\c!x=\zeropoint, + \c!y=\zeropoint, + \c!width=\nextboxwd, + \c!height=\nextboxht, + \c!depth=\nextboxdp, + \c!location=, + \c!leftoffset=\zeropoint, + \c!rightoffset=\zeropoint, + \c!topoffset=\zeropoint, + \c!bottomoffset=\zeropoint, + \c!method=, + #2]% + \donefalse + \ifdim\@@oxleftoffset =\zeropoint\else\donetrue\fi + \ifdim\@@oxrightoffset=\zeropoint\else\donetrue\fi + \ifdim\@@oxtopoffset =\zeropoint\else\donetrue\fi + \ifdim\@@oxbottomoffset =\zeropoint\else\donetrue\fi + \ifdone + \doif\@@oxmethod\v!fixed % new + {\ifdim\@@oxleftoffset=\zeropoint + \ifdim\@@oxrightoffset=\zeropoint \else + \scratchdimen-\@@oxrightoffset + \edef\@@oxx{\the\scratchdimen}% + \let\@@oxrightoffset\zeropoint + \fi + \else + \let\@@oxx\@@oxleftoffset + \let\@@oxleftoffset\zeropoint + \fi + \ifdim\@@oxtopoffset=\zeropoint + \ifdim\@@oxbottomoffset=\zeropoint \else + \scratchdimen-\@@oxbottomoffset + \edef\@@oxy{\the\scratchdimen}% + \let\@@oxbottomoffset\zeropoint + \fi + \else + \let\@@oxy\@@oxtopoffset + \let\@@oxtopoffset\zeropoint + \fi + \donefalse}% + \fi + \ifdone + \setbox\nextbox\vbox + {\forgetall\offinterlineskip + \vskip\@@oxtopoffset + \hbox + {\hskip\@@oxleftoffset + \flushnextbox + \hskip\@@oxrightoffset}% + \vskip\@@oxbottomoffset}% + \scratchdimen\nextboxht + \advance\scratchdimen\nextboxdp + \nextboxht\scratchdimen + \nextboxdp\zeropoint + \fi + \freezedimenmacro\@@oxwidth + \freezedimenmacro\@@oxheight + \freezedimenmacro\@@oxdepth + \setbox\nextbox\hbox + {\hskip\@@oxx\lower\@@oxy\hbox + {\doifelsenothing\@@oxlocation + {\flushnextbox} + {\alignedbox[\@@oxlocation]\hbox{\flushnextbox}}}}% + \nextboxwd\@@oxwidth + \nextboxht\@@oxheight + \nextboxdp\@@oxdepth + \flushnextbox + \egroup}#1} + +% \useMPlibrary[pre] \setupbackgrounds[page][background=pagegrid] +% +% \placefigure[left,none]{}{\offset[leftoffset=1cm]{\externalfigure[koe][breedte=3cm]}} +% \input tufte +% \placefigure[left,none]{}{\offset[rightoffset=1cm]{\externalfigure[koe][breedte=3cm]}} +% \input tufte +% \placefigure[left,none]{}{\offset[topoffset=1cm]{\externalfigure[koe][breedte=3cm]}} +% \input tufte +% \placefigure[left,none]{}{\offset[bottomoffset=1cm]{\externalfigure[koe][breedte=3cm]}} +% \input tufte + +\unexpanded\def\offset {\dodoubleempty\dooffsetbox [\hbox]} % yes or no +\unexpanded\def\aligned{\dosingleempty\doalignedbox[\hbox]} % yes or no + +%\ruledhbox{\offsetbox[x=-1cm,y=-1cm,location=c] +% {\framed[width=4cm,height=4cm]{x}}} + +\def\dotabbed#1#2#3#4% + {\dontleavehmode + \bgroup + \setbox\scratchbox\hbox{#3}% + \hbox to \wd\scratchbox{#1#4#2}% + \egroup} + +\unexpanded\def\ltabbed{\dotabbed\relax\hss} +\unexpanded\def\rtabbed{\dotabbed\hss \relax} +\unexpanded\def\ctabbed{\dotabbed\hss \hss} \let\mtabbed\ctabbed + +% \ltabbed{\romeins{3}}{\romeins{1}} test \endgraf +% \ltabbed{\romeins{3}}{\romeins{2}} test \endgraf +% \ltabbed{\romeins{3}}{\romeins{3}} test \endgraf +% +% \rtabbed{\romeins{3}}{\romeins{1}} test \endgraf +% \rtabbed{\romeins{3}}{\romeins{2}} test \endgraf +% \rtabbed{\romeins{3}}{\romeins{3}} test \endgraf +% +% \ctabbed{\romeins{3}}{\romeins{1}} test \endgraf +% \ctabbed{\romeins{3}}{\romeins{2}} test \endgraf +% \ctabbed{\romeins{3}}{\romeins{3}} test \endgraf + +% alternative, if done, then other name +% +% \def\dotabbed#1#2#3#4% +% {\dontleavehmode +% \bgroup +% \scratchdimen\zeropoint +% \def\docommand##1% +% {\setbox\scratchbox\hbox{##1}% +% \ifdim\wd\scratchbox>\scratchdimen +% \scratchdimen\wd\scratchbox +% \fi}% +% \processcommalist[#3]\docommand +% \hbox to \scratchdimen{#1#4#2}% +% \egroup} +% +% \def\ltabbed{\dotabbed\relax\hss} +% \def\rtabbed{\dotabbed\hss \relax} +% \def\ctabbed{\dotabbed\hss \hss} \let\mtabbed\ctabbed +% +% \ltabbed{\romeins{10},\romeins{2000},\romeins{15}}{\romeins{10}} test \endgraf +% \ltabbed{\romeins{10},\romeins{2000},\romeins{15}}{\romeins{15}} test \endgraf +% \ltabbed{\romeins{10},\romeins{2000},\romeins{15}}{\romeins{2000}} test \endgraf +% +% \rtabbed{\romeins{10},\romeins{2000},\romeins{15}}{\romeins{10}} test \endgraf +% \rtabbed{\romeins{10},\romeins{2000},\romeins{15}}{\romeins{15}} test \endgraf +% \rtabbed{\romeins{10},\romeins{2000},\romeins{15}}{\romeins{2000}} test \endgraf +% +% \ctabbed{\romeins{10},\romeins{2000},\romeins{15}}{\romeins{10}} test \endgraf +% \ctabbed{\romeins{10},\romeins{2000},\romeins{15}}{\romeins{15}} test \endgraf +% \ctabbed{\romeins{10},\romeins{2000},\romeins{15}}{\romeins{2000}} test \endgraf + +% to be documented + +\unexpanded\def\phantombox[#1]% + {\hbox\bgroup + \getparameters + [\??ol] + [\c!width=\zeropoint,% + \c!height=\zeropoint,% + \c!depth=\zeropoint,#1]% + \setbox\scratchbox\null + \wd\scratchbox\@@olwidth + \ht\scratchbox\@@olheight + \dp\scratchbox\@@oldepth + \box\scratchbox + \egroup} + +% \backgroundimage{1}{\hsize}{\vsize}{\externalfigure[cow][\c!width=3cm]} + +\unexpanded\def\backgroundimage#1#2#3% repeat hsize vsize + {\bgroup + \forgetall + \dowithnextbox + {\offinterlineskip + \ifcase#1\relax + % just one + \else + \scratchdimen#2\divide\scratchdimen\nextboxwd\count0\scratchdimen\advance\count0\plusone + \scratchdimen#3\divide\scratchdimen\nextboxht\count2\scratchdimen\advance\count2\plusone + % to be considered, probably methods + \ifcase#1\or % x and y + \setbox\nextbox\hbox{\dorecurse{\count0}{\copy\nextbox}}% + \setbox\nextbox\vbox{\dorecurse{\count2}{\copy\nextbox\endgraf}}% + \or % x + \setbox\nextbox\hbox{\dorecurse{\count0}{\copy\nextbox}}% + \or % y + \setbox\nextbox\vbox{\dorecurse{\count2}{\copy\nextbox\endgraf}}% + \fi + \fi + \ifdim\nextboxwd>#2\relax + \setbox\nextbox\hbox to #2{\hss\flushnextbox\hss}% + \setbox\nextbox\hbox{\expanded{\clip[\c!width=#2,\c!height=\the\nextboxht]{\flushnextbox}}}% + \fi + \ifdim\nextboxht>#3\relax + \setbox\nextbox\vbox to #3{\vss\flushnextbox\vss}% + \setbox\nextbox\hbox{\expanded{\clip[\c!width=\the\nextboxwd,\c!height=#3]{\flushnextbox}}}% + \fi + \flushnextbox + \egroup}% + \hbox} + +\protect \endinput diff --git a/tex/context/base/pack-box.mkiv b/tex/context/base/pack-box.mkiv new file mode 100644 index 000000000..6cb492aa6 --- /dev/null +++ b/tex/context/base/pack-box.mkiv @@ -0,0 +1,944 @@ +%D \module +%D [ file=pack-box, +%D version=2002.04.12, +%D title=\CONTEXT\ Packaging Macros, +%D subtitle=Boxes, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Packaging Macros / Boxes} + +%D This module contains all kind of macros for moving content +%D around. Many macros here come from other modules, but +%D depencies made it more clear to isolate them. + +% \placeornament + +\unprotect + +% \definelayer[\v!tekst-2][\c!positie=\v!ja] +% \definelayer[\v!tekst-1][\c!positie=\v!ja] +% \definelayer[\v!tekst+1][\c!positie=\v!ja] +% \definelayer[\v!tekst+2][\c!positie=\v!ja] + +% we need to set the size, else we get dimensions depending +% on the content, which in itsel fis ok, but can lead to loops +% due to rounding errors (happened in demo-obv) + +\definelayer[\v!text-2][\c!position=\v!yes,\c!width=\overlaywidth,\c!height=\overlayheight] +\definelayer[\v!text-1][\c!position=\v!yes,\c!width=\overlaywidth,\c!height=\overlayheight] +\definelayer[\v!text+1][\c!position=\v!yes,\c!width=\overlaywidth,\c!height=\overlayheight] +\definelayer[\v!text+2][\c!position=\v!yes,\c!width=\overlaywidth,\c!height=\overlayheight] + +\def\internaltextoverlay#1% will become more generic and installable + {\startoverlay % i.e. probably an overlay by itself + {\positionoverlay{\v!text#1}} % see later + {\composedlayer {\v!text#1}} + \stopoverlay} + +%\def\internaltextoverlay#1% +% {\hbox to \zeropoint{\positionoverlay{\v!tekst#1}\hss}% +% \composedlayer{\v!tekst#1}} + +% todo: share info, so that tuo will be smaller + +\defineoverlay[\v!text-2][\internaltextoverlay{-2}] +\defineoverlay[\v!text-1][\internaltextoverlay{-1}] +\defineoverlay[\v!text+1][\internaltextoverlay{+1}] +\defineoverlay[\v!text+2][\internaltextoverlay{+2}] + +% to be documented + +\definelayer[anchor] + +\def\anchor + {\dosingleargument\doanchor} + +\def\doanchor[#1]% + {\ifcsname\??an#1\endcsname\@EA\nonoanchor\else\@EA\dodoanchor\fi[#1]} + +\def\nonoanchor[#1]% + {} + +\def\dodoanchor[#1]% + {\dotripleempty\dododoanchor[#1]} + +\def\dododoanchor + {\ifthirdargument + \expandafter\dodoanchorT + \else + \expandafter\dodoanchorS + \fi} + +\def\dodoanchorS[#1][#2][#3]% + {\dodoanchorT[#1][#2][#2]} + +\def\dodoanchorT[#1][#2][#3]% + {\dowithnextbox + {\bgroup + \checktextbackgrounds + \setbox\scratchbox\null + \wd\scratchbox\nextboxwd + \ht\scratchbox\nextboxht + \dp\scratchbox\nextboxdp + \setlayer + [anchor] + [\c!width=\wd\scratchbox, + \c!height=\ht\scratchbox, + \c!offset=\!!zeropoint, + #2,#3] + {\setlayer[#1]{\flushnextbox}}% + \framed + [#2, + \c!background=anchor, + \c!offset=\v!overlay, + \c!frame=\v!off, + #3] + {\box\scratchbox}% + \egroup}% + \vbox} + +\unexpanded\def\defineanchor + {\doquadrupleempty\dodefineanchor} + +\def\dodefineanchor[#1][#2][#3][#4]% + {\setvalue{\??an#1}{\dodefinedanchor[#2][#3][#4]}} + +\def\dodefinedanchor[#1][#2][#3]% + {\def\docommand[##1][##2]% + {\ifsecondargument + \def\next{\dodoanchorT[#1][#2,##1][#3,##2]}% + \else\iffirstargument + \def\next{\dodoanchorT[#1][#2,##1][#2,##1]}% + \else + \def\next{\dodoanchorT[#1][#2][#3]}% + \fi\fi + \next}% + \dodoubleempty\docommand} + +\def\@@collectorbox{@@collectorbox} + +\unexpanded\def\definecollector + {\dodoubleargument\dodefinecollector} + +\def\dodefinecollector[#1][#2]% + {\ifcsname\@@collectorbox#1\endcsname \else + \expandafter\newbox\csname\@@collectorbox#1\endcsname + \fi + \resetcollector[#1]% + \setupcollector + [#1] + [\c!state=\v!start, + \c!x=\!!zeropoint,\c!y=\!!zeropoint, + \c!offset=\!!zeropoint,\c!rotation=, % geen 0 ! + \c!hoffset=\!!zeropoint,\c!voffset=\!!zeropoint, + \c!location=rb,\c!corner=,#2]} + +\unexpanded\def\setupcollector + {\dodoubleargument\dosetupcollector} + +\def\dosetupcollector[#1][#2]% + {\def\docommand##1{\getparameters[\??cb##1][#2]}% + \processcommalist[#1]\docommand} + +\def\setcollector + {\dodoubleargument\dosetcollector} + +\def\dosetcollector[#1][#2]% + {\bgroup + \forgetall + \dontcomplain + \dowithnextbox + {\ifcsname\@@collectorbox#1\endcsname + \dodosetcollector[#1][#2]% + \else + \writestatus{collector}{unknown layer #1}% + \fi + \egroup} + \hbox} + +\def\collectorparameter#1{\csname\??cb\currentcollector#1\endcsname} + +\def\dodosetcollector[#1][#2]% todo: keep reference point + {\def\currentcollector{#1}% + \chardef\collectorbox\csname\@@collectorbox#1\endcsname + \getparameters[\??cb#1][#2]% + \@@layerxsiz\wd\collectorbox + \@@layerysiz\ht\collectorbox + \doifvaluesomething{\??cb#1\c!rotation} + {\setbox\nextbox\hbox + {\rotate + [\c!location=\v!high, + \c!rotation=\collectorparameter\c!rotation] + {\flushnextbox}}}% + \advance\@@layerysiz\dp\collectorbox + \@@layerxpos\collectorparameter\c!x + \advance\@@layerxpos\collectorparameter\c!hoffset + \@@layerypos\collectorparameter\c!y + \advance\@@layerypos\collectorparameter\c!voffset + \doifelse\v!middle{\collectorparameter\c!corner} + {\ifdim\@@layerxsiz>\zeropoint + \advance\@@layerxpos.5\@@layerxsiz + \fi + \ifdim\@@layerysiz>\zeropoint + \advance\@@layerypos.5\@@layerysiz + \fi}% + {\normalexpanded{\noexpand\doifinset{\v!bottom}{\collectorparameter\c!corner}} + {\ifdim\@@layerysiz>\zeropoint + \advance\@@layerypos-\@@layerysiz + \@@layerypos-\@@layerypos + \fi}% + \normalexpanded{\noexpand\doifinset{\v!right}{\collectorparameter\c!corner}} + {\ifdim\@@layerxsiz>\zeropoint + \advance\@@layerxpos-\@@layerxsiz + \@@layerxpos-\@@layerxpos + \fi}}% + \setbox\nextbox\hbox + {\alignedbox[\collectorparameter\c!location]\vbox{\flushnextbox}}% + \boxmaxdepth\zeropoint % really needed, nice example + \global\advance\boxhdisplacement\@@layerxpos + \ifdim\boxhdisplacement<\zeropoint + \global\setbox\collectorbox\hbox + {\hskip-\boxhdisplacement + \box\collectorbox}% + \fi + \global\advance\boxvdisplacement\@@layerypos + \ifdim\boxvdisplacement<\zeropoint + \global\setbox\collectorbox\hbox + {\lower-\boxvdisplacement + \box\collectorbox}% + \fi + \@@layerxsiz\wd\collectorbox + \@@layerysiz\ht\collectorbox + \advance\@@layerysiz\dp\collectorbox + \global\setbox\collectorbox\hbox + {\box\collectorbox + \hskip-\@@layerxsiz + \hskip\@@layerxpos\relax + \ifdim\boxhdisplacement<\zeropoint + \hskip-\boxhdisplacement + \fi + \lower\@@layerypos\hbox + {\ifdim\boxvdisplacement<\zeropoint + \lower-\boxvdisplacement\flushnextbox + \else + \flushnextbox + \fi}}% + % combine height and depth into depth only (later flushed as height) + \global\setbox\collectorbox\hbox + {\lower\ht\collectorbox\box\collectorbox}% + % just to be sure + \ifdim\wd\collectorbox<\@@layerxsiz + \global\wd\collectorbox\@@layerxsiz + \fi} + +\def\flushcollector[#1]% + {\ifcsname\@@collectorbox#1\endcsname + \doifnotvalue{\??cb#1\c!state}\v!stop + {\vbox + {\hbox + {\doifelsevalue{\??cb#1\c!state}\v!repeat + {\let\next\copy}{\let\next\box}% + \raise\dp\csname\@@collectorbox#1\endcsname + \next\csname\@@collectorbox#1\endcsname}}}% + \else + \writestatus{collector}{unknown collector #1}% + \fi} + +\def\composedcollector#1{\flushcollector[#1]} + +\def\resetcollector[#1]% + {\ifcsname\@@collectorbox#1\endcsname + \global\setbox\csname\@@collectorbox#1\endcsname\emptybox + \fi} + +\def\adaptcollector + {\dodoubleargument\doadaptcollector} + +\def\doadaptcollector[#1][#2]% + {\bgroup + \def\currentcollector{#1}% + \chardef\collectorbox\csname\@@collectorbox#1\endcsname + \getparameters[\??cb#1][\c!voffset=\zeropoint,\c!hoffset=\zeropoint,#2]% + \scratchdimen\wd\collectorbox + \advance\scratchdimen\collectorparameter\c!hoffset + \global\wd\collectorbox\scratchdimen + \scratchdimen\ht\collectorbox + \advance\scratchdimen\collectorparameter\c!voffset + \global\ht\collectorbox\scratchdimen + \egroup} + +%\definecollector[test] +%\setcollector[test] +% [location=rb] +% {\externalfigure[koe][frame=on,width=3cm]} +%\setcollector[test] +% [corner={right,bottom},location={left,top}] +% {\framed{gans}} +%\composedcollector{test} + +\definecollector + [caption] + +\def\collectedtext + {\dodoubleempty\docollectedtext} + +\def\docollectedtext[#1][#2]#3% + {\bgroup + \dowithnextbox + {\setcollector + [caption] + {\flushnextbox}% + \setcollector + [caption][#1] + {\getparameters[\??du][#2]% + \dosetfontattribute\??du\c!style\setupinterlinespace + \framed % watch the special setting of kader/overlay + [\c!frame=\v!overlay,#2] + {\doattributes\??du\c!style\c!color{#3}}}% + \composedcollector{caption}% + \egroup}% + \hbox} + +% \collectedtext +% [corner={right,bottom},location={left,top}] +% [background=color,backgroundcolor=white,offset=0pt] +% {gans} +% {\externalfigure[koe][width=3cm]} +% +% \collectedtext +% [rotation=90,corner={right,bottom},location={right,top}] +% [frame=on,offset=0pt] +% {gans} +% {\externalfigure[koe][width=3cm]} +% +% \collectedtext +% [rotation=90,corner={left,bottom},location={left,top}] +% [frame=on,offset=0pt] +% {gans} +% {\externalfigure[koe][width=3cm]} + +\definelayer + [caption] + +\def\layeredtext + {\dodoubleempty\dolayeredtext} + +\def\dolayeredtext[#1][#2]#3% + {\bgroup + \dowithnextbox + {\!!widtha \nextboxwd + \!!heighta\nextboxht + \bgroup % preserve \nextbox + \setuplayer + [caption] + [\c!width=\!!widtha,\c!height=\!!heighta]% + \setlayer + [caption] + [#1] + {\getparameters[\??du][#2]% + \dosetfontattribute\??du\c!style\setupinterlinespace + \framed + [\c!frame=\v!overlay,,#2] + {\doattributes\??du\c!style\c!color{#3}}}% + \egroup + \framed + [\c!offset=\v!overlay, + \c!frame=\v!off, + \c!background={\v!foreground,caption}, + \c!width=\!!widtha, + \c!height=\!!heighta] + {\flushnextbox}% + \egroup}% + \hbox} + +% \layeredtext +% [corner={right,bottom},location={left,top}] +% [background=color,backgroundcolor=white,offset=0pt] +% {gans} +% {\externalfigure[koe][width=3cm]} +% +% \layeredtext +% [rotation=90,corner={right,bottom},location={right,top}] +% [frame=on,offset=0pt] +% {gans} +% {\externalfigure[koe][width=3cm]} +% +% \layeredtext +% [rotation=90,corner={left,bottom},location={left,top}] +% [frame=on,offset=0pt] +% {gans} +% {\externalfigure[koe][width=3cm]} + +\def\ornamenttext + {\dodoubleempty\doornamenttext} + +\def\doornamenttext[#1][#2]% + {\bgroup + \doifassignmentelse{#1} + {\getparameters[\s!dummy][\c!alternative=\v!a,#1]% + \doifelse\dummyalternative\v!a + {\egroup\collectedtext}% + {\egroup\layeredtext }% + [#1][#2]}% + {\egroup\getvalue{#1}}} + +\unexpanded\def\defineornament + {\dotripleempty\dodefineornament} + +\def\dodefineornament[#1][#2][#3]% + {\setvalue{#1}{\doornamenttext[#2][#3]}} + +% \defineornament +% [affiliation] +% [rotation=90,corner={right,bottom},location={right,top}, +% hoffset=-.25ex] +% [frame=on,background=color,backgroundcolor=red,offset=0pt] +% +% \ruledhbox{\affiliation{gans}{\externalfigure[koe][width=3cm]}} +% +% \defineornament +% [affiliation] +% [rotation=90,corner={right,bottom},location={right,top}, +% hoffset=-.25ex,alternative=b] +% [frame=on,background=color,backgroundcolor=red,offset=0pt] +% +% \ruledhbox{\affiliation{gans}{\externalfigure[koe][width=3cm]}} +% +% \defineornament +% [affiliation] +% [rotation=90,corner={right,bottom},location={left,top}, +% hoffset=.25ex,voffset=.25ex,alternative=a] +% [background=color,style=\ss\tfxx,backgroundcolor=white,offset=0pt] +% +% \affiliation{photo}{\externalfigure[molen][width=3cm]} +% +% \defineornament +% [affiliation] +% [rotation=90,corner={right,bottom},location={left,top}, +% hoffset=.25ex,voffset=.25ex,alternative=b] +% [background=color,style=\ss\tfxx,backgroundcolor=white,offset=0pt] +% +% \affiliation{drawing}{\externalfigure[hakker][width=3cm]} + +% pas op: aanpassen aan nieuwe layer hoek ankers en columnset + +\newcount\nofbleeds % per pag + +\unexpanded\def\setupbleeding + {\dodoubleempty\getparameters[\??bg]} + +\setupbleeding + [\c!location=l, + \c!stretch=\v!yes, + \c!width=3cm, + \c!height=3cm, + \c!offset=2mm, + \c!page=\v!no, + \c!voffset=\@@bgoffset, + \c!hoffset=\@@bgoffset] + +\def\bleed + {\dosingleempty\dobleed} + + +\def\bleedwidth {\the\hsize}% +\def\bleedheight{\the\vsize}% + +\def\dobleed[#1]#2% + {\hbox\bgroup + \xdef\bleedwidth {\the\hsize}% + \xdef\bleedheight{\the\vsize}% + \global\advance\nofbleeds\plusone + \getparameters[\??bg][#1]% + \!!doneafalse % left + \!!donebfalse % right + \!!donecfalse % top + \!!donedfalse % bottom + % replace this part ! todo: default location + \processaction + [\@@bglocation] + [ t=>\!!donectrue\let\@@bghoffset\!!zeropoint, + b=>\!!donedtrue\let\@@bghoffset\!!zeropoint, + l=>\!!doneatrue\let\@@bgvoffset\!!zeropoint, + r=>\!!donebtrue\let\@@bgvoffset\!!zeropoint, + bl=>\!!doneatrue\!!donedtrue, + lb=>\!!doneatrue\!!donedtrue, + br=>\!!donebtrue\!!donedtrue, + rb=>\!!donebtrue\!!donedtrue, + tl=>\!!doneatrue\!!donectrue, + lt=>\!!doneatrue\!!donectrue, + tr=>\!!donebtrue\!!donectrue, + rt=>\!!donebtrue\!!donectrue]% + \doifelse\@@bgstretch\v!yes\donetrue\donefalse + \scratchdimen\@@bgwidth + \ifdone + \if!!donea + \advance\scratchdimen\MPx{\??bg:\number\nofbleeds}% + \else\if!!doneb + \scratchdimen\paperwidth + \advance\scratchdimen-\MPx{\??bg:\number\nofbleeds}% + \fi\fi + \fi + \advance\scratchdimen\@@bghoffset + \xdef\bleedwidth{\the\scratchdimen}% + \scratchdimen\@@bgheight + \ifdone + \if!!donec + \scratchdimen\paperheight + \advance\scratchdimen-\MPy{\??bg:\number\nofbleeds}% + \else\if!!doned + \advance\scratchdimen\MPy{\??bg:\number\nofbleeds}% + \fi\fi + \fi + \advance\scratchdimen\@@bgvoffset + \xdef\bleedheight{\the\scratchdimen}% + \hsize\bleedwidth + \vsize\bleedheight + \setbox\scratchbox\hbox{#2}% + \doif\@@bgpage\v!yes + {\setbox\scratchbox\topskippedbox{\box\scratchbox}}% + \setbox\scratchbox\hbox to \@@bgwidth + {\if!!donea\hss\fi\box\scratchbox\if!!doneb\hss\fi}% + \if!!doned + \setbox\scratchbox\hbox + {\lower\bleedheight\hbox{\raise\@@bgheight\box\scratchbox}}% + \fi + \wd\scratchbox\@@bgwidth + \ht\scratchbox\@@bgheight + \dp\scratchbox\zeropoint + \ifdone + \hpos{\??bg:\number\nofbleeds}{\box\scratchbox}% + \else + \box\scratchbox + \fi + \egroup} + +\setupbleeding[\c!stretch=\v!yes] + +\defineexternalfigure[bleed][\c!width=\bleedwidth,\c!height=\bleedheight] + +% \placefigure[left]{none} +% {\bleed[width=5cm,height=3cm,location=lt]{\externalfigure[koe][bleed]}} +% +% \input tufte +% +% \placefigure[left]{none} +% {\bleed[width=5cm,height=3cm,location=l]{\externalfigure[koe][bleed]}} +% +% \input tufte +% +% \placefigure[right]{none} +% {\bleed[width=5cm,height=3cm,location=r]{\externalfigure[koe][bleed]}} +% +% \input tufte +% +% \placefigure +% [top,none] +% {} % no caption +% {\bleed +% [hoffset=-\backspace, +% voffset=3mm, +% width=0cm, +% height=6\lineheight, +% page=yes, % correct for topskip +% location=lt] +% {\externalfigure[koe][bleed][frame=on]}} + +% \setlayerframed[layer id][layer settings][framed setting]{data} +% \setlayerframed[layer id][combined settings]{data} + +\def\setlayerframed + {\dotripleempty\dosetlayerframed} + +\def\dosetlayerframed + {\ifthirdargument + \expandafter\dosetlayerframedT + \else + \expandafter\dosetlayerframedS + \fi} + +\def\dosetlayerframedT[#1][#2][#3]% + {\dowithnextbox{\setlayer[#1][#2]{\flushnextbox}}% + \hbox\framed[#3]} + +\def\dosetlayerframedS[#1][#2][#3]% + {\dowithnextbox % we could use a local setlayer command (no doif..empty) which also saves a nextbox + {\setlayer[#1][\c!width=\nextboxwd,\c!height=\nextboxht,\c!offset=\!!zeropoint,#2]{\flushnextbox}}% + \hbox\framed[\c!location=\v!normal,#2]} + +\def\setlayertext + {\dotripleempty\dosetlayertext} + +\def\dosetlayertext[#1][#2][#3]% + {\bgroup + \getparameters + [\??lx] + [\c!align=, + \c!width=\hsize, + \c!color=, + \c!style=, + #3]% + \dowithnextboxcontent + {\forgetall + \hsize\@@lxwidth + \expanded{\setupalign[\@@lxalign]}% + \dosetfontattribute\??lx\c!style} + {\setlayer[#1][#2]{\strut\color[\@@lxcolor]{\flushnextbox}}% + \egroup}% + \vtop} + +% \setupbackgrounds +% [page] +% [background=pagefigures] +% +% \definelayer +% [pagefigures] +% [x=-2mm, +% y=-2mm, +% width=\paperwidth, +% height=\paperheight] +% +% \definelayerpreset [lefttop] [corner={left,top},location={right,bottom}] +% \definelayerpreset [righttop] [corner={right,top},location={left,bottom}] +% \definelayerpreset [leftbottom] [corner={left,bottom},location={right,top}] +% \definelayerpreset [rightbottom] [corner={right,bottom},location={left,top}] +% \definelayerpreset [middle] [corner=middle,location=middle] +% +% \setlayer[pagefigures][preset=lefttop] +% \setlayer[pagefigures][preset=righttop] +% \setlayer[pagefigures][preset=leftbottom] +% \setlayer[pagefigures][preset=rightbottom] + +\definelayerpreset + [\v!left\v!top] + [\c!corner={\v!left,\v!top},\c!location={\v!right,\v!bottom}] + +\definelayerpreset + [\v!right\v!top] + [\c!corner={\v!right,\v!top},\c!location={\v!left,\v!bottom}] + +\definelayerpreset + [\v!left\v!bottom] + [\c!corner={\v!left,\v!bottom},\c!location={\v!right,\v!top}] + +\definelayerpreset + [\v!right\v!bottom] + [\c!corner={\v!right,\v!bottom},\c!location={\v!left,\v!top}] + +\definelayerpreset + [\v!middle] + [\c!corner=\v!middle,\c!location=\v!middle] + +% \definelayerpreset +% [\v!middle\v!top] +% [\c!location=\v!bottom,\c!hoffset=.5\layerwidth] + +% \definelayerpreset +% [\v!middle\v!bottom] +% [\c!location=\v!top,\c!hoffset=.5\layerwidth,\c!voffset=\layerheight] + +% \definelayerpreset +% [\v!middle\v!left] +% [\c!location=\v!right,\c!voffset=.5\layerheight] + +% \definelayerpreset +% [\v!middle\v!right] +% [\c!location=\v!left,\c!hoffset=\layerwidth,\c!voffset=.5\layerheight] + +\definelayerpreset + [\v!middle\v!top] + [\c!location=\v!bottom,\c!corner=\v!top,\c!dx=.5\layerwidth] + +\definelayerpreset + [\v!middle\v!bottom] + [\c!location=\v!top,\c!corner=\v!bottom,\c!dx=.5\layerwidth] + +\definelayerpreset + [\v!middle\v!left] + [\c!location=\v!right,\c!corner=\v!left,\c!dy=.5\layerheight] + +\definelayerpreset + [\v!middle\v!right] + [\c!location=\v!left,\c!corner=\v!right,\c!dy=.5\layerheight] + +\def\alignedbox + {\dodoubleempty\doalignedbox[]} + +% \def\doalignedbox[#1][#2]% +% {\bgroup +% %\let\iftraceboxplacement\iftracelayers % ugly +% \dowithnextbox +% {\let\next\middlebox +% \processaction +% [#2] +% [ t=>\let\next\topbox , b=>\let\next\bottombox , +% l=>\let\next\leftbox , r=>\let\next\rightbox , +% bl=>\let\next\bottomleftbox,br=>\let\next\bottomrightbox, +% tl=>\let\next\topleftbox ,tr=>\let\next\toprightbox , +% lt=>\let\next\lefttopbox ,lb=>\let\next\leftbottombox , +% rt=>\let\next\righttopbox ,rb=>\let\next\rightbottombox]% +% \next{\flushnextbox}% +% \egroup}#1} + +\def\doalignedbox[#1][#2]% + {\bgroup + %\let\iftraceboxplacement\iftracelayers % ugly + \dowithnextbox + {\serializecommalist[#2]% + \executeifdefined{\??ab\??ab\serializedcommalist}\middlebox{\flushnextbox}% + \egroup}#1} + +\setvalue{\??ab\??ab }{\middlebox} +\setvalue{\??ab\??ab\v!middle }{\middlebox} +\setvalue{\??ab\??ab\v!left }{\leftbox } +\setvalue{\??ab\??ab\v!right }{\rightbox } +\setvalue{\??ab\??ab\v!bottom }{\bottombox} +\setvalue{\??ab\??ab\v!top }{\topbox } + +\setvalue{\??ab\??ab\v!middle\v!middle}{\middlebox} +\setvalue{\??ab\??ab\v!left \v!top }{\lefttopbox} +\setvalue{\??ab\??ab\v!left \v!bottom}{\leftbottombox} +\setvalue{\??ab\??ab\v!right \v!top }{\righttopbox} +\setvalue{\??ab\??ab\v!right \v!bottom}{\rightbottombox} +\setvalue{\??ab\??ab\v!top \v!left }{\topleftbox} +\setvalue{\??ab\??ab\v!bottom\v!left }{\bottomleftbox} +\setvalue{\??ab\??ab\v!top \v!right }{\toprightbox} +\setvalue{\??ab\??ab\v!bottom\v!right }{\bottomrightbox} + +\setvalue{\??ab\??ab c}{\middlebox} +\setvalue{\??ab\??ab l}{\leftbox} +\setvalue{\??ab\??ab r}{\rightbox} +\setvalue{\??ab\??ab b}{\bottombox} +\setvalue{\??ab\??ab t}{\topbox} + +\setvalue{\??ab\??ab lt}{\lefttopbox} +\setvalue{\??ab\??ab lb}{\leftbottombox} +\setvalue{\??ab\??ab rt}{\righttopbox} +\setvalue{\??ab\??ab rb}{\rightbottombox} +\setvalue{\??ab\??ab tl}{\topleftbox} +\setvalue{\??ab\??ab bl}{\bottomleftbox} +\setvalue{\??ab\??ab tr}{\toprightbox} +\setvalue{\??ab\??ab br}{\bottomrightbox} + +\setvalue{\??ab\??ab m}{\middlebox} + +% The next ones were desparately needed by Vit Zyka (see +% \type {supp-box} for definitions). + +\setvalue{\??ab\??ab g}{\baselinemiddlebox} +\setvalue{\??ab\??ab gl}{\baselineleftbox} +\setvalue{\??ab\??ab gc}{\baselinemiddlebox} +\setvalue{\??ab\??ab gr}{\baselinerightbox} + +\setvalue{\??ab\??ab \v!line }{\baselinemiddlebox} % \v!grid is taken +\setvalue{\??ab\??ab \v!line\v!left }{\baselineleftbox} +\setvalue{\??ab\??ab \v!line\v!middle}{\baselinemiddlebox} +\setvalue{\??ab\??ab \v!line\v!right }{\baselinerightbox} + +\unexpanded\def\offsetbox + {\dodoubleempty\dooffsetbox[]} + +% left/right/top/bottomoffset -> dimensions change +% x/y | method=fixed -> dimensions don't change + +\def\dooffsetbox[#1][#2]% + {\bgroup + \dowithnextbox + {\getparameters[\??ox] + [\c!x=\zeropoint, + \c!y=\zeropoint, + \c!width=\nextboxwd, + \c!height=\nextboxht, + \c!depth=\nextboxdp, + \c!location=, + \c!leftoffset=\zeropoint, + \c!rightoffset=\zeropoint, + \c!topoffset=\zeropoint, + \c!bottomoffset=\zeropoint, + \c!method=, + #2]% + \donefalse + \ifdim\@@oxleftoffset =\zeropoint\else\donetrue\fi + \ifdim\@@oxrightoffset=\zeropoint\else\donetrue\fi + \ifdim\@@oxtopoffset =\zeropoint\else\donetrue\fi + \ifdim\@@oxbottomoffset =\zeropoint\else\donetrue\fi + \ifdone + \doif\@@oxmethod\v!fixed % new + {\ifdim\@@oxleftoffset=\zeropoint + \ifdim\@@oxrightoffset=\zeropoint \else + \scratchdimen-\@@oxrightoffset + \edef\@@oxx{\the\scratchdimen}% + \let\@@oxrightoffset\zeropoint + \fi + \else + \let\@@oxx\@@oxleftoffset + \let\@@oxleftoffset\zeropoint + \fi + \ifdim\@@oxtopoffset=\zeropoint + \ifdim\@@oxbottomoffset=\zeropoint \else + \scratchdimen-\@@oxbottomoffset + \edef\@@oxy{\the\scratchdimen}% + \let\@@oxbottomoffset\zeropoint + \fi + \else + \let\@@oxy\@@oxtopoffset + \let\@@oxtopoffset\zeropoint + \fi + \donefalse}% + \fi + \ifdone + \setbox\nextbox\vbox + {\forgetall\offinterlineskip + \vskip\@@oxtopoffset + \hbox + {\hskip\@@oxleftoffset + \flushnextbox + \hskip\@@oxrightoffset}% + \vskip\@@oxbottomoffset}% + \scratchdimen\nextboxht + \advance\scratchdimen\nextboxdp + \nextboxht\scratchdimen + \nextboxdp\zeropoint + \fi + \freezedimenmacro\@@oxwidth + \freezedimenmacro\@@oxheight + \freezedimenmacro\@@oxdepth + \setbox\nextbox\hbox + {\hskip\@@oxx\lower\@@oxy\hbox + {\doifelsenothing\@@oxlocation + {\flushnextbox} + {\alignedbox[\@@oxlocation]\hbox{\flushnextbox}}}}% + \nextboxwd\@@oxwidth + \nextboxht\@@oxheight + \nextboxdp\@@oxdepth + \flushnextbox + \egroup}#1} + +% \useMPlibrary[pre] \setupbackgrounds[page][background=pagegrid] +% +% \placefigure[left,none]{}{\offset[leftoffset=1cm]{\externalfigure[koe][breedte=3cm]}} +% \input tufte +% \placefigure[left,none]{}{\offset[rightoffset=1cm]{\externalfigure[koe][breedte=3cm]}} +% \input tufte +% \placefigure[left,none]{}{\offset[topoffset=1cm]{\externalfigure[koe][breedte=3cm]}} +% \input tufte +% \placefigure[left,none]{}{\offset[bottomoffset=1cm]{\externalfigure[koe][breedte=3cm]}} +% \input tufte + +\unexpanded\def\offset {\dodoubleempty\dooffsetbox [\hbox]} % yes or no +\unexpanded\def\aligned{\dosingleempty\doalignedbox[\hbox]} % yes or no + +%\ruledhbox{\offsetbox[x=-1cm,y=-1cm,location=c] +% {\framed[width=4cm,height=4cm]{x}}} + +\def\dotabbed#1#2#3#4% + {\dontleavehmode + \bgroup + \setbox\scratchbox\hbox{#3}% + \hbox to \wd\scratchbox{#1#4#2}% + \egroup} + +\unexpanded\def\ltabbed{\dotabbed\relax\hss} +\unexpanded\def\rtabbed{\dotabbed\hss \relax} +\unexpanded\def\ctabbed{\dotabbed\hss \hss} \let\mtabbed\ctabbed + +% \ltabbed{\romeins{3}}{\romeins{1}} test \endgraf +% \ltabbed{\romeins{3}}{\romeins{2}} test \endgraf +% \ltabbed{\romeins{3}}{\romeins{3}} test \endgraf +% +% \rtabbed{\romeins{3}}{\romeins{1}} test \endgraf +% \rtabbed{\romeins{3}}{\romeins{2}} test \endgraf +% \rtabbed{\romeins{3}}{\romeins{3}} test \endgraf +% +% \ctabbed{\romeins{3}}{\romeins{1}} test \endgraf +% \ctabbed{\romeins{3}}{\romeins{2}} test \endgraf +% \ctabbed{\romeins{3}}{\romeins{3}} test \endgraf + +% alternative, if done, then other name +% +% \def\dotabbed#1#2#3#4% +% {\dontleavehmode +% \bgroup +% \scratchdimen\zeropoint +% \def\docommand##1% +% {\setbox\scratchbox\hbox{##1}% +% \ifdim\wd\scratchbox>\scratchdimen +% \scratchdimen\wd\scratchbox +% \fi}% +% \processcommalist[#3]\docommand +% \hbox to \scratchdimen{#1#4#2}% +% \egroup} +% +% \def\ltabbed{\dotabbed\relax\hss} +% \def\rtabbed{\dotabbed\hss \relax} +% \def\ctabbed{\dotabbed\hss \hss} \let\mtabbed\ctabbed +% +% \ltabbed{\romeins{10},\romeins{2000},\romeins{15}}{\romeins{10}} test \endgraf +% \ltabbed{\romeins{10},\romeins{2000},\romeins{15}}{\romeins{15}} test \endgraf +% \ltabbed{\romeins{10},\romeins{2000},\romeins{15}}{\romeins{2000}} test \endgraf +% +% \rtabbed{\romeins{10},\romeins{2000},\romeins{15}}{\romeins{10}} test \endgraf +% \rtabbed{\romeins{10},\romeins{2000},\romeins{15}}{\romeins{15}} test \endgraf +% \rtabbed{\romeins{10},\romeins{2000},\romeins{15}}{\romeins{2000}} test \endgraf +% +% \ctabbed{\romeins{10},\romeins{2000},\romeins{15}}{\romeins{10}} test \endgraf +% \ctabbed{\romeins{10},\romeins{2000},\romeins{15}}{\romeins{15}} test \endgraf +% \ctabbed{\romeins{10},\romeins{2000},\romeins{15}}{\romeins{2000}} test \endgraf + +% to be documented + +\unexpanded\def\phantombox[#1]% + {\hbox\bgroup + \getparameters + [\??ol] + [\c!width=\zeropoint,% + \c!height=\zeropoint,% + \c!depth=\zeropoint,#1]% + \setbox\scratchbox\null + \wd\scratchbox\@@olwidth + \ht\scratchbox\@@olheight + \dp\scratchbox\@@oldepth + \box\scratchbox + \egroup} + +% \backgroundimage{1}{\hsize}{\vsize}{\externalfigure[cow][\c!width=3cm]} + +\unexpanded\def\backgroundimage#1#2#3% repeat hsize vsize + {\bgroup + \forgetall + \dowithnextbox + {\offinterlineskip + \ifcase#1\relax + % just one + \else + \scratchdimen#2\divide\scratchdimen\nextboxwd\count0\scratchdimen\advance\count0\plusone + \scratchdimen#3\divide\scratchdimen\nextboxht\count2\scratchdimen\advance\count2\plusone + % to be considered, probably methods + \ifcase#1\or % x and y + \setbox\nextbox\hbox{\dorecurse{\count0}{\copy\nextbox}}% + \setbox\nextbox\vbox{\dorecurse{\count2}{\copy\nextbox\endgraf}}% + \or % x + \setbox\nextbox\hbox{\dorecurse{\count0}{\copy\nextbox}}% + \or % y + \setbox\nextbox\vbox{\dorecurse{\count2}{\copy\nextbox\endgraf}}% + \fi + \fi + \ifdim\nextboxwd>#2\relax + \setbox\nextbox\hbox to #2{\hss\flushnextbox\hss}% + \setbox\nextbox\hbox{\expanded{\clip[\c!width=#2,\c!height=\the\nextboxht]{\flushnextbox}}}% + \fi + \ifdim\nextboxht>#3\relax + \setbox\nextbox\vbox to #3{\vss\flushnextbox\vss}% + \setbox\nextbox\hbox{\expanded{\clip[\c!width=\the\nextboxwd,\c!height=#3]{\flushnextbox}}}% + \fi + \flushnextbox + \egroup}% + \hbox} + +\protect \endinput diff --git a/tex/context/base/pack-lyr.mkii b/tex/context/base/pack-lyr.mkii new file mode 100644 index 000000000..85549bccb --- /dev/null +++ b/tex/context/base/pack-lyr.mkii @@ -0,0 +1,755 @@ +%D \module +%D [ file=pack-lyr, +%D version=2000.10.20, +%D title=\CONTEXT\ Packaging Macros, +%D subtitle=Layers, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Packaging Macros / Layers} + +%D This module is now etex dependent. + +% todo : first / last / next / +... => page key +% test on left/right box when no doublesided option given +% use \ifcsname instead of doifvalue + +\unprotect + +% When being backgrounds layers get the background offset +% displacement. Should be an option, on by default +% (compatibility). + +% positie=forceer == ja maar dan ook in status=herhaal + +%D The layering mechanism implemented here is independent of +%D the output routine, but future extensions may depend on a +%D more close cooperation. + +%D First we overload a macro from \type {core-rul}. From now on +%D we accept a (optional) argument: the specific layer it +%D will go in. This means that we can move an overlay from one +%D background to the other using the dimensions of the parent. + +%D ! ! ! ! to be documented ! ! ! ! + +\ifx\undefined\defineoverlay \message{loaded to early} \wait \fi + +\def\defineoverlay + {\dotripleempty\dodefineoverlay} + +\def\dodefineoverlay[#1][#2][#3]% overlay [layer] content + {\ifthirdargument + \writestatus{BEWARE}{This (overlay definition) has changed!}% temp + \def\docommand##1{\setvalue{\??ov##1}{\setlayer[#2]{\executedefinedoverlay{##1}{#3}}}} + \else + \def\docommand##1{\setvalue{\??ov##1}{\executedefinedoverlay{##1}{#2}}}% + \fi + \processcommalist[#1]\docommand} + +%D When tracing is turned on, a couple of boxes will +%D show up as well as the reference point. + +\newif\iftracelayers % \tracelayerstrue + +%D This handy constant saved some string memory. + +\def\@@layerbox{@@layerbox} + +%D \macros +%D {definelayer} +%D +%D Each layer gets its own (global) box. This also means that +%D the data that goes into a layer, is typeset immediately. +%D Each layer automatically gets an associated overlay, +%D which can be used in any background assignment. + +% todo : links/rechts + +\def\definelayer + {\dodoubleargument\dodefinelayer} + +\def\dodefinelayer[#1][#2]% \zeropoint ipv \!!zeropoint + {\setuplayer + [#1] + [\c!doublesided=,\c!preset=, + \c!state=\v!start,\c!direction=\v!normal,\c!option=, + \c!x=\zeropoint,\c!y=\zeropoint,\c!position=\v!no, + \c!line=0,\c!column=0, + \c!width=\nextboxwd,\c!height=\nextboxht, + \c!offset=\zeropoint,\c!rotation=, % geen 0 ! + \c!hoffset=\zeropoint,\c!voffset=\zeropoint, + \c!dx=\zeropoint,\c!dy=\zeropoint, + \c!location=rb,\c!position=\v!no,\c!page=, + \c!method=\v!overlay, + \c!sx=1,\c!sy=1,\c!corner=,#2]% + \doifvalue{\??ll#1\c!doublesided}\v!yes + {\dopresetlayerbox{\v!left #1}% + \dopresetlayerbox{\v!right#1}}% + \dopresetlayerbox{#1}% + \defineoverlay[#1][\composedlayer{#1}]} + +\def\dopresetlayerbox#1% + {\ifundefined{\@@layerbox#1}% + \expandafter\newbox\csname\@@layerbox#1\endcsname + \else + \resetlayer[#1]% + \fi} + +%D \macros +%D {setuplayer} +%D +%D After a layer is defined, you can change its +%D characteristics. + +\def\setuplayer + {\dodoubleargument\dosetuplayer} + +\def\dosetuplayer[#1][#2]% + {\def\docommand##1{\getparameters[\??ll##1][#2]}% + \processcommalist[#1]\docommand} + +%D \macros +%D {setlayer} +%D +%D Data is moved into a layer with the following macro. When +%D \type {position} is set, relative positioning is used, with +%D the current point as reference point. Otherwise the topleft +%D corner is used as reference point. +%D +%D \starttyping +%D \setlayer [identifier] [optional parameters] {data} +%D \stoptyping + +\newcount\currentlayerdata + +\let\currentlayerwidth \!!zeropoint +\let\currentlayerheight\!!zeropoint + +\def\setcurrentlayerdimensions + {\dodoubleempty\dosetcurrentlayerdimensions} + +\def\dosetcurrentlayerdimensions[#1][#2]% name left|right + {\edef\currentlayerwidth {\thelayerwidth {#2#1}}% + \edef\currentlayerheight{\thelayerheight{#2#1}}} + +\def\thelayerwidth #1{\the\wd\executeifdefined{\@@layerbox#1}\emptybox} +\def\thelayerheight#1{\the\ht\executeifdefined{\@@layerbox#1}\emptybox} + +\def\setlayer + {\dotripleempty\dosetlayer} + +\def\dosetlayer[#1][#2][#3]% #4 == box do \fi is ok + {\doifelsevalue{\??ll#1\c!state}\v!stop + {\dowithnextbox\donothing\hbox} + {\ifthirdargument + \dodosetlayer[#1][#2][#3]% + \else + \doifassignmentelse{#2} + {\dodosetlayer[#1][][#2]}% + {\dodosetlayer[#1][#2][]}% + \fi}} + +\def\dodosetlayer[#1][#2][#3]% #2 = links/rechts + {\bgroup + \recalculatebackgrounds + \recalculatelogos + \global\advance\currentlayerdata\plusone + \forgetall + \dontcomplain + \doifvalue{\??ll#1\c!option}\v!test\tracelayerstrue + \iftracelayers\traceboxplacementtrue\fi + \dowithnextbox % sneller als aparte macro + {\ifcsname\@@layerbox#1\endcsname % nb: odd/even discard, left/right not + \edef\@@layerloc{#2}% + \ifx\@@layerloc\v!even + \ifodd\realpageno + % discard nextbox + \else + \dododosetlayer[#1][\v!left][#3]% + \fi + \else\ifx\@@layerloc\v!odd + \ifodd\realpageno + \dododosetlayer[#1][\v!right][#3]% + %\else + % discard nextbox + \fi + \else + \dododosetlayer[#1][#2][#3]% + \fi\fi + \else + \writestatus{layer}{unknown layer #1}% + \fi + \egroup}% + \hbox} + +\newbox\layerbox + +\newdimen\@@layerxsiz +\newdimen\@@layerysiz +\newdimen\@@layerxoff +\newdimen\@@layeryoff +\newdimen\@@layerxpos +\newdimen\@@layerypos + +\let\lastlayerxpos\!!zeropoint +\let\lastlayerypos\!!zeropoint +\let\lastlayerwd \!!zeropoint +\let\lastlayerht \!!zeropoint +\let\lastlayerdp \!!zeropoint + +% todo left/right + +\def\setlastlayerpos#1% + {\edef\layerpage{\MPp{lyr:\the\currentlayerdata}}% + \xdef\lastlayerxpos{\the\dimexpr-\MPx{lyr:#1:\layerpage}+\MPx{lyr:\the\currentlayerdata}\relax}% + \xdef\lastlayerypos{\the\dimexpr \MPy{lyr:#1:\layerpage}-\MPy{lyr:\the\currentlayerdata}\relax}} + +\def\definelayerpreset + {\dodoubleargument\dodefinelayerpreset} + +% \def\dodefinelayerpreset[#1][#2]% +% {\setvalue{\??ll\??ll#1}{\dopresetlayer{#2}}} +% +% more fun: \definelayerpreset[whatever][lefttop] + +\def\dodefinelayerpreset[#1][#2]% + {\doifassignmentelse{#2} + {\setvalue{\??ll\??ll#1}{\dopresetlayer{#2}}} + {\setvalue{\??ll\??ll#1}{\getvalue{\??ll\??ll#2}}}} + +\def\dopresetlayer#1#2#3% #1=list #2=tag #3=list + {\getparameters[\??ll#2][#1,#3]} + +\letempty\currentlayer + +\def\layerparameter#1{\csname\??ll\currentlayer#1\endcsname} + +\newdimen\layerwidth +\newdimen\layerheight + +\chardef\@@lacome=1 % LAyerCOnstructionMEthod / temp, will be default + +\def\dododosetlayer[#1][#2][#3]% will be sped up + {% we use the global width, never change this + \def\currentlayer{#1}% + \@@layerxsiz\layerparameter\c!width + \@@layerysiz\layerparameter\c!height + \layerwidth \@@layerxsiz + \layerheight\@@layerysiz + % preroll + \getparameters[\??ll\currentlayer][#3]% + % presets and real roll +% maybe todo: +% \doif{\layerparameter\c!method}\v!fit +% {\@@layerxsiz\thelayerwidth \currentlayer +% \@@layerysiz\thelayerheight\currentlayer +% \layerwidth \@@layerxsiz +% \layerheight\@@layerysiz +% }% + % etc + \executeifdefined{\??ll\??ll\layerparameter\c!preset}\gobbletwoarguments\currentlayer{#3}% + % that was real slow + \doif{\layerparameter\c!position}\v!overlay % slow, use \dosetvalue instead + {\getparameters[\??ll\currentlayer][\c!width=\zeropoint,\c!height=\zeropoint,\c!position=\v!yes]}% + \doifsomething{\layerparameter\c!rotation} + {\setbox\nextbox\hbox + {\rotate % to be checked with new rotation + [\c!location=\v!high,\c!rotation=\layerparameter\c!rotation] + {\flushnextbox}}}% + % no, not local + % \@@layerxsiz\layerparameter\c!width + % \@@layerysiz\layerparameter\c!height + % never change that + \@@layerxpos\layerparameter\c!x + \@@layerypos\layerparameter\c!y + \doifelse{\layerparameter\c!hoffset}\v!max + {\@@layerxoff\@@layerxsiz}{\@@layerxoff\layerparameter\c!hoffset}% + \doifelse{\layerparameter\c!voffset}\v!max + {\@@layeryoff\@@layerysiz}{\@@layeryoff\layerparameter\c!voffset}% + % dx/dy are internal context ones and can be used in preset + \advance\@@layerxoff\dimexpr\layerparameter\c!offset+\layerparameter\c!dx\relax + \advance\@@layeryoff\dimexpr\layerparameter\c!offset+\layerparameter\c!dy\relax + \@@layerxpos\layerparameter\c!sx\@@layerxpos + \@@layerypos\layerparameter\c!sy\@@layerypos + \@@layerxoff\layerparameter\c!sx\@@layerxoff + \@@layeryoff\layerparameter\c!sy\@@layeryoff + \doifelse{\layerparameter\c!position}\v!yes % combine ^ + {\setlastlayerpos{#2\currentlayer}% todo l/r %%%%%%%%%%%% + \@@layerxpos\lastlayerxpos + \@@layerypos\lastlayerypos + \letgvalue{\??ll\currentlayer\layerpage\c!position}\v!yes + \letgvalue{\??ll\currentlayer\c!state}\v!start % needed ? + \setbox\layerbox\vbox to \@@layerysiz + {\hbox to \@@layerxsiz{\xypos{lyr:\the\currentlayerdata}\hss}\vss}} + {\setbox\layerbox\emptybox + \globallet\lastlayerxpos\!!zeropoint + \globallet\lastlayerypos\!!zeropoint + \ExpandBothAfter\doifinset\v!bottom{\layerparameter\c!corner} + {\ifnum\layerparameter\c!line=\zerocount\else % can be < 0 + \setevalue{\??ll\currentlayer\c!line}% + {\the\numexpr-\layerparameter\c!line+\layoutlines+\plusone\relax}% + \fi + \ifdim\@@layerysiz>\zeropoint + \advance\@@layerypos-\@@layerysiz + \@@layerypos-\@@layerypos + \@@layeryoff-\@@layeryoff + \fi}% + \ExpandBothAfter\doifinset\v!right{\layerparameter\c!corner} + {\ifnum\layerparameter\c!column=\zerocount\else % can be < 0 + \setevalue{\??ll\currentlayer\c!column}% + {\the\numexpr-\layerparameter\c!column+\layoutcolumns+\plusone\relax}% + \fi + \ifdim\@@layerxsiz>\zeropoint + \advance\@@layerxpos-\@@layerxsiz + \@@layerxpos-\@@layerxpos + \@@layerxoff-\@@layerxoff + \fi}% + \ExpandBothAfter\doif\v!middle{\layerparameter\c!corner} + {\ifdim\@@layerxsiz>\zeropoint \advance\@@layerxpos.5\@@layerxsiz \fi + \ifdim\@@layerysiz>\zeropoint \advance\@@layerypos.5\@@layerysiz \fi}% + \edef\layerpage{\layerparameter\c!page}}% + \doifsomething\layerpage + {\edef\layerpage{:\layerpage}% + \doifundefined{\@@layerbox#2\currentlayer\layerpage} + {\global\expandafter\newbox\csname\@@layerbox#2\currentlayer\layerpage\endcsname}}% + \dontcomplain % more comfortable + \mathchardef\layerpagebox\csname\@@layerbox#2\currentlayer\layerpage\endcsname + \ifvoid\layerpagebox + \gsetboxllx\layerpagebox\zeropoint + \gsetboxlly\layerpagebox\zeropoint + \fi + \global\setbox\layerpagebox\vbox %to \layerparameter\c!height % new, otherwise no negative y possible + {\offinterlineskip + %postpone, to after nextboxwd correction % \hsize\layerparameter\c!width % new, keep box small + %\ifvoid\csname\@@layerbox\currentlayer\layerpage\endcsname\else % why not #2#1 + \ifvoid\layerpagebox + \let\lastlayerwidth \zeropoint + \let\lastlayerheight\zeropoint + \else + \edef\lastlayerwidth {\the\wd\layerpagebox}% + \edef\lastlayerheight{\the\ht\layerpagebox}% + \ht\layerpagebox\zeropoint + \dp\layerpagebox\zeropoint + \wd\layerpagebox\zeropoint + \doifnot{\layerparameter\c!direction}\v!reverse{\box\layerpagebox}% + \fi + % don't move + \xdef\lastlayerwd{\the\nextboxwd}% + \xdef\lastlayerht{\the\nextboxht}% % not entirely ok when grid ! + \xdef\lastlayerdp{\the\nextboxdp}% % not entirely ok when grid ! + % this code + \doifelse{\layerparameter\c!location}\v!grid\donetrue\donefalse + \ifdone + \nextboxht\strutheight + \nextboxdp\strutdepth + \else + \setbox\nextbox\hbox{\alignedbox[\layerparameter\c!location]\vbox{\flushnextbox}}% + \fi + \ifnum\layerparameter\c!line=\zerocount\else % no \ifcase, can be negative + \advance\@@layerypos\dimexpr\layerparameter\c!line\lineheight+\topskip-\lineheight-\nextboxht\relax + \fi + \ifnum\layerparameter\c!column=\zerocount\else % no \ifcase, can be negative + \advance\@@layerxpos\layoutcolumnoffset{\layerparameter\c!column}% + \fi + \ifdone + \setbox\nextbox\hbox{\alignedbox[rb]\vbox{\flushnextbox}}% + \fi + % ll registration + \scratchdimen\@@layerxpos + \advance\scratchdimen\@@layerxoff + \ifdim\scratchdimen<\getboxllx\layerpagebox + \gsetboxllx\layerpagebox\scratchdimen + \fi + \ifcase\@@lacome\or % this test will become obsolete + \advance\scratchdimen\nextboxwd + \nextboxwd\ifdim\scratchdimen>\lastlayerwidth \scratchdimen \else \lastlayerwidth \fi + \fi + \scratchdimen\@@layerypos + \advance\scratchdimen\@@layeryoff + \ifdim\scratchdimen<\getboxlly\layerpagebox + \gsetboxlly\layerpagebox\scratchdimen + \fi + % ll compensation + \ifcase\@@lacome\or % this test will become obsolete + \advance\scratchdimen\dimexpr\nextboxht+\nextboxdp\relax + \nextboxht\ifdim\scratchdimen>\lastlayerheight \scratchdimen \else \lastlayerheight \fi + \nextboxdp\zeropoint + \fi + % placement + \hsize\layerparameter\c!width % new, keep box small + \vbox to \layerparameter\c!height \bgroup + \smashbox\nextbox + \vskip\dimexpr\@@layerypos+\@@layeryoff\relax + \hskip\dimexpr\@@layerxpos+\@@layerxoff\relax + \flushnextbox + \ifvoid\layerpagebox + % already flushed + \else + % the reverse case % check ! + \vskip-\dimexpr\@@layerypos+\@@layeryoff\relax + \box\layerpagebox + \fi + \egroup}% + % when position is true, the layerbox holds the compensation and needs + % to be placed; never change this ! + \ifvoid\layerbox\else\box\layerbox\fi} + +%D Given the task to be accomplished, the previous macro is +%D not even that complicated. It mainly comes down to skipping +%D to the right place and placing a box on top of or below the +%D existing content. In the case of position tracking, another +%D reference point is chosen. + +%D \macros +%D {doifelselayerdata} +%D + +\def\doifelselayerdata#1% + {\ifundefined{\@@layerbox#1}% + \@EA\secondoftwoarguments + \else\ifvoid\csname\@@layerbox#1\endcsname + \@EAEAEA\secondoftwoarguments + \else + \@EAEAEA\firstoftwoarguments + \fi\fi} + +%D \macros +%D {flushlayer} +%D +%D When we flush a layer, we flush both the main one and the +%D page dependent one (when defined). This feature is more +%D efficient in \ETEX\ since there testing for an undefined +%D macro does not takes hash space. + +% todo: setups before flush, handy hook + +\unexpanded\def\flushlayer[#1]% + {\doifelsevalue{\??ll#1\c!state}\v!next + {\global\letvalue{\??ll#1\c!state}\v!start} % dangerous, stack-built-up + {\doifelsevalue{\??ll#1\c!state}\v!continue + {\global\letvalue{\??ll#1\c!state}\v!repeat} % dangerous, stack-built-up + {\doifelsevalue{\??ll#1\c!doublesided}\v!yes + {\doifundefinedelse{\@@layerbox#1}% + {\dodoflushlayerA[#1]} + {\doifbothsidesoverruled + {\dodoflushlayerB\v!left [#1]}% left + {\dodoflushlayerB\v!right[#1]}% right + {\dodoflushlayerB\v!left [#1]}}}% left + {\dodoflushlayerA[#1]}}}} + +\def\dodoflushlayerA[#1]% + {\doifnotvalue{\??ll#1\c!state}\v!stop + {\startoverlay + {\dodoflushlayer1{#1}{#1}} + {\dodoflushlayer0{#1}{#1:\realfolio}} + \stopoverlay}} + +\def\dodoflushlayerB#1[#2]% + {\doifnotvalue{\??ll#2\c!state}\v!stop + {\startoverlay + {\dodoflushlayer1{#2}{#2}} + {\dodoflushlayer0{#2}{#2:\realfolio}} + {\dodoflushlayer1{#2}{#1#2}} + {\dodoflushlayer0{#2}{#1#2:\realfolio}} + \stopoverlay}} + +\def\dodoflushlayer#1#2#3% + {\ifundefined{\@@layerbox#3}% + \ifcase#1\else\writestatus{layer}{unknown layer #3}\fi + \else + \bgroup + \forgetall + \offinterlineskip + % needed because we need to handle method + \executeifdefined{\??ll\??ll\getvalue{\??ll#2\c!preset}}\gobbletwoarguments{#2}{}% + % + \doifvalue{\??ll#2\c!option}\v!test\tracelayerstrue + \iftracelayers\traceboxplacementtrue\fi + \!!doneafalse + \!!donebfalse + \doifvalue{\??ll#2\c!method}\v!overlay\!!doneatrue + \doifvalue{\??ll#2\c!method}\v!fit\!!donebtrue + \!!donectrue + \ifcase#1\else + \doifnotvalue{\??ll#2\c!position}\v!yes + {\doifvalue{\??ll#2\c!repeat}\v!yes\!!donecfalse + \doifvalue{\??ll#2\c!state}\v!repeat\!!donecfalse}% + \fi + \mathchardef\layerbox\csname\@@layerbox#3\endcsname + % we need to copy in order to retain the negative offsets for a next + % stage of additions, i.e. llx/lly accumulate in repeat mode and the + % compensation may differ each flush depending on added content + \setbox\nextbox \if!!doneb +% \vbox +% {\scratchdimen\getboxlly\layerbox +% \vskip-\scratchdimen +% \scratchdimen\getboxllx\layerbox +% \hskip-\scratchdimen +% \advance\scratchdimen-\wd\layerbox +% \hsize-\scratchdimen +% \if!!donec\box\else\copy\fi\layerbox}% + \vbox + {\vskip-\getboxlly\layerbox + \hskip-\getboxllx\layerbox + \hsize-\dimexpr\getboxllx\layerbox-\wd\layerbox\relax + \if!!donec\box\else\copy\fi\layerbox}% + \else + \if!!donec\box\else\copy\fi\layerbox % sorry for the delay due to copying + \fi + % todo: method=offset => overlayoffset right/down (handy for backgrounds with offset) + \iftracelayers \ruledvbox \else \vbox \fi \if!!donea to \overlayheight \fi + {\hbox \if!!donea to \overlaywidth \fi + {% klopt dit? #3 en niet #2 ? + \doifvalue{\??ll#3\realfolio\c!position}\v!yes{\xypos{lyr:#3:\realfolio}}% + \doifoverlayelse{#3} + {\box\nextbox} + {\startlayoutcomponent{l:#3}{layer #3}\box\nextbox\stoplayoutcomponent}% + \hss}% + \vss}% + \if!!donec + \gsetboxllx\layerbox\zeropoint + \gsetboxlly\layerbox\zeropoint + \fi + \egroup + \fi} + +% \definelayer[test][method=fit] \setupcolors[state=start] \tracelayerstrue +% +% \framed[framecolor=red,offset=overlay]{\setlayer[test]{aa}\setlayer[test][x=10pt]{g}\flushlayer[test]} +% \framed[framecolor=red,offset=overlay]{\setlayer[test]{aa}\setlayer[test][x=-10pt]{bb}\flushlayer[test]} +% \framed[framecolor=red,offset=overlay]{\setlayer[test][x=-20pt]{cccccc}\flushlayer[test]} +% \framed[framecolor=red,offset=overlay]{\setlayer[test]{dd}\setlayer[test][x=-20pt,y=-3pt]{eeeeee}\flushlayer[test]} + +%D \macros +%D {composedlayer,placelayer,tightlayer} +%D +%D This is a handy shortcut, which saves a couple of braces +%D when we use it as parameter. This name also suits better +%D to other layering commands. + +\def\composedlayer#1{\flushlayer[#1]} + +\let\placelayer\flushlayer + +\def\tightlayer[#1]% + {\hbox + {\def\currentlayer{#1}% todo: left/right + \setbox\nextbox\emptybox % hoogte/breedte are \wd\nextbox/\ht\nextbox + \hsize\layerparameter\c!width % \overlaywidth = \hsize + \vsize\layerparameter\c!height % \overlaywheight = \vsize + \composedlayer{#1}}} + +%D \macros +%D {resetlayer} +%D +%D This macro hardly needs an explanation (and is seldom +%D needed as well). + +\def\doresetlayer#1% + {\ifundefined{\@@layerbox#1}\else + \global\setbox\csname\@@layerbox#1\endcsname\emptybox + \fi} + +\def\resetlayer[#1]% + {\doresetlayer{#1}% + \doifvalue{\??ll#1\c!doublesided}\v!yes % kind of redundant test + {\doresetlayer{\v!left #1}% + \doresetlayer{\v!right#1}}% + \doresetlayer{#1:\realfolio}} + +%D \macros +%D {setMPlayer} +%D +%D The following layer macro uses the positions that are +%D registered by \METAPOST. +%D +%D \starttyping +%D \definelayer[test] +%D +%D \setMPlayer [test] [somepos-1] {Whatever we want here!} +%D \setMPlayer [test] [somepos-2] {Whatever we need there!} +%D \setMPlayer [test] [somepos-3] {\externalfigure[cow.mps][width=2cm]} +%D +%D \startuseMPgraphic{oeps} +%D draw fullcircle scaled 10cm withcolor red ; +%D register ("somepos-1",2cm,3cm,center currentpicture) ; +%D register ("somepos-2",8cm,5cm,(-1cm,-2cm)) ; +%D register ("somepos-3",0cm,0cm,(-2cm,2cm)) ; +%D \stopuseMPgraphic +%D +%D \getMPlayer[test]{\useMPgraphic{oeps}} +%D \stoptyping +%D +%D The last line is equivalent to +%D +%D \starttyping +%D \framed +%D [background={foreground,test},offset=overlay] +%D {\useMPgraphic{oeps}} +%D \stoptyping + +\def\setMPlayer + {\dotripleempty\dosetMPlayer} + +\def\MPlayerwidth {\hsize} +\def\MPlayerheight{\vsize} + +\def\dosetMPlayer[#1][#2][#3]% + {\checkpositions % new, else only support after \starttext + \edef\MPlayerwidth {\MPw{#2}}% + \edef\MPlayerheight{\MPh{#2}}% + \setlayer[#1][\c!x=\MPx{#2},\c!y=\MPy{#2},\c!position=\v!no,#3]} + +\def\getMPlayer + {\dodoubleempty\dogetMPlayer} + +\def\dogetMPlayer[#1][#2]% + {\framed + [\c!background={\v!foreground,#1}, + \c!frame=\v!off, + \c!offset=\v!overlay,#2]} + +% Some day this (old) mechanism will be combined/integrated +% in overlays + +\newskip\xposition \newskip\yposition +\newskip\xdimension \newskip\ydimension +\newskip\xoffset \newskip\yoffset + +% already defined \newbox\positionbox + +\def\startpositioning + {\bgroup + \xposition \zeropoint \yposition \zeropoint + \xdimension\zeropoint \ydimension\zeropoint + \xoffset \zeropoint \yoffset \zeropoint + \hfuzz \paperwidth \vfuzz \paperheight + \setbox\positionbox\hbox\bgroup} + +\def\stoppositioning + {\doifnot\@@psoffset\v!yes + {\global\xoffset\zeropoint + \global\yoffset\zeropoint}% + \global\advance\xdimension \xoffset + \global\advance\ydimension \yoffset + \egroup + \vbox to \ydimension + {\vskip\yoffset + \hbox to \xdimension + {\hskip\xoffset + \box\positionbox + \hfill} + \vfill}% + \egroup} + +\def\resetpositioning + {\getparameters[\??ps] + [\c!state=\v!start,% + \c!unit=\s!cm,% + \c!factor=1,% + \c!scale=1,% + \c!xfactor=\@@psfactor,% + \c!yfactor=\@@psfactor,% + \c!xscale=\@@psscale,% + \c!yscale=\@@psscale,% + \c!xstep=\v!absolute,% + \c!ystep=\v!absolute,% + \c!xoffset=\!!zeropoint,% + \c!yoffset=\!!zeropoint]} + +\def\setuppositioning + {\resetpositioning + \dodoubleargument\getparameters[\??ps]} + +\def\calculateposition#1#2#3#4#5#6#7#8#9% + {\setdimensionwithunit\scratchskip{#1}\@@psunit + \scratchskip#8\scratchskip + \scratchskip#9\scratchskip + \advance\scratchskip #4\relax + \doif{#2}\v!relative + {\advance\scratchskip #3% + \let#4\!!zeropoint}% + #3\scratchskip\relax + \doifnot\@@psstate\v!overlay + {\scratchskip#5\relax + \advance\scratchskip #3\relax + \ifdim#3<-#7\relax \global#7-#3\relax \fi + \ifdim\scratchskip>#6\relax \global#6\scratchskip\relax \fi}} + +\def\position + {\dosingleempty\doposition} + +\def\doposition[#1]#2(#3,#4)% + {\dowithnextbox + {\bgroup + \getparameters[\??ps][#1]% + \dontcomplain + \calculateposition{#3}\@@psxstep\xposition + \@@psxoffset{\nextboxwd}\xdimension\xoffset + \@@psxscale\@@psxfactor + \scratchdimen\nextboxht \advance\scratchdimen \nextboxdp + \calculateposition{#4}\@@psystep\yposition + \@@psyoffset\scratchdimen\ydimension\yoffset + \@@psyscale\@@psyfactor + \vbox to \zeropoint % kan beter. + {\vskip\yposition + \hbox to \zeropoint + {\hskip\xposition + \flushnextbox + \hss} + \vss}% + \xdef\dopoppositioning + {\xposition\the\xposition + \yposition\the\yposition + \noexpand\def\noexpand\@@psxoffset{\@@psxoffset}% + \noexpand\def\noexpand\@@psyoffset{\@@psyoffset}}% + \egroup + \dopoppositioning + \ignorespaces} + \hbox} + +\resetpositioning + +\setuppositioning + [\c!unit=\s!cm, + \c!factor=1, + \c!scale=1, + \c!xstep=\v!absolute, + \c!ystep=\v!absolute, + \c!offset=\v!yes, + \c!xoffset=\!!zeropoint, + \c!yoffset=\!!zeropoint] + +%D Watch out, a redefinition: + +\ifx\settextpagecontent\undefined \writestatus\m!systems{error in page-lyr.tex} \wait \fi + +\let\normalsettextpagecontent\settextpagecontent + +\definelayer + [OTRTEXT] + +\setuplayer + [OTRTEXT] + [\c!width=\innermakeupwidth, + \c!height=\textheight] + +% will be overloaded in page-spr + +\def\settextpagecontent#1#2#3% #2 and #3 will disappear + {\doifelselayerdata{OTRTEXT} + {\setbox#1\hbox to \makeupwidth + {\startoverlay + {\tightlayer[OTRTEXT]} % first, otherwise problems with toc + {\normalsettextpagecontent{#1}{#2}{#3}\box#1} + \stopoverlay}% + \dp#1\zeropoint}% + {\normalsettextpagecontent{#1}{#2}{#3}}} + +\protect \endinput diff --git a/tex/context/base/pack-lyr.mkiv b/tex/context/base/pack-lyr.mkiv new file mode 100644 index 000000000..0cb3ee244 --- /dev/null +++ b/tex/context/base/pack-lyr.mkiv @@ -0,0 +1,786 @@ +%D \module +%D [ file=pack-lyr, +%D version=2000.10.20, +%D title=\CONTEXT\ Packaging Macros, +%D subtitle=Layers, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Packaging Macros / Layers} + +% todo : first / last / next / +... => page key +% test on left/right box when no doublesided option given +% use \ifcsname instead of doifvalue + +\unprotect + +% When being backgrounds layers get the background offset +% displacement. Should be an option, on by default +% (compatibility). + +% positie=forceer == ja maar dan ook in status=herhaal + +%D The layering mechanism implemented here is independent of +%D the output routine, but future extensions may depend on a +%D more close cooperation. + +%D First we overload a macro from \type {core-rul}. From now on +%D we accept a (optional) argument: the specific layer it +%D will go in. This means that we can move an overlay from one +%D background to the other using the dimensions of the parent. + +\ifx\undefined\defineoverlay \message{loaded to early} \wait \fi + +\unexpanded\def\defineoverlay + {\dotripleempty\dodefineoverlay} + +\def\dodefineoverlay[#1][#2][#3]% overlay [layer] content + {\ifthirdargument + %\writestatus{BEWARE}{This (overlay definition) has changed!}% temp + \def\docommand##1{\setvalue{\??ov##1}{\setlayer[#2]{\executedefinedoverlay{##1}{#3}}}} + \else + \def\docommand##1{\setvalue{\??ov##1}{\executedefinedoverlay{##1}{#2}}}% + \fi + \processcommalist[#1]\docommand} + +%D When tracing is turned on, a couple of boxes will +%D show up as well as the reference point. + +\newif\iftracelayers % \tracelayerstrue + +%D This handy constant saved some string memory. + +\def\@@layerbox{@@layerbox} + +%D \macros +%D {definelayer} +%D +%D Each layer gets its own (global) box. This also means that +%D the data that goes into a layer, is typeset immediately. +%D Each layer automatically gets an associated overlay, +%D which can be used in any background assignment. + +% todo : links/rechts + +\unexpanded\def\definelayer + {\dodoubleargument\dodefinelayer} + +\def\dodefinelayer[#1][#2]% \zeropoint ipv \!!zeropoint + {\setuplayer + [#1] + [\c!doublesided=,\c!preset=, + \c!state=\v!start,\c!direction=\v!normal,\c!option=, + \c!x=\zeropoint,\c!y=\zeropoint,\c!position=\v!no, + \c!line=0,\c!column=0, + \c!width=\nextboxwd,\c!height=\nextboxht, + \c!offset=\zeropoint,\c!rotation=, % geen 0 ! + \c!hoffset=\zeropoint,\c!voffset=\zeropoint, + \c!dx=\zeropoint,\c!dy=\zeropoint, + \c!location=rb,\c!position=\v!no,\c!page=, + \c!method=\v!overlay, + \c!sx=1,\c!sy=1,\c!corner=,#2]% + \doifvalue{\??ll#1\c!doublesided}\v!yes + {\dopresetlayerbox{\v!left #1}% + \dopresetlayerbox{\v!right#1}}% + \dopresetlayerbox{#1}% + \defineoverlay[#1][\composedlayer{#1}]} + +\def\dopresetlayerbox#1% + {\ifcsname\@@layerbox#1\endcsname + \resetlayer[#1]% + \else + \expandafter\newbox\csname\@@layerbox#1\endcsname + \fi} + +%D \macros +%D {setuplayer} +%D +%D After a layer is defined, you can change its +%D characteristics. + +\unexpanded\def\setuplayer + {\dodoubleargument\dosetuplayer} + +\def\dosetuplayer[#1][#2]% + {\def\docommand##1{\getparameters[\??ll##1][#2]}% + \processcommalist[#1]\docommand} + +%D \macros +%D {setlayer} +%D +%D Data is moved into a layer with the following macro. When +%D \type {position} is set, relative positioning is used, with +%D the current point as reference point. Otherwise the topleft +%D corner is used as reference point. +%D +%D \starttyping +%D \setlayer [identifier] [optional parameters] {data} +%D \stoptyping + +\newcount\currentlayerdata + +\let\currentlayerwidth \!!zeropoint +\let\currentlayerheight\!!zeropoint + +\def\setcurrentlayerdimensions + {\dodoubleempty\dosetcurrentlayerdimensions} + +\def\dosetcurrentlayerdimensions[#1][#2]% name left|right + {\edef\currentlayerwidth {\thelayerwidth {#2#1}}% + \edef\currentlayerheight{\thelayerheight{#2#1}}} + +\def\thelayerwidth #1{\the\wd\executeifdefined{\@@layerbox#1}\emptybox} +\def\thelayerheight#1{\the\ht\executeifdefined{\@@layerbox#1}\emptybox} + +\def\setlayer + {\dotripleempty\dosetlayer} + +% \def\dosetlayer[#1][#2][#3]% #4 == box do \fi is ok +% {\doifelsevalue{\??ll#1\c!state}\v!stop +% {\dowithnextbox\donothing\hbox} +% {\ifthirdargument +% \dodosetlayer[#1][#2][#3]% +% \else +% \dodosetlayer[#1][][#2]% +% \fi}} + +\def\dosetlayer[#1][#2][#3]% #4 == box do \fi is ok + {\doifelsevalue{\??ll#1\c!state}\v!stop + {\dowithnextbox\donothing\hbox} + {\ifthirdargument + \dodosetlayer[#1][#2][#3]% + \else + \doifassignmentelse{#2} + {\dodosetlayer[#1][][#2]}% + {\dodosetlayer[#1][#2][]}% + \fi}} + +\def\dodosetlayer[#1][#2][#3]% #2 = links/rechts + {\bgroup + \recalculatebackgrounds % brrr + \global\advance\currentlayerdata\plusone + \forgetall + \dontcomplain + \doifvalue{\??ll#1\c!option}\v!test\tracelayerstrue + \iftracelayers\traceboxplacementtrue\fi + \dowithnextbox{\dodosetlayerindeed{#1}{#2}{#3}\egroup}\hbox} + +\def\dodosetlayerindeed#1#2#3% #2 = links/rechts + {\ifcsname\@@layerbox#1\endcsname % nb: odd/even discard, left/right not + \edef\@@layerloc{#2}% + \ifx\@@layerloc\v!even + \ifodd\realpageno + % discard nextbox + \else + \dododosetlayer[#1][\v!left][#3]% + \fi + \else\ifx\@@layerloc\v!odd + \ifodd\realpageno + \dododosetlayer[#1][\v!right][#3]% + %\else + % discard nextbox + \fi + \else + \dododosetlayer[#1][#2][#3]% + \fi\fi + \else + \writestatus{layer}{unknown layer #1}% + \fi} + +\newbox\layerbox + +\newdimen\@@layerxsiz +\newdimen\@@layerysiz +\newdimen\@@layerxoff +\newdimen\@@layeryoff +\newdimen\@@layerxpos +\newdimen\@@layerypos + +\let\lastlayerxpos\!!zeropoint +\let\lastlayerypos\!!zeropoint +\let\lastlayerwd \!!zeropoint +\let\lastlayerht \!!zeropoint +\let\lastlayerdp \!!zeropoint + +% todo left/right + +\def\setlastlayerpos#1% + {\edef\layerpage{\MPp{lyr:\the\currentlayerdata}}% + \xdef\lastlayerxpos{\the\dimexpr-\MPx{lyr:#1:\layerpage}+\MPx{lyr:\the\currentlayerdata}\relax}% + \xdef\lastlayerypos{\the\dimexpr \MPy{lyr:#1:\layerpage}-\MPy{lyr:\the\currentlayerdata}\relax}} + +\unexpanded\def\definelayerpreset + {\dodoubleargument\dodefinelayerpreset} + +% \def\dodefinelayerpreset[#1][#2]% +% {\setvalue{\??ll\??ll#1}{\dopresetlayer{#2}}} +% +% more fun: \definelayerpreset[whatever][lefttop] + +\def\dodefinelayerpreset[#1][#2]% + {\doifassignmentelse{#2} + {\setvalue{\??ll\??ll#1}{\dopresetlayer{#2}}} + {\setvalue{\??ll\??ll#1}{\csname\??ll\??ll#2\endcsname}}} + +\def\dopresetlayer#1#2#3% #1=list #2=tag #3=list + {\getparameters[\??ll#2][#1,#3]} + +\letempty\currentlayer + +\def\layerparameter#1{\csname\??ll\currentlayer#1\endcsname} + +\newdimen\layerwidth +\newdimen\layerheight + +\chardef\@@lacome=1 % LAyerCOnstructionMEthod / temp, will be default + +\def\dododosetlayer[#1][#2][#3]% will be sped up + {% we use the global width, never change this + \def\currentlayer{#1}% + \@@layerxsiz\layerparameter\c!width + \@@layerysiz\layerparameter\c!height + \layerwidth \@@layerxsiz + \layerheight\@@layerysiz + % preroll + \getparameters[\??ll\currentlayer][#3]% + % + % \executeifdefined{\??ll\??ll\layerparameter\c!preset}\gobbletwoarguments\currentlayer{#3}% + % + \edef\@@currentlayerpreset{\layerparameter\c!preset}% + \ifcsname\??ll\??ll\@@currentlayerpreset\endcsname\csname\??ll\??ll\@@currentlayerpreset\endcsname\currentlayer{#3}\fi + % + \doif{\layerparameter\c!position}\v!overlay % slow, use \dosetvalue instead + {\getparameters[\??ll\currentlayer][\c!width=\zeropoint,\c!height=\zeropoint,\c!position=\v!yes]}% + \doifsomething{\layerparameter\c!rotation}% todo: use direct lowlevel call + {\setbox\nextbox\hbox{\rotate[\c!location=\v!high,\c!rotation=\layerparameter\c!rotation]{\flushnextbox}}}% + % no, not local + % \@@layerxsiz\layerparameter\c!width + % \@@layerysiz\layerparameter\c!height + % never change that + \@@layerxpos\layerparameter\c!x + \@@layerypos\layerparameter\c!y + \doifelse{\layerparameter\c!hoffset}\v!max{\@@layerxoff\@@layerxsiz}{\@@layerxoff\layerparameter\c!hoffset}% + \doifelse{\layerparameter\c!voffset}\v!max{\@@layeryoff\@@layerysiz}{\@@layeryoff\layerparameter\c!voffset}% + % dx/dy are internal context ones and can be used in preset + \advance\@@layerxoff\dimexpr\layerparameter\c!offset+\layerparameter\c!dx\relax + \advance\@@layeryoff\dimexpr\layerparameter\c!offset+\layerparameter\c!dy\relax + \@@layerxpos\layerparameter\c!sx\@@layerxpos + \@@layerypos\layerparameter\c!sy\@@layerypos + \@@layerxoff\layerparameter\c!sx\@@layerxoff + \@@layeryoff\layerparameter\c!sy\@@layeryoff + \edef\@@currentlayerposition{\layerparameter\c!position}% + \ifx\@@currentlayerposition\v!yes % combine ^ + \setlastlayerpos{#2\currentlayer}% sets \layerpage; todo l/r %%%%%%%%%%%% + \@@layerxpos\lastlayerxpos + \@@layerypos\lastlayerypos + \letgvalue{\??ll\currentlayer\layerpage\c!position}\v!yes + \letgvalue{\??ll\currentlayer\c!state}\v!start % needed ? + \setbox\layerbox\vbox to \@@layerysiz{\hbox to \@@layerxsiz{\xypos{lyr:\the\currentlayerdata}\hss}\vss}% + \else + \setbox\layerbox\emptybox + \globallet\lastlayerxpos\!!zeropoint + \globallet\lastlayerypos\!!zeropoint + \normalexpanded{\noexpand\doifinset{\v!bottom}{\layerparameter\c!corner}}\dosetlayerbottompositions + \normalexpanded{\noexpand\doifinset{\v!right }{\layerparameter\c!corner}}\dosetlayerrightpositions + \normalexpanded{\noexpand\doifinset{\v!middle}{\layerparameter\c!corner}}\dosetlayermiddlepositions + \edef\layerpage{\layerparameter\c!page}% + \fi + \ifx\layerpage\empty \else % is expanded + \edef\layerpage{:\layerpage}% + \ifcsname\@@layerbox#2\currentlayer\layerpage\endcsname \else + \expandafter\newbox\csname\@@layerbox#2\currentlayer\layerpage\endcsname + \fi + \fi + \chardef\layerpagebox\csname\@@layerbox#2\currentlayer\layerpage\endcsname + \ifvoid\layerpagebox + \gsetboxllx\layerpagebox\zeropoint + \gsetboxlly\layerpagebox\zeropoint + \fi + \global\setbox\layerpagebox\vbox %to \layerparameter\c!height % new, otherwise no negative y possible + {\offinterlineskip + %postpone, to after nextboxwd correction % \hsize\layerparameter\c!width % new, keep box small + %\ifvoid\csname\@@layerbox\currentlayer\layerpage\endcsname\else % why not #2#1 + \ifvoid\layerpagebox + \let\lastlayerwidth \zeropoint + \let\lastlayerheight\zeropoint + \else + \edef\lastlayerwidth {\the\wd\layerpagebox}% + \edef\lastlayerheight{\the\ht\layerpagebox}% + \ht\layerpagebox\zeropoint + \dp\layerpagebox\zeropoint + \wd\layerpagebox\zeropoint + \doifnot{\layerparameter\c!direction}\v!reverse{\box\layerpagebox}% + \fi + % don't move + \xdef\lastlayerwd{\the\nextboxwd}% + \xdef\lastlayerht{\the\nextboxht}% % not entirely ok when grid ! + \xdef\lastlayerdp{\the\nextboxdp}% % not entirely ok when grid ! + % this code + \doifelse{\layerparameter\c!location}\v!grid\donetrue\donefalse + \ifdone + \nextboxht\strutheight + \nextboxdp\strutdepth + \else + \setbox\nextbox\hbox{\alignedbox[\layerparameter\c!location]\vbox{\flushnextbox}}% + \fi + \ifnum\layerparameter\c!line=\zerocount\else % no \ifcase, can be negative + \advance\@@layerypos\dimexpr\layerparameter\c!line\lineheight+\topskip-\lineheight-\nextboxht\relax + \fi + \ifnum\layerparameter\c!column=\zerocount\else % no \ifcase, can be negative + \advance\@@layerxpos\layoutcolumnoffset{\layerparameter\c!column}% + \fi + \ifdone + \setbox\nextbox\hbox{\alignedbox[rb]\vbox{\flushnextbox}}% + \fi + % ll registration + \scratchdimen\@@layerxpos + \advance\scratchdimen\@@layerxoff + \ifdim\scratchdimen<\getboxllx\layerpagebox + \gsetboxllx\layerpagebox\scratchdimen + \fi + \ifcase\@@lacome\or % this test will become obsolete + \advance\scratchdimen\nextboxwd + \nextboxwd\ifdim\scratchdimen>\lastlayerwidth \scratchdimen \else \lastlayerwidth \fi + \fi + \scratchdimen\dimexpr\@@layerypos+\@@layeryoff\relax + \ifdim\scratchdimen<\getboxlly\layerpagebox + \gsetboxlly\layerpagebox\scratchdimen + \fi + % ll compensation + \ifcase\@@lacome\or % this test will become obsolete + \advance\scratchdimen\dimexpr\nextboxht+\nextboxdp\relax + \nextboxht\ifdim\scratchdimen>\lastlayerheight \scratchdimen \else \lastlayerheight \fi + \nextboxdp\zeropoint + \fi + % placement + \hsize\layerparameter\c!width % new, keep box small + \vbox to \layerparameter\c!height \bgroup + \smashbox\nextbox + \vskip\dimexpr\@@layerypos+\@@layeryoff\relax + \hskip\dimexpr\@@layerxpos+\@@layerxoff\relax + \flushnextbox + \ifvoid\layerpagebox + % already flushed + \else + % the reverse case % check ! + \vskip-\dimexpr\@@layerypos+\@@layeryoff\relax + \box\layerpagebox + \fi + \egroup}% + % when position is true, the layerbox holds the compensation and needs + % to be placed; never change this ! + \ifvoid\layerbox\else\box\layerbox\fi} + +\def\dosetlayerbottompositions + {\ifnum\layerparameter\c!line=\zerocount\else % can be < 0 + \setevalue{\??ll\currentlayer\c!line}{\the\numexpr-\layerparameter\c!line+\layoutlines+\plusone\relax}% + \fi + \ifdim\@@layerysiz>\zeropoint + \advance\@@layerypos-\@@layerysiz + \@@layerypos-\@@layerypos + \@@layeryoff-\@@layeryoff + \fi} + +\def\dosetlayerrightpositions + {\ifnum\layerparameter\c!column=\zerocount\else % can be < 0 + \setevalue{\??ll\currentlayer\c!column}{\the\numexpr-\layerparameter\c!column+\layoutcolumns+\plusone\relax}% + \fi + \ifdim\@@layerxsiz>\zeropoint + \advance\@@layerxpos-\@@layerxsiz + \@@layerxpos-\@@layerxpos + \@@layerxoff-\@@layerxoff + \fi} + +\def\dosetlayermiddlepositions + {\ifdim\@@layerxsiz>\zeropoint \advance\@@layerxpos.5\@@layerxsiz \fi + \ifdim\@@layerysiz>\zeropoint \advance\@@layerypos.5\@@layerysiz \fi} + +%D Given the task to be accomplished, the previous macro is +%D not even that complicated. It mainly comes down to skipping +%D to the right place and placing a box on top of or below the +%D existing content. In the case of position tracking, another +%D reference point is chosen. + +%D \macros +%D {doifelselayerdata} + +\def\doifelselayerdata#1% + {\ifcsname\@@layerbox#1\endcsname + \ifvoid\csname\@@layerbox#1\endcsname + \@EAEAEA\secondoftwoarguments + \else + \@EAEAEA\firstoftwoarguments + \fi + \else + \@EA\secondoftwoarguments + \fi} + +%D \macros +%D {flushlayer} +%D +%D When we flush a layer, we flush both the main one and the +%D page dependent one (when defined). This feature is more +%D efficient in \ETEX\ since there testing for an undefined +%D macro does not takes hash space. + +% todo: setups before flush, handy hook + +% \unexpanded\def\flushlayer[#1]% +% {\doifelsevalue{\??ll#1\c!state}\v!next +% {\global\letvalue{\??ll#1\c!state}\v!start} % dangerous, stack-built-up +% {\doifelsevalue{\??ll#1\c!state}\v!continue +% {\global\letvalue{\??ll#1\c!state}\v!repeat} % dangerous, stack-built-up +% {\doifelsevalue{\??ll#1\c!doublesided}\v!yes +% {\doifundefinedelse{\@@layerbox#1}% +% {\dodoflushlayerA[#1]} +% {\doifbothsidesoverruled +% {\dodoflushlayerB\v!left [#1]}% left +% {\dodoflushlayerB\v!right[#1]}% right +% {\dodoflushlayerB\v!left [#1]}}}% left +% {\dodoflushlayerA[#1]}}}} + +\unexpanded\def\flushlayer[#1]% quite core, so optimized + {\begingroup + \forgetall + \edef\currentlayer{#1}% + \edef\@@currentlayerstate{\csname\??ll\currentlayer\c!state\endcsname}% + \ifx\@@currentlayerstate\v!stop + % nothing + \else\ifx\@@currentlayerstate\v!next + \global\expandafter\let\csname\??ll\currentlayer\c!state\endcsname\v!start % dangerous, stack-built-up + \else\ifx\@@currentlayerstate\v!continue + \global\expandafter\let\csname\??ll\currentlayer\c!state\endcsname\v!repeat % dangerous, stack-built-up + \else + \edef\@@currentlayerdoublesided{\csname\??ll\currentlayer\c!doublesided\endcsname}% + \ifx\@@currentlayerdoublesided\v!yes + \ifcsname\@@layerbox#1\endcsname + % we can make a dedicated one for this + \doifbothsidesoverruled{\dodoflushlayerB\v!left}{\dodoflushlayerB\v!right}{\dodoflushlayerB\v!left}% + \else + \dodoflushlayerA + \fi + \else + \dodoflushlayerA + \fi + \fi\fi\fi + \endgroup} + +% \ifcase#1\else\writestatus{layer}{unknown layer #3}\fi + +\def\dodoflushlayerA + {\startoverlay + {\ifcsname\@@layerbox\currentlayer \endcsname\dodoflushlayer\plusone \currentlayer \fi}% + {\ifcsname\@@layerbox\currentlayer:\realfolio\endcsname\dodoflushlayer\zerocount{\currentlayer:\realfolio}\fi}% + \stopoverlay} + +\def\dodoflushlayerB#1% + {\startoverlay + {\ifcsname\@@layerbox \currentlayer \endcsname\dodoflushlayer\plusone \currentlayer \fi}% + {\ifcsname\@@layerbox \currentlayer:\realfolio\endcsname\dodoflushlayer\zerocount {\currentlayer:\realfolio}\fi}% + {\ifcsname\@@layerbox#1\currentlayer \endcsname\dodoflushlayer\plusone {#1\currentlayer }\fi}% + {\ifcsname\@@layerbox#1\currentlayer:\realfolio\endcsname\dodoflushlayer\zerocount{#1\currentlayer:\realfolio}\fi}% + \stopoverlay} + +\def\dodoflushlayer#1#2% quite core, so optimized + {\begingroup % already grouped + \offinterlineskip + \edef\@@currentlayermethod{\csname\??ll\currentlayer\c!method\endcsname}% + \edef\@@currentlayeroption{\csname\??ll\currentlayer\c!option\endcsname}% + % needed because we need to handle method but we should find a way to + % speed this up + \edef\@@currentlayerpreset{\csname\??ll\currentlayer\c!preset\endcsname}% + \ifcsname\??ll\??ll\@@currentlayerpreset\endcsname\csname\??ll\??ll\@@currentlayerpreset\endcsname\currentlayer{}\fi + % + \ifx\@@currentlayeroption\v!test + \tracelayerstrue + \fi + \iftracelayers + \traceboxplacementtrue + \fi + \!!doneafalse + \!!donebfalse + \ifx\@@currentlayermethod\v!overlay\!!doneatrue\fi + \ifx\@@currentlayermethod\v!fit \!!donebtrue\fi + \!!donectrue + \ifcase#1\else + \edef\@@currentlayerposition{\csname\??ll\currentlayer\c!position\endcsname}% + \ifx\@@currentlayerposition\v!yes \else + \edef\@@currentlayerrepeat{\csname\??ll\currentlayer\c!repeat\endcsname}% + % \edef\@@currentlayerstate {\csname\??ll\currentlayer\c!state\endcsname}% actually this is already set + \ifx\@@currentlayerrepeat\v!yes + \!!donecfalse + \else\ifx\@@currentlayerstate\v!repeat + \!!donecfalse + \fi\fi + \fi + \fi + \chardef\layerbox\csname\@@layerbox#2\endcsname % \@@layerbox\currentlayer + % we need to copy in order to retain the negative offsets for a next + % stage of additions, i.e. llx/lly accumulate in repeat mode and the + % compensation may differ each flush depending on added content + \setbox\nextbox + \if!!doneb + \therepositionededlayerbox + \else + \if!!donec\box\else\copy\fi\layerbox % sorry for the delay due to copying + \fi + % todo: method=offset => overlayoffset right/down (handy for backgrounds with offset) + \doifoverlayelse{#2}{\setlayoutcomponentattribute\v!layer{#2}}\resetlayoutcomponentattribute + \iftracelayers \ruledvbox \else \vbox \fi \if!!donea to \overlayheight \fi \layoutcomponentboxattribute + {\hbox \if!!donea to \overlaywidth \fi + {\edef\@@currentlayerpageposition{\csname\??ll#2\realfolio\c!position\endcsname}% + \ifx\@@currentlayerpageposition\v!yes\xypos{lyr:#2:\realfolio}\fi + \box\nextbox + \hss}% + \vss}% + \if!!donec + \gsetboxllx\layerbox\zeropoint + \gsetboxlly\layerbox\zeropoint + \fi + \endgroup} + +\def\therepositionededlayerbox % assumes that \if!!donec is set (todo: use dedicated flags) + {\vbox + {\vskip-\getboxlly\layerbox + \hskip-\getboxllx\layerbox + \hsize-\dimexpr\getboxllx\layerbox-\wd\layerbox\relax + \if!!donec\box\else\copy\fi\layerbox}} + +% \definelayer[test][method=fit] \setupcolors[state=start] \tracelayerstrue +% +% \framed[framecolor=red,offset=overlay]{\setlayer[test]{aa}\setlayer[test][x=10pt]{g}\flushlayer[test]} +% \framed[framecolor=red,offset=overlay]{\setlayer[test]{aa}\setlayer[test][x=-10pt]{bb}\flushlayer[test]} +% \framed[framecolor=red,offset=overlay]{\setlayer[test][x=-20pt]{cccccc}\flushlayer[test]} +% \framed[framecolor=red,offset=overlay]{\setlayer[test]{dd}\setlayer[test][x=-20pt,y=-3pt]{eeeeee}\flushlayer[test]} + +%D \macros +%D {composedlayer,placelayer,tightlayer} +%D +%D This is a handy shortcut, which saves a couple of braces +%D when we use it as parameter. This name also suits better +%D to other layering commands. + +\def\composedlayer#1{\flushlayer[#1]} + +\let\placelayer\flushlayer + +\def\tightlayer[#1]% + {\hbox + {\def\currentlayer{#1}% todo: left/right + \setbox\nextbox\emptybox % hoogte/breedte are \wd\nextbox/\ht\nextbox + \hsize\layerparameter\c!width % \overlaywidth = \hsize + \vsize\layerparameter\c!height % \overlaywheight = \vsize + \composedlayer{#1}}} + +%D \macros +%D {resetlayer} +%D +%D This macro hardly needs an explanation (and is seldom +%D needed as well). + +\def\doresetlayer#1% + {\ifcsname\@@layerbox#1\endcsname + \global\setbox\csname\@@layerbox#1\endcsname\emptybox + \fi} + +\def\resetlayer[#1]% + {\doresetlayer{#1}% + \doifvalue{\??ll#1\c!doublesided}\v!yes % kind of redundant test + {\doresetlayer{\v!left #1}% + \doresetlayer{\v!right#1}}% + \doresetlayer{#1:\realfolio}} + +%D \macros +%D {setMPlayer} +%D +%D The following layer macro uses the positions that are +%D registered by \METAPOST. +%D +%D \starttyping +%D \definelayer[test] +%D +%D \setMPlayer [test] [somepos-1] {Whatever we want here!} +%D \setMPlayer [test] [somepos-2] {Whatever we need there!} +%D \setMPlayer [test] [somepos-3] {\externalfigure[cow.mps][width=2cm]} +%D +%D \startuseMPgraphic{oeps} +%D draw fullcircle scaled 10cm withcolor red ; +%D register ("somepos-1",2cm,3cm,center currentpicture) ; +%D register ("somepos-2",8cm,5cm,(-1cm,-2cm)) ; +%D register ("somepos-3",0cm,0cm,(-2cm,2cm)) ; +%D \stopuseMPgraphic +%D +%D \getMPlayer[test]{\useMPgraphic{oeps}} +%D \stoptyping +%D +%D The last line is equivalent to +%D +%D \starttyping +%D \framed +%D [background={foreground,test},offset=overlay] +%D {\useMPgraphic{oeps}} +%D \stoptyping + +\def\setMPlayer + {\dotripleempty\dosetMPlayer} + +\def\MPlayerwidth {\hsize} +\def\MPlayerheight{\vsize} + +\def\dosetMPlayer[#1][#2][#3]% + {\edef\MPlayerwidth {\MPw{#2}}% + \edef\MPlayerheight{\MPh{#2}}% + \setlayer[#1][\c!x=\MPx{#2},\c!y=\MPy{#2},\c!position=\v!no,#3]} + +\def\getMPlayer + {\dodoubleempty\dogetMPlayer} + +\def\dogetMPlayer[#1][#2]% + {\framed + [\c!background={\v!foreground,#1}, + \c!frame=\v!off, + \c!offset=\v!overlay,#2]} + +% The next mechanism is obsolete and will be removed in \MKIV\ (or move to +% the compatibility module. + +\newskip\xposition \newskip\yposition +\newskip\xdimension \newskip\ydimension +\newskip\xoffset \newskip\yoffset + +% already defined \newbox\positionbox + +\unexpanded\def\startpositioning + {\bgroup + \xposition \zeropoint \yposition \zeropoint + \xdimension\zeropoint \ydimension\zeropoint + \xoffset \zeropoint \yoffset \zeropoint + \hfuzz \paperwidth \vfuzz \paperheight + \setbox\positionbox\hbox\bgroup} + +\unexpanded\def\stoppositioning + {\doifnot\@@psoffset\v!yes + {\global\xoffset\zeropoint + \global\yoffset\zeropoint}% + \global\advance\xdimension \xoffset + \global\advance\ydimension \yoffset + \egroup + \vbox to \ydimension + {\vskip\yoffset + \hbox to \xdimension + {\hskip\xoffset + \box\positionbox + \hfill} + \vfill}% + \egroup} + +\def\resetpositioning + {\getparameters[\??ps] + [\c!state=\v!start,% + \c!unit=\s!cm,% + \c!factor=1,% + \c!scale=1,% + \c!xfactor=\@@psfactor,% + \c!yfactor=\@@psfactor,% + \c!xscale=\@@psscale,% + \c!yscale=\@@psscale,% + \c!xstep=\v!absolute,% + \c!ystep=\v!absolute,% + \c!xoffset=\!!zeropoint,% + \c!yoffset=\!!zeropoint]} + +\unexpanded\def\setuppositioning + {\resetpositioning + \dodoubleargument\getparameters[\??ps]} + +\def\calculateposition#1#2#3#4#5#6#7#8#9% + {\setdimensionwithunit\scratchskip{#1}\@@psunit + \scratchskip#8\scratchskip + \scratchskip#9\scratchskip + \advance\scratchskip #4\relax + \doif{#2}\v!relative + {\advance\scratchskip #3% + \let#4\!!zeropoint}% + #3\scratchskip\relax + \doifnot\@@psstate\v!overlay + {\scratchskip#5\relax + \advance\scratchskip #3\relax + \ifdim#3<-#7\relax \global#7-#3\relax \fi + \ifdim\scratchskip>#6\relax \global#6\scratchskip\relax \fi}} + +\def\position + {\dosingleempty\doposition} + +\def\doposition[#1]#2(#3,#4)% + {\dowithnextbox + {\bgroup + \getparameters[\??ps][#1]% + \dontcomplain + \calculateposition{#3}\@@psxstep\xposition + \@@psxoffset{\nextboxwd}\xdimension\xoffset + \@@psxscale\@@psxfactor + \scratchdimen\nextboxht \advance\scratchdimen \nextboxdp + \calculateposition{#4}\@@psystep\yposition + \@@psyoffset\scratchdimen\ydimension\yoffset + \@@psyscale\@@psyfactor + \vbox to \zeropoint % kan beter. + {\vskip\yposition + \hbox to \zeropoint + {\hskip\xposition + \flushnextbox + \hss} + \vss}% + \xdef\dopoppositioning + {\xposition\the\xposition + \yposition\the\yposition + \noexpand\def\noexpand\@@psxoffset{\@@psxoffset}% + \noexpand\def\noexpand\@@psyoffset{\@@psyoffset}}% + \egroup + \dopoppositioning + \ignorespaces} + \hbox} + +\resetpositioning + +\setuppositioning + [\c!unit=\s!cm, + \c!factor=1, + \c!scale=1, + \c!xstep=\v!absolute, + \c!ystep=\v!absolute, + \c!offset=\v!yes, + \c!xoffset=\!!zeropoint, + \c!yoffset=\!!zeropoint] + +%D Watch out, a redefinition: + +\ifx\settextpagecontent\undefined \writestatus\m!systems{error in page-lyr.tex} \wait \fi + +\let\normalsettextpagecontent\settextpagecontent + +\definelayer + [OTRTEXT] + +\setuplayer + [OTRTEXT] + [\c!width=\innermakeupwidth, + \c!height=\textheight] + +% will be overloaded in page-spr + +\def\settextpagecontent#1#2#3% #2 and #3 will disappear + {\doifelselayerdata{OTRTEXT} + {\setbox#1\hbox to \makeupwidth + {\startoverlay + {\tightlayer[OTRTEXT]} % first, otherwise problems with toc + {\normalsettextpagecontent{#1}{#2}{#3}\box#1} + \stopoverlay}% + \dp#1\zeropoint}% + {\normalsettextpagecontent{#1}{#2}{#3}}} + +\protect \endinput diff --git a/tex/context/base/pack-obj.lua b/tex/context/base/pack-obj.lua new file mode 100644 index 000000000..0ee46fc99 --- /dev/null +++ b/tex/context/base/pack-obj.lua @@ -0,0 +1,54 @@ +if not modules then modules = { } end modules ['pack-obj'] = { + version = 1.001, + comment = "companion to pack-obj.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +--[[ldx-- +We save object references in the main utility table. jobobjects are +reusable components.
+--ldx]]-- + +local texsprint, texcount = tex.sprint, tex.count + +jobobjects = jobobjects or { } +jobobjects.collected = jobobjects.collected or { } +jobobjects.tobesaved = jobobjects.tobesaved or { } + +local collected, tobesaved = jobobjects.collected, jobobjects.tobesaved + +local function initializer() + collected, tobesaved = jobobjects.collected, jobobjects.tobesaved +end + +job.register('jobobjects.collected', jobobjects.tobesaved, initializer, nil) + +function jobobjects.save(tag,number,page) + local t = { number, page } + tobesaved[tag], collected[tag] = t, t +end + +function jobobjects.set(tag,number,page) + collected[tag] = { number, page } +end + +function jobobjects.get(tag) + return collected[tag] or tobesaved[tag] +end + +function jobobjects.number(tag,default) + local o = collected[tag] or tobesaved[tag] + texsprint((o and o[1]) or default) +end + +function jobobjects.page(tag,default) + local o = collected[tag] or tobesaved[tag] + texsprint((o and o[2]) or default) +end + +function jobobjects.doifelse(tag) + commands.testcase(collected[tag] or tobesaved[tag]) +end + diff --git a/tex/context/base/pack-obj.mkii b/tex/context/base/pack-obj.mkii new file mode 100644 index 000000000..6971ad04f --- /dev/null +++ b/tex/context/base/pack-obj.mkii @@ -0,0 +1,371 @@ +%D \module +%D [ file=pack-obj, +%D version=1998.01.15, +%D title=\CONTEXT\ Packaging Macros, +%D subtitle=Objects, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +% todo, move more to mkiv, get rid of blabelgroup + +\writestatus{loading}{ConTeXt Packaging Macros / Objects} + +\unprotect + +%D \macros +%D {setobject,getobject,ifinobject} +%D +%D Boxes can be considered reuable objects. Unfortunaltely once +%D passed to the \DVI\ file, such objects cannot be reused. In +%D \PDF\ however, reusing is possible and sometimes even a +%D necessity. Therefore, \CONTEXT\ supports reusable objects. +%D +%D During the \TEX\ processing run, boxes can serve the purpose +%D of objects, and the \DVI\ driver module implements objects +%D using packed boxes. +%D +%D The \PDF\ and \PDFTEX\ driver modules implement objects +%D using \PDF\ forms. There is no (real) restriction on the +%D number of objects there. +%D +%D The first application of objects in \CONTEXT\ concerned +%D \METAPOST\ graphics and fill||in form fields. The first +%D application can save lots of bytes, while the latter use is +%D more a necessity than byte saving. +%D +%D \starttyping +%D \setobject{class}{name}\somebox{} +%D \getobject{class}{name} +%D \stoptyping +%D +%D Here \type{\somebox} can be whatever box specification suits +%D \TEX. We save the dimensions of an object, although some +%D drivers will do so themselves. This means that when for +%D instance using \PDFTEX\ we could save a hash entry plus some +%D 20+ memory locations per object by delegating this +%D housekeeping to the driver. The current approach permits +%D us to keep the box characteristic too. + +\newif\ifinobject + +\def\checkobjectreferences + {\startnointerference + \protectlabels + \ifx\usedoutputdriver\currentoutput + \doutilities{objectreferences}\jobname\empty\relax\relax + \else + % different format (will fails on \purenumber) + \fi + \global\let\checkobjectreferences\relax + \stopnointerference} + +\def\objectplaceholder{NOT YET FLUSHED}% + +\def\presetobject#1#2% \global added + {\blabelgroup + \ifcsname\r!object#1::#2\endcsname\else + \global\@EA\let\csname\r!object#1::#2\endcsname\objectplaceholder + \fi + \elabelgroup} + +\def\dosetobject#1#2#3% \initializepaper this will move to \everyshipout + {\initializepaper + \blabelgroup + \ifcsname\r!object#2::#3\endcsname + \elabelgroup \expandafter\gobblefivearguments + \else % tzt, overload internal referenced objects to save entries + \elabelgroup \expandafter\dodosetobject + \fi + {#1}{#2}{#3}} + +\def\resetobject#1#2% + {\checkobjectreferences + \letbeundefined{\r!object#1::#2}} + +%D \macros +%D {finalizeobjectbox} +%D +%D This one provides a hook for last minute object box processing +%D we need this in \MKIV. + +\ifx\finalizeobjectbox\undefined + \let\finalizeobjectbox\gobbleoneargument +\fi + +%D Somehow there is a rounding error problem in either \PDFTEX\ +%D or in viewers, or maybe it is conforming the specs. The next +%D variable compensate for it by removing the rather tight +%D clip. + +\def\objectoffset{1cm} + +% \def\dodosetobject#1#2#3% +% {\bgroup +% \inobjecttrue +% \dowithnextbox{\dododosetobject{#1}{#2}{#3}\egroup}} + +\def\dodosetobject#1#2#3% + {\bgroup + \globalpushmacro\crossreferenceobject \objectreferenced + \inobjecttrue + \dowithnextbox + {\globalpopmacro\crossreferenceobject + \dododosetobject{#1}{#2}{#3}\egroup}} + +\def\dododosetobject#1#2#3% + {\blabelgroup + \dontshowcomposition % rather fuzzy in \setxvalue ... \hbox + \scratchdimen\objectoffset + \@EA\xdef\csname\r!object#2::#3\endcsname + {\noexpand\dohandleobject{#2}{#3}% + {\ifhbox\nextbox\hbox\else\vbox\fi}% + %{\the\nextboxwd}{\the\nextboxht}{\the\nextboxdp}}% + {\number\nextboxwd}{\number\nextboxht}{\number\nextboxdp}% + {\number\scratchdimen}}% + \expanded % freeze the dimensions since \dostartobject may use \nextbox + {\dostartobject + {#2}{#3}{\the\nextboxwd}{\the\nextboxht}{\the\nextboxdp}}% + \ifcase#1\relax\else \ifdim\objectoffset>\zeropoint + \setbox\nextbox\vbox spread 2\scratchdimen + {\forgetall \offinterlineskip + \vss\hbox spread 2\scratchdimen{\hss\flushnextbox\hss}\vss}% + \fi \fi + \flushnextbox + \dostopobject + \elabelgroup} + +\def\getobject#1#2% + {\blabelgroup + \let\dohandleobject\dogetobject + \csname\r!object#1::#2\endcsname} + +% \def\dogetobject#1#2#3#4#5#6% +% {\initializepaper +% \forgetall +% \dontshowcomposition +% \setbox\scratchbox\vbox +% {\doinsertobject{#1}{#2}}% +% \setbox\scratchbox#3% +% {\vbox to #5\scaledpoint +% {\ifdim\ht\scratchbox>#5\scaledpoint +% % or \ifdim\wd\scratchbox>#4\scaledpoint +% \vss\hbox to #4\scaledpoint{\hss\box\scratchbox\hss}\vss +% \else +% \vss\box\scratchbox +% \fi}}% +% \wd\scratchbox#4\scaledpoint +% \ht\scratchbox#5\scaledpoint +% \dp\scratchbox#6\scaledpoint +% \box\scratchbox +% \elabelgroup} + +% \def\dogetobject#1#2#3#4#5#6#7% +% {\initializepaper +% \forgetall +% \dontshowcomposition +% \setbox\scratchbox\vbox +% {\doinsertobject{#1}{#2}}% +% \setbox\scratchbox#3% +% {\vbox to #5\scaledpoint +% {\ifdim\ht\scratchbox>#5\scaledpoint +% % or \ifdim\wd\scratchbox>#4\scaledpoint +% \vss\hbox to #4\scaledpoint{\hss\box\scratchbox\hss}\vss +% \else +% \vss\box\scratchbox +% \fi}}% +% \scratchdimen#7\scaledpoint +% \setbox\nextbox\hbox +% {\hskip-\scratchdimen\lower\scratchdimen\flushnextbox}% +% \wd\scratchbox#4\scaledpoint +% \ht\scratchbox#5\scaledpoint +% \dp\scratchbox#6\scaledpoint +% \box\scratchbox +% \elabelgroup} + +\def\dogetobject#1#2#3#4#5#6#7% don't change this, should work for dvi & pdf + {\initializepaper + \forgetall + \dontshowcomposition + \setbox\scratchbox\vbox + {\doinsertobject{#1}{#2}}% + \setbox\scratchbox#3% + {\vbox to #5\scaledpoint + {\ifdim\ht\scratchbox>#5\scaledpoint + \vss\hbox to #4\scaledpoint{\hss\box\scratchbox\hss}\vss + \else\ifdim\wd\scratchbox>#4\scaledpoint + \vss\hbox to #4\scaledpoint{\hss\box\scratchbox\hss}\vss + \else + %\vss\box\scratchbox + \vss\hbox to #4\scaledpoint{\box\scratchbox\hss}% fix Chof + \fi\fi}}% + \box\scratchbox + \elabelgroup} + +%D If needed one can ask for the dimensions of an object with: +%D +%D \starttyping +%D \getobjectdimensions{class}{name} +%D \stoptyping +%D +%D The results are reported in \type {\objectwidth}, \type +%D {\objectheight} and \type {\objectdepth}. + +% \def\dogetobjectdimensions#1#2#3#4#5#6% +% {\def\objectwidth {#4\s!sp}% +% \def\objectheight{#5\s!sp}% +% \def\objectdepth {#6\s!sp}} + +\def\dogetobjectdimensions#1#2#3#4#5#6#7% + {\def\objectwidth {#4\s!sp}% + \def\objectheight{#5\s!sp}% + \def\objectdepth {#6\s!sp}% + \def\objectmargin{#7\s!sp}} + +\def\getobjectdimensions#1#2% + {\let\dohandleobject\dogetobjectdimensions + \let\objectwidth \!!zeropoint + \let\objectheight\!!zeropoint + \let\objectdepth \!!zeropoint + \labelcsname\r!object#1::#2\endcsname} + +%D Apart from this kind of objects, that have typeset content, +%D we can have low level driver specific objects. Both types +%D can have references to internal representations, hidden for +%D the user. We keep track of such references by means of a +%D dedicated cross reference mechanism. Normally, objects are +%D defined before they are used, but forward referencing +%D sometimes occurs. +%D +%D \starttyping +%D \dosetobjectreference {class} {identifier} {reference value} {page} +%D \dogetobjectreference {class} {identifier} \csname +%D \stoptyping +%D +%D These commands are to be called by the \type{\startobject}, +%D \type{\stopobject} and \type{\insertobject} specials. + +\def\objectreferenced{\global\chardef\crossreferenceobject\plusone} +\def\driverreferenced{\global\chardef\crossreferenceobject\zerocount} + +\objectreferenced + +% no undefined test ! ! ! ! (pdftex fails on undefined objects) + +\def\setobjectreferences + {\def\objectreference##1##2##3##4% + {\ifundefined{\r!driver##1::##2}% + \setxvalue{\r!driver##1::##2}{{##3}{##4}}% + \else + \showmessage\m!references{31}{[##1 ##2=>##3/##4]}% + \fi}} + +\def\resetobjectreferences + {\let\objectreference\gobblefourarguments} + +\resetobjectreferences + +\def\doregisterobjectreference#1#2#3% + {\checkobjectreferences + \blabelgroup + \expanded{\writeutilitycommand{\noexpand\objectreference{#1}{#2}{#3}{\noexpand\realfolio}}}% + \setxvalue{\r!driver#1::#2}{{#3}{\noexpand\realfolio}}% + \elabelgroup} + +\def\dooverloadobjectreference#1#2#3% + {\checkobjectreferences + \blabelgroup + \setxvalue{\r!driver#1::#2}{{#3}{\noexpand\realfolio}}% + \elabelgroup} + +\def\dosetobjectreference + {\ifcase\crossreferenceobject + \objectreferenced + \expandafter\dooverloadobjectreference + \else + \expandafter\doregisterobjectreference + \fi} + +\def\dosetdriverreference + {\driverreferenced\dosetobjectreference} + +\def\defaultobjectreference#1#2{0} % driver dependent +\def\defaultobjectpage #1#2{\realfolio} + +\def\dogetobjectreference {\dodogetobjectreference\firstoftwoarguments\defaultobjectreference} +\def\dogetobjectreferencepage{\dodogetobjectreference\secondoftwoarguments\defaultobjectpage} + +\def\dodogetobjectreference#1#2#3#4#5% + {\checkobjectreferences + \blabelgroup + \ifundefined{\r!driver#3::#4}% + \showmessage\m!references{30}{[#3 #4=>#3/#4]}% + \xdef#5{#2{#3}{#4}}% + \else + \xdef#5{\@EAEAEA#1\csname\r!driver#3::#4\endcsname}% + \fi + \elabelgroup} + +\def\setobject {\driverreferenced\dosetobject1} +\def\settightobject{\driverreferenced\dosetobject0} + +%D \macros +%D {doifobjectfoundelse,doifobjectreferencefoundelse} +%D +%D To prevent redundant definition of objects, one can use +%D the next tests: +%D +%D \starttyping +%D \doifobjectfoundelse{class}{object}{do then}{do else} +%D \doifobjectreferencefoundelse{class}{object}{do then}{do else} +%D \stoptyping + +\def\doifobjectfoundelse#1#2% + {\blabelgroup \ifcsname\r!object#1::#2\endcsname + \elabelgroup \expandafter\firstoftwoarguments + \else + \elabelgroup \expandafter\secondoftwoarguments + \fi} + +\def\doifobjectreferencefoundelse#1#2% + {\checkobjectreferences + \blabelgroup \ifcsname\r!driver#1::#2\endcsname + \elabelgroup \expandafter\firstoftwoarguments + \else + \elabelgroup \expandafter\secondoftwoarguments + \fi} + +%D \macros +%D {doifobjectssupportedelse} +%D +%D Starting with reuse of graphics, we will implement object +%D reuse when possible. To enable mechanisms to determine +%D what method to use, we provide: +%D +%D \starttyping +%D \doifobjectssupportedelse{true action}{false action} +%D \stoptyping +%D +%D As we can see, currently objects depend on the special +%D driver. + +\newif\ifobjectssupported \objectssupportedtrue + +\def\doifobjectssupportedelse + {\ifobjectssupported + \@EA\doifspecialavailableelse\@EA\doinsertobject + \else + \@EA\secondoftwoarguments + \fi} + +%D There is a conceptual problem here. Objects are not possible +%D in \DVI, unless faked like in \type {spec-dvi}. This means +%D that we must be careful in loading special drivers that do +%D support objects while we still want to be able to use the +%D \DVI\ output. + +\protect \endinput diff --git a/tex/context/base/pack-obj.mkiv b/tex/context/base/pack-obj.mkiv new file mode 100644 index 000000000..81220e324 --- /dev/null +++ b/tex/context/base/pack-obj.mkiv @@ -0,0 +1,399 @@ +%D \module +%D [ file=pack-obj, +%D version=1998.01.15, +%D title=\CONTEXT\ Packaging Macros, +%D subtitle=Objects, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Packaging Macros / Objects} + +\unprotect + +\let\objectreference\gobblefourarguments % catch mkii tuo stuff + +\registerctxluafile{pack-obj}{1.001} + +% \startluacode +% local texbox, texdimen, texcount, texwrite = tex.box, tex.dimen, tex.count, tex.write +% local pdfxform, pdfrefxform = pdf.xform, pdf.refxform +% +% function pdf.xform (l) texbox["objectbox"] = nil return l end +% function pdf.refxform(l) return node.copy_list(l) end +% +% backends.codeinjections.register = pdf.xform +% backends.codeinjections.restore = pdf.refxform +% +% local codeinjections = backends.codeinjections +% +% objects = objects or { } +% +% local data = { } +% +% objects.data = data +% objects.n = 0 +% +% function objects.register(name) +% objects.n = objects.n + 1 +% local list = texbox.objectbox +% nodes.process_page(list) +% data[name] = { +% codeinjections.restore(list), +% texdimen.objectwd, +% texdimen.objectht, +% texdimen.objectdp, +% texdimen.objectoff, +% } +% end +% +% function objects.restore(name) +% local d = data[name] +% if d then +% texbox .objectbox = codeinjections.restore(d[1]) +% texdimen.objectwd = d[2] +% texdimen.objectht = d[3] +% texdimen.objectdp = d[4] +% texdimen.objectoff = d[5] +% else +% texbox .objectbox = nil +% texdimen.objectwd = 0 +% texdimen.objectht = 0 +% texdimen.objectdp = 0 +% texdimen.objectoff = 0 +% end +% end +% +% function objects.reference(name) +% local d = data[name] +% texwrite((d and d[1]) or 0) +% end +% +% function objects.enhance(name) +% local d = data[name] +% if d then +% d[6] = texcount.realpageno +% end +% end +% +% function objects.page(name) +% local d = data[name] +% texwrite((d and d[6]) or texcount.realpageno) +% end +% +% function objects.doifelse(name) +% commands.testcase(data[name]) +% end +% \stopluacode +% +% \newbox \objectbox +% \newtoks \everyobject +% \newif \ifinobject +% +% \newdimen\objectoff \def\objectmargin{\the\objectoff} +% \newdimen\objectwd \def\objectwidth {\the\objectwd } +% \newdimen\objectht \def\objectheight{\the\objectht } +% \newdimen\objectdp \def\objectdepth {\the\objectdp } +% +% \def\objectoffset{1cm} +% +% \everyobject{\the\everyPDFxform} +% +% \let\doresetobjects\relax +% +% \def\setobject #1#2{\begingroup\objectoff\objectoffset\inobjecttrue\the\everyobject\dowithnextbox{\dosetobject{#1}{#2}}} +% \def\settightobject#1#2{\begingroup\objectoff\zeropoint \inobjecttrue\the\everyobject\dowithnextbox{\dosetobject{#1}{#2}}} +% +% \let\objectsetvbox\vbox %\def\objectsetvbox{\ruledvbox} +% \let\objectgetvbox\vbox %\def\objectgetvbox{\ruledvbox} +% \let\objectsethbox\hbox %\def\objectsethbox{\ruledhbox} +% \let\objectgethbox\hbox %\def\objectgethbox{\ruledhbox} +% +% \def\dosetobject#1#2% +% {\objectwd\wd\nextbox +% \objectht\ht\nextbox +% \objectdp\dp\nextbox +% \ifdim\objectoff=\zeropoint\relax +% \setbox\objectbox\box\nextbox +% \else +% \setbox\objectbox\objectsetvbox spread 2\objectoff{\vss\objectsethbox spread 2\objectoff{\hss\flushnextbox\hss}\vss}% +% \fi +% \ctxlua{objects.register("#1::#2")}% +% \endgroup} +% +% \def\getobject#1#2% +% {\begingroup +% \ctxlua{objects.restore("#1::#2")}% +% \ifdim\objectoff=\zeropoint\relax \else +% \setbox\objectbox\objectgetvbox to \dimexpr\objectht+\objectdp\relax +% {\vss\objectgethbox to \objectwd{\hss\box\objectbox\hss}\vss}% +% \wd\objectbox\objectwd +% \ht\objectbox\objectht +% \dp\objectbox\objectdp +% \fi +% \box\objectbox +% \endgroup} +% +% \def\getpageobject#1#2% +% {\begingroup +% \ctxlua{objects.restore("#1::#2")}% +% \ifdim\objectoff=\zeropoint\relax +% \setbox\objectbox\objectgethbox{\ctxlatelua{objects.enhance("#1::#2")}\box\objectbox} +% \else +% \setbox\objectbox\objectgetvbox to \dimexpr\objectht+\objectdp\relax +% {\vss\objectgethbox to \objectwd{\ctxlatelua{objects.enhance("#1::#2")}\hss\box\objectbox\hss}\vss}% +% \wd\objectbox\objectwd +% \ht\objectbox\objectht +% \dp\objectbox\objectdp +% \fi +% \box\objectbox +% \endgroup} +% +% \def\setobjectdirectly #1#2{\ctxlua{objects.register("#1::#2")}} +% \def\getobjectdirectly #1#2{\ctxlua{objects.restore ("#1::#2")}} +% \def\getobjectdimensions #1#2{\ctxlua{objects.restore ("#1::#2")}} +% \def\doifobjectfoundelse #1#2{\ctxlua{objects.doifelse("#1::#2")}} +% \def\doifobjectreferencefoundelse#1#2{\ctxlua{objects.doifelse("#1::#2")}} +% +% \let\objectreferenced\relax +% \let\driverreferenced\relax +% +% \def\doregisterobjectreference{\writestatus{objects}{obsolete: register object reference}\gobblethreearguments} +% \def\dooverloadobjectreference{\writestatus{objects}{obsolete: overload object reference}\gobblethreearguments} +% \def\dosetobjectreference {\writestatus{objects}{obsolete: set object reference}\gobblethreearguments} +% \def\dosetdriverreference {\writestatus{objects}{obsolete: set driver reference}\gobblethreearguments} +% +% \def\defaultobjectreference{0} +% \def\defaultobjectpage {\realfolio} +% +% \def\dogetobjectreference #1#2#3{\xdef#3{\ctxlua{objects.reference("#1::#2)}}} +% \def\dogetobjectreferencepage#1#2#3{\xdef#3{\ctxlua{objects.page("#1::#2))}}} +% +% \protect +% +% \starttext +% test \setobject{a}{b}\ruledhbox{xxx}\getobject{a}{b} test +% \vskip3cm +% test \settightobject{a}{b}\ruledhbox{xxx}\getobject{a}{b} test +% test \settightobject{a}{c}\ruledhbox{xxx}\getobject{a}{c} test +% \dorecurse{5000}{test \setobject{a}{b}\ruledhbox{xxx}\getobject{a}{b} test } +% \stoptext + +%D \macros +%D {setobject,getobject,ifinobject} +%D +%D Boxes can be considered reuable objects. Unfortunaltely once +%D passed to the \DVI\ file, such objects cannot be reused. In +%D \PDF\ however, reusing is possible and sometimes even a +%D necessity. Therefore, \CONTEXT\ supports reusable objects. +%D +%D During the \TEX\ processing run, boxes can serve the purpose +%D of objects, and the \DVI\ driver module implements objects +%D using packed boxes. +%D +%D The \PDF\ and \PDFTEX\ driver modules implement objects +%D using \PDF\ forms. There is no (real) restriction on the +%D number of objects there. +%D +%D The first application of objects in \CONTEXT\ concerned +%D \METAPOST\ graphics and fill||in form fields. The first +%D application can save lots of bytes, while the latter use is +%D more a necessity than byte saving. +%D +%D \starttyping +%D \setobject{class}{name}\somebox{} +%D \getobject{class}{name} +%D \stoptyping +%D +%D Here \type{\somebox} can be whatever box specification suits +%D \TEX. We save the dimensions of an object, although some +%D drivers will do so themselves. This means that when for +%D instance using \PDFTEX\ we could save a hash entry plus some +%D 20+ memory locations per object by delegating this +%D housekeeping to the driver. The current approach permits +%D us to keep the box characteristic too. + +\newif\ifinobject + +\def\objectplaceholder{NOT YET FLUSHED}% + +\def\presetobject#1#2% \global added + {\ifcsname\r!object#1::#2\endcsname\else + \global\@EA\let\csname\r!object#1::#2\endcsname\objectplaceholder + \fi} + +\def\dosetobject#1#2#3% \initializepaper this will move to \everyshipout + {% \initializepaper + \ifcsname\r!object#2::#3\endcsname + \expandafter\gobblefivearguments + \else % tzt, overload internal referenced objects to save entries + \expandafter\dodosetobject + \fi + {#1}{#2}{#3}} + +\def\resetobject#1#2% + {\letbeundefined{\r!object#1::#2}} + +%D \macros +%D {finalizeobjectbox} +%D +%D This one provides a hook for last minute object box processing +%D we need this in \MKIV. + +\ifx\finalizeobjectbox\undefined + \let\finalizeobjectbox\gobbleoneargument +\fi + +%D Somehow there is a rounding error problem in either \PDFTEX\ +%D or in viewers, or maybe it is conforming the specs. The next +%D variable compensate for it by removing the rather tight +%D clip. + +\def\objectoffset{1cm} + +\def\dodosetobject#1#2#3% + {\bgroup + \globalpushmacro\crossreferenceobject \objectreferenced + \inobjecttrue + \dowithnextbox + {\globalpopmacro\crossreferenceobject + \dododosetobject{#1}{#2}{#3}\egroup}} + +\def\dododosetobject#1#2#3% + {\begingroup + \dontshowcomposition % rather fuzzy in \setxvalue ... \hbox + \scratchdimen\objectoffset + \@EA\xdef\csname\r!object#2::#3\endcsname + {\noexpand\dohandleobject{#2}{#3}% + {\ifhbox\nextbox\hbox\else\vbox\fi}% + {\number\nextboxwd}{\number\nextboxht}{\number\nextboxdp}% + {\number\scratchdimen}}% + \expanded % freeze the dimensions since \dostartobject may use \nextbox + {\dostartobject{#2}{#3}{\the\nextboxwd}{\the\nextboxht}{\the\nextboxdp}}% + \ifcase#1\relax\else \ifdim\objectoffset>\zeropoint + \setbox\nextbox\vbox spread 2\scratchdimen + {\forgetall \offinterlineskip + \vss\hbox spread 2\scratchdimen{\hss\flushnextbox\hss}\vss}% + \fi \fi + \flushnextbox + \dostopobject + \endgroup} + +\def\getobject#1#2% + {\begingroup + \let\dohandleobject\dogetobject + \csname\r!object#1::#2\endcsname} + +\def\dogetobject#1#2#3#4#5#6#7% don't change this, should work for dvi & pdf + {% \initializepaper + \forgetall + \dontshowcomposition + \setbox\scratchbox\vbox attr \viewerlayerattribute \attribute\viewerlayerattribute + {\doinsertobject{#1}{#2}}% + \setbox\scratchbox#3% + {\vbox to #5\scaledpoint + {\ifdim\ht\scratchbox>#5\scaledpoint + \vss\hbox to #4\scaledpoint{\hss\box\scratchbox\hss}\vss + \else\ifdim\wd\scratchbox>#4\scaledpoint + \vss\hbox to #4\scaledpoint{\hss\box\scratchbox\hss}\vss + \else + %\vss\box\scratchbox + \vss\hbox to #4\scaledpoint{\box\scratchbox\hss}% fix Chof + \fi\fi}}% +% \forcecolorhack % needed in order to use layers etc + \box\scratchbox + \endgroup} + +%D If needed one can ask for the dimensions of an object with: +%D +%D \starttyping +%D \getobjectdimensions{class}{name} +%D \stoptyping +%D +%D The results are reported in \type {\objectwidth}, \type +%D {\objectheight} and \type {\objectdepth}. + +\def\dogetobjectdimensions#1#2#3#4#5#6#7% + {\def\objectwidth {#4\s!sp}% + \def\objectheight{#5\s!sp}% + \def\objectdepth {#6\s!sp}% + \def\objectmargin{#7\s!sp}} + +\def\getobjectdimensions#1#2% + {\let\dohandleobject\dogetobjectdimensions + \let\objectwidth \!!zeropoint + \let\objectheight\!!zeropoint + \let\objectdepth \!!zeropoint + \csname\r!object#1::#2\endcsname} + +%D Apart from this kind of objects, that have typeset content, +%D we can have low level driver specific objects. Both types +%D can have references to internal representations, hidden for +%D the user. We keep track of such references by means of a +%D dedicated cross reference mechanism. Normally, objects are +%D defined before they are used, but forward referencing +%D sometimes occurs. +%D +%D \starttyping +%D \dosetobjectreference {class} {identifier} {reference value} {page} +%D \dogetobjectreference {class} {identifier} \csname +%D \stoptyping +%D +%D These commands are to be called by the \type{\startobject}, +%D \type{\stopobject} and \type{\insertobject} specials. + +\def\objectreferenced{\global\chardef\crossreferenceobject\plusone} +\def\driverreferenced{\global\chardef\crossreferenceobject\zerocount} + +\objectreferenced + +% no undefined test ! ! ! ! (pdftex fails on undefined objects) + +\def\doregisterobjectreference#1#2#3{\normalexpanded{\noexpand\ctxlatelua{jobobjects.save("#1::#2",#3,\noexpand\the\realpageno)}}} +\def\dooverloadobjectreference#1#2#3{\ctxlua{jobobjects.set("#1::#2",#3,\the\realpageno)}} + +\def\dosetobjectreference + {\ifcase\crossreferenceobject + \objectreferenced + \expandafter\dooverloadobjectreference + \else + \expandafter\doregisterobjectreference + \fi} + +\def\dosetdriverreference + {\driverreferenced\dosetobjectreference} + +\def\defaultobjectreference#1#2{0} % driver dependent +\def\defaultobjectpage #1#2{\realfolio} + +\def\dogetobjectreference #1#2#3{\xdef#3{\ctxlua{jobobjects.number("#1::#2","\defaultobjectreference{#1}{#2}")}}} +\def\dogetobjectreferencepage#1#2#3{\xdef#3{\ctxlua{jobobjects.page("#1::#2","\defaultobjectpage{#1}{#2}")}}} + +\def\setobject {\driverreferenced\dosetobject1} +\def\settightobject{\driverreferenced\dosetobject0} + +%D \macros +%D {doifobjectfoundelse,doifobjectreferencefoundelse} +%D +%D To prevent redundant definition of objects, one can use +%D the next tests: +%D +%D \starttyping +%D \doifobjectfoundelse{class}{object}{do then}{do else} +%D \doifobjectreferencefoundelse{class}{object}{do then}{do else} +%D \stoptyping + +\def\doifobjectfoundelse#1#2% + {\ifcsname\r!object#1::#2\endcsname + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +\def\doifobjectreferencefoundelse#1#2{\ctxlua{jobobjects.doifelse("#1::#2")}} + +\protect \endinput diff --git a/tex/context/base/pack-rul.lua b/tex/context/base/pack-rul.lua new file mode 100644 index 000000000..378167c97 --- /dev/null +++ b/tex/context/base/pack-rul.lua @@ -0,0 +1,53 @@ +if not modules then modules = { } end modules ['pack-rul'] = { + version = 1.001, + comment = "companion to pack-rul.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +--[[ldx-- +An explanation is given in the history document