summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Hagen <pragma@wxs.nl>2020-12-08 12:12:33 +0100
committerContext Git Mirror Bot <phg@phi-gamma.net>2020-12-08 12:12:33 +0100
commit59aead50be62c503185af6459f099dac0ebee313 (patch)
treec41bf2d7d49046bfe474663e6371ac4889f1e4ff
parentaff2893a6d6652223e92e3de18e1260cbd533fea (diff)
downloadcontext-59aead50be62c503185af6459f099dac0ebee313.tar.gz
2020-12-08 11:09:00
-rw-r--r--doc/context/documents/general/manuals/luametatex.pdfbin1283945 -> 1283942 bytes
-rw-r--r--doc/context/sources/general/manuals/luametatex/luametatex.tex4
-rw-r--r--tex/context/base/mkii/cont-new.mkii2
-rw-r--r--tex/context/base/mkii/context.mkii2
-rw-r--r--tex/context/base/mkiv/cont-new.mkiv2
-rw-r--r--tex/context/base/mkiv/context.mkiv2
-rw-r--r--tex/context/base/mkiv/font-cft.lua6
-rw-r--r--tex/context/base/mkiv/font-chk.lua6
-rw-r--r--tex/context/base/mkiv/font-ctx.lua37
-rw-r--r--tex/context/base/mkiv/font-one.lua19
-rw-r--r--tex/context/base/mkiv/font-otl.lua33
-rw-r--r--tex/context/base/mkiv/font-tfm.lua22
-rw-r--r--tex/context/base/mkiv/font-tpk.lua1
-rw-r--r--tex/context/base/mkiv/grph-bmp.lua6
-rw-r--r--tex/context/base/mkiv/grph-chk.lua11
-rw-r--r--tex/context/base/mkiv/grph-pat.lua2
-rw-r--r--tex/context/base/mkiv/lpdf-grp.lua2
-rw-r--r--tex/context/base/mkiv/luat-cod.lua2
-rw-r--r--tex/context/base/mkiv/math-act.lua8
-rw-r--r--tex/context/base/mkiv/math-vfu.lua7
-rw-r--r--tex/context/base/mkiv/node-acc.lua7
-rw-r--r--tex/context/base/mkiv/node-res.lua8
-rw-r--r--tex/context/base/mkiv/node-ser.lua1
-rw-r--r--tex/context/base/mkiv/node-tra.lua14
-rw-r--r--tex/context/base/mkiv/status-files.pdfbin26086 -> 26088 bytes
-rw-r--r--tex/context/base/mkiv/status-lua.pdfbin255761 -> 254200 bytes
-rw-r--r--tex/context/base/mkiv/strc-tag.lua2
-rw-r--r--tex/context/base/mkiv/typo-drp.lua9
-rw-r--r--tex/context/base/mkxl/cont-new.mkxl2
-rw-r--r--tex/context/base/mkxl/context.mkxl2
-rw-r--r--tex/context/base/mkxl/core-sys.lmt8
-rw-r--r--tex/context/base/mkxl/driv-shp.lmt46
-rw-r--r--tex/context/base/mkxl/font-chk.lmt475
-rw-r--r--tex/context/base/mkxl/font-chk.mkxl2
-rw-r--r--tex/context/base/mkxl/font-col.lmt477
-rw-r--r--tex/context/base/mkxl/font-col.mklx2
-rw-r--r--tex/context/base/mkxl/font-con.lmt1492
-rw-r--r--tex/context/base/mkxl/font-ctx.lmt3180
-rw-r--r--tex/context/base/mkxl/font-def.lmt504
-rw-r--r--tex/context/base/mkxl/font-enh.lmt90
-rw-r--r--tex/context/base/mkxl/font-fbk.lmt357
-rw-r--r--tex/context/base/mkxl/font-fmp.lmt123
-rw-r--r--tex/context/base/mkxl/font-lib.mklx31
-rw-r--r--tex/context/base/mkxl/font-ogr.lmt58
-rw-r--r--tex/context/base/mkxl/font-tra.mkxl18
-rw-r--r--tex/context/base/mkxl/lpdf-col.lmt188
-rw-r--r--tex/context/base/mkxl/lpdf-emb.lmt60
-rw-r--r--tex/context/base/mkxl/lpdf-grp.lmt2
-rw-r--r--tex/context/base/mkxl/lpdf-img.lmt4
-rw-r--r--tex/context/base/mkxl/lpdf-lmt.lmt19
-rw-r--r--tex/context/base/mkxl/lpdf-vfc.lmt4
-rw-r--r--tex/context/base/mkxl/luat-cod.lmt2
-rw-r--r--tex/context/base/mkxl/meta-fnt.lmt263
-rw-r--r--tex/context/base/mkxl/meta-fnt.mkxl2
-rw-r--r--tex/context/base/mkxl/node-acc.lmt112
-rw-r--r--tex/context/base/mkxl/node-ini.mkxl4
-rw-r--r--tex/context/base/mkxl/node-met.lmt633
-rw-r--r--tex/context/base/mkxl/node-ref.lmt1
-rw-r--r--tex/context/base/mkxl/node-res.lmt34
-rw-r--r--tex/context/base/mkxl/node-rul.lmt3
-rw-r--r--tex/context/base/mkxl/node-ser.lmt1
-rw-r--r--tex/context/base/mkxl/node-tra.lmt14
-rw-r--r--tex/context/base/mkxl/strc-tag.lmt4
-rw-r--r--tex/context/base/mkxl/supp-box.lmt15
-rw-r--r--tex/context/base/mkxl/syst-aux.mkxl9
-rw-r--r--tex/context/base/mkxl/toks-scn.lmt32
-rw-r--r--tex/context/base/mkxl/typo-chr.lmt291
-rw-r--r--tex/context/base/mkxl/typo-chr.mkxl2
-rw-r--r--tex/context/base/mkxl/typo-drp.lmt9
-rw-r--r--tex/context/base/mkxl/typo-ovl.lmt184
-rw-r--r--tex/context/base/mkxl/typo-ovl.mkxl2
-rw-r--r--tex/generic/context/luatex/luatex-fonts-merged.lua10
72 files changed, 8550 insertions, 436 deletions
diff --git a/doc/context/documents/general/manuals/luametatex.pdf b/doc/context/documents/general/manuals/luametatex.pdf
index 0b73a9556..6d7e0b892 100644
--- a/doc/context/documents/general/manuals/luametatex.pdf
+++ b/doc/context/documents/general/manuals/luametatex.pdf
Binary files differ
diff --git a/doc/context/sources/general/manuals/luametatex/luametatex.tex b/doc/context/sources/general/manuals/luametatex/luametatex.tex
index 4251b8770..77cf98019 100644
--- a/doc/context/sources/general/manuals/luametatex/luametatex.tex
+++ b/doc/context/sources/general/manuals/luametatex/luametatex.tex
@@ -68,6 +68,10 @@
% Thanks to sebastian.miele@gmail.com for close reading the manual and sending
% fixes.
+\pushoverloadmode \unprotect
+ % test code
+\protect \popoverloadmode
+
\enabletrackers[system.usage=summary]
\environment luametatex-style
diff --git a/tex/context/base/mkii/cont-new.mkii b/tex/context/base/mkii/cont-new.mkii
index d6a41419c..96a3acfec 100644
--- a/tex/context/base/mkii/cont-new.mkii
+++ b/tex/context/base/mkii/cont-new.mkii
@@ -11,7 +11,7 @@
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.
-\newcontextversion{2020.12.06 18:12}
+\newcontextversion{2020.12.08 11:06}
%D This file is loaded at runtime, thereby providing an
%D excellent place for hacks, patches, extensions and new
diff --git a/tex/context/base/mkii/context.mkii b/tex/context/base/mkii/context.mkii
index d3ff08369..864df0c99 100644
--- a/tex/context/base/mkii/context.mkii
+++ b/tex/context/base/mkii/context.mkii
@@ -20,7 +20,7 @@
%D your styles an modules.
\edef\contextformat {\jobname}
-\edef\contextversion{2020.12.06 18:12}
+\edef\contextversion{2020.12.08 11:06}
%D For those who want to use this:
diff --git a/tex/context/base/mkiv/cont-new.mkiv b/tex/context/base/mkiv/cont-new.mkiv
index b8b97f52b..f60590c20 100644
--- a/tex/context/base/mkiv/cont-new.mkiv
+++ b/tex/context/base/mkiv/cont-new.mkiv
@@ -13,7 +13,7 @@
% \normalend % uncomment this to get the real base runtime
-\newcontextversion{2020.12.06 18:12}
+\newcontextversion{2020.12.08 11:06}
%D This file is loaded at runtime, thereby providing an excellent place for hacks,
%D patches, extensions and new features. There can be local overloads in cont-loc
diff --git a/tex/context/base/mkiv/context.mkiv b/tex/context/base/mkiv/context.mkiv
index dc6bc1a4c..907146f68 100644
--- a/tex/context/base/mkiv/context.mkiv
+++ b/tex/context/base/mkiv/context.mkiv
@@ -45,7 +45,7 @@
%D {YYYY.MM.DD HH:MM} format.
\edef\contextformat {\jobname}
-\edef\contextversion{2020.12.06 18:12}
+\edef\contextversion{2020.12.08 11:06}
%D Kind of special:
diff --git a/tex/context/base/mkiv/font-cft.lua b/tex/context/base/mkiv/font-cft.lua
index ee9ffa6a7..cf6c232a6 100644
--- a/tex/context/base/mkiv/font-cft.lua
+++ b/tex/context/base/mkiv/font-cft.lua
@@ -6,7 +6,7 @@ if not modules then modules = { } end modules ['font-cft'] = {
license = "see context related readme files"
}
--- context font tables
+-- context font tables (needs updating)
--
-- todo: extra:
--
@@ -229,7 +229,7 @@ do
data.scaled = {
properties = {
- encodingbytes = t_cardinal,
+ encodingbytes = t_cardinal, -- not in lmtx
embedding = t_cardinal, -- ?
cidinfo = t_hash,
format = t_string,
@@ -238,7 +238,7 @@ do
filename = t_string,
psname = t_string,
name = t_string,
- virtualized = t_boolean,
+ virtualized = t_boolean, -- not in lmtx
hasitalics = t_boolean,
autoitalicamount = t_float,
nostackmath = t_boolean,
diff --git a/tex/context/base/mkiv/font-chk.lua b/tex/context/base/mkiv/font-chk.lua
index d2a0943f6..e18e4b804 100644
--- a/tex/context/base/mkiv/font-chk.lua
+++ b/tex/context/base/mkiv/font-chk.lua
@@ -273,12 +273,6 @@ registerotffeature {
fonts.loggers.add_placeholders = function(id) addmissingsymbols(fontdata[id or true]) end
fonts.loggers.category_to_placeholder = mapping
-function commands.getplaceholderchar(name)
- local id = currentfont()
- addmissingsymbols(fontdata[id])
- context(getprivatenode(fontdata[id],name))
-end
-
-- todo in luatex: option to add characters (just slots, no kerns etc)
-- we can do that now so ...
diff --git a/tex/context/base/mkiv/font-ctx.lua b/tex/context/base/mkiv/font-ctx.lua
index 6c21bc0cc..5c93f302f 100644
--- a/tex/context/base/mkiv/font-ctx.lua
+++ b/tex/context/base/mkiv/font-ctx.lua
@@ -167,11 +167,6 @@ addformatter(formatters,"font:features",[["'"..sequenced(%s," ",true).."'"]],{ s
-- ... like font-sfm or so
constructors.resolvevirtualtoo = true -- context specific (due to resolver)
-
-if CONTEXTLMTXMODE and CONTEXTLMTXMODE > 0 then
- constructors.fixprotrusion = false
-end
-
constructors.sharefonts = true -- experimental
constructors.nofsharedfonts = 0
constructors.nofsharedhashes = 0
@@ -339,12 +334,7 @@ implement {
permanent = false,
actions = function()
for i=1,7 do
- if CONTEXTLMTXMODE > 0 then
- font.setfontdimen(0,i,0)
- else
- -- we have no direct method
- context([[\fontdimen%s\nullfont\zeropoint]],i)
- end
+ context([[\fontdimen%s\nullfont\zeropoint]],i)
end
definers.resetnullfont()
end
@@ -1657,17 +1647,7 @@ tfmdata.original = specification.specification
return id, csnames[id]
end
- local read
-
- if CONTEXTLMTXMODE and CONTEXTLMTXMODE > 0 then -- maybe always
- read = function(name,size)
- return (define { name = name, size = size } or 0)
- end
- else
- read = definers.read
- end
-
- callbacks.register('define_font', read, "definition of fonts (tfmdata preparation)")
+ callbacks.register('define_font', definers.read, "definition of fonts (tfmdata preparation)")
-- here
@@ -1833,7 +1813,7 @@ function mappings.loadfile(name)
if trace_mapfiles then
report_mapfiles("loading map file %a",name)
end
- lpdf.setmapfile(name)
+ lpdf.setmapfile(name) -- bah. will go in lmtx
loaded[name] = true
end
end
@@ -1851,7 +1831,7 @@ function mappings.loadline(how,line)
if trace_mapfiles then
report_mapfiles("processing map line %a",line)
end
- lpdf.setmapline(how)
+ lpdf.setmapline(how) -- bah. will go in lmtx
loaded[how] = true
end
end
@@ -1860,6 +1840,10 @@ function mappings.reset()
lpdf.setmapfile("") -- tricky ... backend related
end
+function mappings.getentry(...)
+ return lpdf.getmapentry(...) -- tricky ... backend related
+end
+
implement {
name = "loadmapfile",
actions = mappings.loadfile,
@@ -3390,8 +3374,3 @@ function fonts.helpers.collectanchors(tfmdata)
return anchors
end
-
-if CONTEXTLMTXMODE > 0 then
- fonts.constructors.addtounicode = false
- fonts.constructors.autocleanup = false
-end
diff --git a/tex/context/base/mkiv/font-one.lua b/tex/context/base/mkiv/font-one.lua
index a76c92985..5ef6e4749 100644
--- a/tex/context/base/mkiv/font-one.lua
+++ b/tex/context/base/mkiv/font-one.lua
@@ -592,15 +592,18 @@ local function copytotfm(data)
parameters.descender = abs(metadata.descender or 0)
parameters.units = 1000
--
- properties.spacer = spacer
+ properties.spacer = spacer
+ properties.format = fonts.formats[filename] or "type1"
+ properties.filename = filename
+ properties.fontname = fontname
+ properties.fullname = fullname
+ properties.psname = fullname
+ properties.name = filename or fullname or fontname
+ properties.private = properties.private or data.private or privateoffset
+ --
+if not CONTEXTLMTXMODE or CONTEXTLMTXMODE == 0 then
properties.encodingbytes = 2
- properties.format = fonts.formats[filename] or "type1"
- properties.filename = filename
- properties.fontname = fontname
- properties.fullname = fullname
- properties.psname = fullname
- properties.name = filename or fullname or fontname
- properties.private = properties.private or data.private or privateoffset
+end
--
if next(characters) then
return {
diff --git a/tex/context/base/mkiv/font-otl.lua b/tex/context/base/mkiv/font-otl.lua
index e8f89b4e7..b8e13f107 100644
--- a/tex/context/base/mkiv/font-otl.lua
+++ b/tex/context/base/mkiv/font-otl.lua
@@ -486,23 +486,26 @@ local function copytotfm(data,cache_id)
end
end
--
- parameters.designsize = (designsize/10)*65536
- parameters.minsize = (minsize /10)*65536
- parameters.maxsize = (maxsize /10)*65536
- parameters.ascender = abs(metadata.ascender or 0)
- parameters.descender = abs(metadata.descender or 0)
- parameters.units = units
- parameters.vheight = metadata.defaultvheight
+ parameters.designsize = (designsize/10)*65536
+ parameters.minsize = (minsize /10)*65536
+ parameters.maxsize = (maxsize /10)*65536
+ parameters.ascender = abs(metadata.ascender or 0)
+ parameters.descender = abs(metadata.descender or 0)
+ parameters.units = units
+ parameters.vheight = metadata.defaultvheight
--
- properties.space = spacer
+ properties.space = spacer
+ properties.format = data.format or formats.otf
+ properties.filename = filename
+ properties.fontname = fontname
+ properties.fullname = fullname
+ properties.psname = psname
+ properties.name = filename or fullname
+ properties.subfont = subfont
+ --
+if not CONTEXTLMTXMODE or CONTEXTLMTXMODE == 0 then
properties.encodingbytes = 2
- properties.format = data.format or formats.otf
- properties.filename = filename
- properties.fontname = fontname
- properties.fullname = fullname
- properties.psname = psname
- properties.name = filename or fullname
- properties.subfont = subfont
+end
--
-- properties.name = specification.name
-- properties.sub = specification.sub
diff --git a/tex/context/base/mkiv/font-tfm.lua b/tex/context/base/mkiv/font-tfm.lua
index e2f0d22f2..63f3276b1 100644
--- a/tex/context/base/mkiv/font-tfm.lua
+++ b/tex/context/base/mkiv/font-tfm.lua
@@ -139,10 +139,12 @@ local function read_from_tfm(specification)
-- If reencode returns a new table, we assume that we're doing something
-- special. An 'auto' reencode picks up its vector from the pfb file.
- if lpdf and lpdf.getmapentry and not features.reencode then
+ local getmapentry = fonts.mappings.getentry
+
+ if getmapentry and not features.reencode then
-- This can happen multiple times but not that often so we don't
-- optimize this.
- local encoding, pfbfile, encfile = lpdf.getmapentry(filename)
+ local encoding, pfbfile, encfile = getmapentry(filename)
if encoding and pfbfile then
features.reencode = encfile
features.pfbfile = pfbfile
@@ -170,9 +172,9 @@ local function read_from_tfm(specification)
properties.format = tfmdata.format or fonts.formats.tfm -- better than nothing
properties.usedbitmap = tfmdata.usedbitmap
--
-if lpdf and lpdf.getmapentry and newtfmdata then
- properties.filename = features.pfbfile
-end
+ if getmapentry and newtfmdata then
+ properties.filename = features.pfbfile
+ end
--
tfmdata.properties = properties
tfmdata.resources = resources
@@ -234,8 +236,10 @@ end
--
-- The tounicode data is passed to the backend that constructs the vectors for us.
--
- tfmdata.tounicode = 1
- local tounicode = fonts.mappings.tounicode
+if not CONTEXTLMTXMODE or CONTEXTLMTXMODE == 0 then
+ tfmdata.tounicode = 1
+end
+ local tounicode = fonts.mappings.tounicode
for unicode, v in next, tfmdata.characters do
local u = v.unicode
if u then
@@ -514,11 +518,13 @@ do
tfmdata.fullname = tfmdata.fullname or tfmdata.name
tfmdata.psname = file.nameonly(pfbfile or tfmdata.name)
tfmdata.filename = pfbfile
- tfmdata.encodingbytes = 2
-- tfmdata.format = bitmap and "type3" or "type1"
tfmdata.format = "type1"
+if not CONTEXTLMTXMODE or CONTEXTLMTXMODE == 0 then
+ tfmdata.encodingbytes = 2
tfmdata.tounicode = 1
tfmdata.embedding = "subset"
+end
tfmdata.usedbitmap = bitmap and virtualid
tfmdata.private = private
diff --git a/tex/context/base/mkiv/font-tpk.lua b/tex/context/base/mkiv/font-tpk.lua
index c24af1df8..16b89c393 100644
--- a/tex/context/base/mkiv/font-tpk.lua
+++ b/tex/context/base/mkiv/font-tpk.lua
@@ -848,7 +848,6 @@ do
direction = direction,
-- checksum = checksum,
-- embedding = "unknown",
- -- encodingbytes = 0,
-- extend = 1000,
-- slant = 0,
-- squeeze = 0,
diff --git a/tex/context/base/mkiv/grph-bmp.lua b/tex/context/base/mkiv/grph-bmp.lua
index e10b01aa4..6669b5db1 100644
--- a/tex/context/base/mkiv/grph-bmp.lua
+++ b/tex/context/base/mkiv/grph-bmp.lua
@@ -39,10 +39,6 @@ function bitmaps.new(xsize,ysize,colorspace,colordepth,mask,index)
}
end
--- function backends.codeinjections.bitmap(bitmap)
--- return lpdf.injectors.bitmap(bitmap)
--- end
-
local function flush(bitmap)
local specification = backends.codeinjections.bitmap(bitmap)
if specification then
@@ -89,7 +85,7 @@ local function placeholder(nx,ny)
end
end
- return lpdf.injectors.bitmap(bitmap)
+ return backends.codeinjections.bitmap(bitmap)
end
diff --git a/tex/context/base/mkiv/grph-chk.lua b/tex/context/base/mkiv/grph-chk.lua
index 0688f46ef..0cfc9ba08 100644
--- a/tex/context/base/mkiv/grph-chk.lua
+++ b/tex/context/base/mkiv/grph-chk.lua
@@ -35,7 +35,7 @@ function checkers.pdf(data)
local request = data.request
local used = data.used
if request and used and not request.scanimage then
- local image = lpdf.epdf.image
+ local image = lpdf.epdf.image -- ok, this is a pdf specific main function
local openpdf = image.open
local closepdf = image.close
local querypdf = image.query
@@ -163,7 +163,6 @@ function checkers.jpg(data)
local used = data.used
if request and used and not request.scanimage then
local identify = graphics.identify
- local inject = lpdf.injectors.jpg
local found = false
request.scanimage = function(t)
local result = wrappedidentify(identify,t.filename,"jpg")
@@ -187,7 +186,7 @@ function checkers.jpg(data)
request.copyimage = function(t)
if found then
found = false
- return inject(t)
+ return backends.codeinjections.jpg(t)
end
end
end
@@ -199,7 +198,6 @@ function checkers.jp2(data) -- idem as jpg
local used = data.used
if request and used and not request.scanimage then
local identify = graphics.identify
- local inject = lpdf.injectors.jp2
local found = false
request.scanimage = function(t)
local result = wrappedidentify(identify,t.filename,"jp2")
@@ -223,7 +221,7 @@ function checkers.jp2(data) -- idem as jpg
request.copyimage = function(t)
if found then
found = false
- return inject(t)
+ return backends.codeinjections.jp2(t)
end
end
end
@@ -235,7 +233,6 @@ function checkers.png(data) -- same as jpg (for now)
local used = data.used
if request and used and not request.scanimage then
local identify = graphics.identify
- local inject = lpdf.injectors.png -- currently pdf specific
local found = false
request.scanimage = function(t)
local result = wrappedidentify(identify,t.filename,"png")
@@ -263,7 +260,7 @@ function checkers.png(data) -- same as jpg (for now)
t.colorref = used.colorref -- this is a bit of a hack
if found then
found = false
- local ok, result = pcall(inject,t)
+ local ok, result = pcall(backends.codeinjections.png,t)
if ok then
return result
else
diff --git a/tex/context/base/mkiv/grph-pat.lua b/tex/context/base/mkiv/grph-pat.lua
index e38a9a674..4310af672 100644
--- a/tex/context/base/mkiv/grph-pat.lua
+++ b/tex/context/base/mkiv/grph-pat.lua
@@ -38,7 +38,7 @@ interfaces.implement {
return
end
nodes.handlers.finalizebox(number)
- names[name] = lpdf.registerpattern {
+ names[name] = backends.codeinjections.registerpattern {
number = number,
width = specification.width or box.width,
height = specification.height or (box.height + box.depth) ,
diff --git a/tex/context/base/mkiv/lpdf-grp.lua b/tex/context/base/mkiv/lpdf-grp.lua
index e827419a6..963b1dde3 100644
--- a/tex/context/base/mkiv/lpdf-grp.lua
+++ b/tex/context/base/mkiv/lpdf-grp.lua
@@ -293,3 +293,5 @@ end
function lpdf.patternstream(n,width,height)
return f_pattern("Pt" .. n,width*basepoints,height*basepoints)
end
+
+backends.pdf.codeinjections.registerpattern = lpdf.registerpattern
diff --git a/tex/context/base/mkiv/luat-cod.lua b/tex/context/base/mkiv/luat-cod.lua
index 0b7521180..927f6dfab 100644
--- a/tex/context/base/mkiv/luat-cod.lua
+++ b/tex/context/base/mkiv/luat-cod.lua
@@ -68,7 +68,7 @@ function lua.registercode(filename,options)
end
end
if barename == filename then
- filename = filename .. (opts.autosuffix and CONTEXTLMTXMODE > 0 and ".lmt" or ".lua")
+ filename = filename .. ".lua"
end
local code = environment.luafilechunk(filename,false,opts.optimize)
if code then
diff --git a/tex/context/base/mkiv/math-act.lua b/tex/context/base/mkiv/math-act.lua
index 03f44dd1c..4f38b4195 100644
--- a/tex/context/base/mkiv/math-act.lua
+++ b/tex/context/base/mkiv/math-act.lua
@@ -247,10 +247,10 @@ function mathematics.overloaddimensions(target,original,set)
local factor = parameters.factor
local hfactor = parameters.hfactor
local vfactor = parameters.vfactor
- -- to be sure
+if not CONTEXTLMTXMODE or CONTEXTLMTXMODE == 0 then
target.type = "virtual"
target.properties.virtualized = true
- --
+end
local function overload(dimensions)
for unicode, data in next, dimensions do
local character = characters[unicode]
@@ -603,8 +603,10 @@ interfaces.implement {
-- if not fonts then
-- fonts = { }
-- target.fonts = fonts
+-- if not CONTEXTLMTXMODE or CONTEXTLMTXMODE == 0 then
-- target.type = "virtual"
-- target.properties.virtualized = true
+-- end
-- end
-- if #fonts == 0 then
-- fonts[1] = { id = 0, size = size } -- sel, will be resolved later
@@ -709,8 +711,10 @@ function mathematics.finishfallbacks(target,specification,fallbacks)
fonts = { }
target.fonts = fonts
end
+if not CONTEXTLMTXMODE or CONTEXTLMTXMODE == 0 then
target.type = "virtual"
target.properties.virtualized = true
+end
if #fonts == 0 then
fonts[1] = { id = 0, size = size } -- self, will be resolved later
end
diff --git a/tex/context/base/mkiv/math-vfu.lua b/tex/context/base/mkiv/math-vfu.lua
index 9090955e9..c7079caca 100644
--- a/tex/context/base/mkiv/math-vfu.lua
+++ b/tex/context/base/mkiv/math-vfu.lua
@@ -837,7 +837,6 @@ function vfmath.define(specification,set,goodies)
setmetatableindex(goodies,parent.goodies)
end
--
- properties.virtualized = true
properties.hasitalics = true
properties.hasmath = true
--
@@ -850,9 +849,13 @@ function vfmath.define(specification,set,goodies)
-- we need to set some values in main as well (still?)
--
main.fullname = properties.fullname
- main.type = "virtual"
main.nomath = false
--
+if not CONTEXTLMTXMODE or CONTEXTLMTXMODE == 0 then
+ properties.virtualized = true
+ main.type = "virtual"
+end
+ --
parameters.x_height = parameters.x_height or 0
--
local already_reported = false
diff --git a/tex/context/base/mkiv/node-acc.lua b/tex/context/base/mkiv/node-acc.lua
index e6c617602..d8f4b25bf 100644
--- a/tex/context/base/mkiv/node-acc.lua
+++ b/tex/context/base/mkiv/node-acc.lua
@@ -129,10 +129,9 @@ end)
-- local done = false
-- for n, id in nextnode, tonuts(head) do
-- if id == disc then
--- local r = getfield(n,"replace")
--- local p = getfield(n,"pre")
--- if r and p then
--- local str = compact(r)
+-- local pre, post, replace = getdisc(n)
+-- if replace and pre then
+-- local str = compact(replace)
-- local hsh = hyphenated[str]
-- if not hsh then
-- hsh = #codes + 1
diff --git a/tex/context/base/mkiv/node-res.lua b/tex/context/base/mkiv/node-res.lua
index 03de8f843..478a46906 100644
--- a/tex/context/base/mkiv/node-res.lua
+++ b/tex/context/base/mkiv/node-res.lua
@@ -626,10 +626,10 @@ do
return v
end)
- traversers.node = nodes.traverse (glyph)
- traversers.char = nodes.traverse_char (glyph)
- if nuts.traverse_glyph then traversers.glyph = nodes.traverse_glyph (glyph) end
- if nuts.traverse_list then traversers.list = nodes.traverse_list (glyph) end
+ traversers.node = nodes.traverse (glyph)
+ traversers.char = nodes.traverse_char (glyph)
+ if nodes.traverse_glyph then traversers.glyph = nodes.traverse_glyph(glyph) end
+ if nodes.traverse_list then traversers.list = nodes.traverse_list (glyph) end
nodes.traversers = traversers
diff --git a/tex/context/base/mkiv/node-ser.lua b/tex/context/base/mkiv/node-ser.lua
index 6fc2b7ea4..25a6dd6c3 100644
--- a/tex/context/base/mkiv/node-ser.lua
+++ b/tex/context/base/mkiv/node-ser.lua
@@ -19,7 +19,6 @@ local context = context
local nodes = nodes
local node = node
-local traverse = nodes.traverse
local is_node = nodes.is_node
local nodecodes = nodes.nodecodes
diff --git a/tex/context/base/mkiv/node-tra.lua b/tex/context/base/mkiv/node-tra.lua
index a86dfb620..83c072c19 100644
--- a/tex/context/base/mkiv/node-tra.lua
+++ b/tex/context/base/mkiv/node-tra.lua
@@ -180,18 +180,8 @@ end
nodes.tosequence = tosequence
nuts .tosequence = tosequence
-if CONTEXTLMTXMODE > 0 then
-
- function nodes.report(t)
- report_nodes("output %a, %s nodes",tex.getoutputactive(),count_nodes(t))
- end
-
-else
-
- function nodes.report(t)
- report_nodes("output %a, %s nodes",status.output_active,count_nodes(t))
- end
-
+function nodes.report(t)
+ report_nodes("output %a, %s nodes",status.output_active,count_nodes(t))
end
function nodes.packlist(head)
diff --git a/tex/context/base/mkiv/status-files.pdf b/tex/context/base/mkiv/status-files.pdf
index f61ff2582..1f59f1008 100644
--- a/tex/context/base/mkiv/status-files.pdf
+++ b/tex/context/base/mkiv/status-files.pdf
Binary files differ
diff --git a/tex/context/base/mkiv/status-lua.pdf b/tex/context/base/mkiv/status-lua.pdf
index 75f0d1787..c39495b08 100644
--- a/tex/context/base/mkiv/status-lua.pdf
+++ b/tex/context/base/mkiv/status-lua.pdf
Binary files differ
diff --git a/tex/context/base/mkiv/strc-tag.lua b/tex/context/base/mkiv/strc-tag.lua
index 694f7bdf0..83ff515e3 100644
--- a/tex/context/base/mkiv/strc-tag.lua
+++ b/tex/context/base/mkiv/strc-tag.lua
@@ -266,8 +266,8 @@ end
function structures.atlocation(str)
local specification = taglist[texgetattribute(a_tagged)]
if specification then
+ local list = specification.taglist
if list then
- local taglist = specification.taglist
local pattern = patterns[str]
for i=#list,1,-1 do
if find(list[i],pattern) then
diff --git a/tex/context/base/mkiv/typo-drp.lua b/tex/context/base/mkiv/typo-drp.lua
index 12864e52d..ddc6d68ae 100644
--- a/tex/context/base/mkiv/typo-drp.lua
+++ b/tex/context/base/mkiv/typo-drp.lua
@@ -321,13 +321,8 @@ actions[v_default] = function(head,setting)
if trace_initials then
report_initials("setting hangafter to %i and hangindent to %p",hangafter,hangindent)
end
- if CONTEXTLMTXMODE > 0 then
- texset("hangafter",hangafter,true)
- texset("hangindent",hangindent,true)
- else
- texset("hangafter",hangafter)
- texset("hangindent",hangindent)
- end
+ texset("hangafter",hangafter)
+ texset("hangindent",hangindent)
end
if indent then
insert_after(first,first,new_kern(-parindent))
diff --git a/tex/context/base/mkxl/cont-new.mkxl b/tex/context/base/mkxl/cont-new.mkxl
index 3c11528a8..ac5966acb 100644
--- a/tex/context/base/mkxl/cont-new.mkxl
+++ b/tex/context/base/mkxl/cont-new.mkxl
@@ -13,7 +13,7 @@
% \normalend % uncomment this to get the real base runtime
-\newcontextversion{2020.12.06 18:12}
+\newcontextversion{2020.12.08 11:06}
%D This file is loaded at runtime, thereby providing an excellent place for hacks,
%D patches, extensions and new features. There can be local overloads in cont-loc
diff --git a/tex/context/base/mkxl/context.mkxl b/tex/context/base/mkxl/context.mkxl
index 5eca52575..5dde967b5 100644
--- a/tex/context/base/mkxl/context.mkxl
+++ b/tex/context/base/mkxl/context.mkxl
@@ -29,7 +29,7 @@
%D {YYYY.MM.DD HH:MM} format.
\immutable\edef\contextformat {\jobname}
-\immutable\edef\contextversion{2020.12.06 18:12}
+\immutable\edef\contextversion{2020.12.08 11:06}
%overloadmode 1 % check frozen / warning
%overloadmode 2 % check frozen / error
diff --git a/tex/context/base/mkxl/core-sys.lmt b/tex/context/base/mkxl/core-sys.lmt
index 12f001e37..0d471133d 100644
--- a/tex/context/base/mkxl/core-sys.lmt
+++ b/tex/context/base/mkxl/core-sys.lmt
@@ -74,14 +74,6 @@ implement { name = "inputfilesuffix", public = true, actions = function() co
implement { name = "inputfilename", public = true, actions = function() context(environment.inputfilename) end }
implement { name = "outputfilename", public = true, actions = function() context(environment.outputfilename) end }
-statistics.register("result saved in file", function()
- -- suffix will be fetched from backend
- local outputfilename = environment.outputfilename or environment.jobname or tex.jobname or "<unset>"
- return lpdf and format("%s.%s, compresslevel %s, objectcompresslevel %s",outputfilename,"pdf",
- lpdf.getcompression()
- ) or "error"
-end)
-
implement {
name = "systemlog",
arguments = "3 strings",
diff --git a/tex/context/base/mkxl/driv-shp.lmt b/tex/context/base/mkxl/driv-shp.lmt
index 8e3a936bb..7a385d7ab 100644
--- a/tex/context/base/mkxl/driv-shp.lmt
+++ b/tex/context/base/mkxl/driv-shp.lmt
@@ -184,6 +184,14 @@ local tospace = false directives.register("backends.spaces", function(v) tospac
local default = 16384 * number.dimenfactors.bp -- 65536 // 4
+local startcolor = function() end
+local stopcolor = function() end
+
+updaters.register("backend.update",function()
+ startcolor = fonts.vfcommands.startcolor
+ stopcolor = fonts.vfcommands.stopcolor
+end)
+
local function flush_vf_packet(pos_h,pos_v,pos_r,font,char,data,factor,vfcommands)
if nesting > 100 then
@@ -293,6 +301,7 @@ local function flush_vf_packet(pos_h,pos_v,pos_r,font,char,data,factor,vfcommand
level = level - 1
end
elseif command == "pdf" then
+ -- this will disappear and become a plug
flushliteral(false,pos_h,pos_v,packet[2],packet[3])
elseif command == "rule" then
local size_v = packet[2]
@@ -307,6 +316,7 @@ local function flush_vf_packet(pos_h,pos_v,pos_r,font,char,data,factor,vfcommand
end
end
elseif command == "frame" then
+ -- d:width d:height d:depth d:rulethickness b:outline b:advance b:baseline s:color
local width = packet[2]
if width > 0 then
local height = packet[3] or 0
@@ -317,10 +327,18 @@ local function flush_vf_packet(pos_h,pos_v,pos_r,font,char,data,factor,vfcommand
width = width + width * factor / 1000
end
if width > 0 then
- local line = packet[5] or default
- local outline = not packet[6]
- local advance = not packet[7]
- flushspecialrule(pos_h,pos_v,pos_r,width,height,depth,line,outline)
+ local line = packet[5] or default
+ local outline = not packet[6]
+ local advance = not packet[7]
+ local baseline = outline and packet[8]
+ local color = packet[9]
+ if color then
+ startcolor(color)
+ end
+ flushspecialrule(pos_h,pos_v,pos_r,width,height,depth,line,outline,baseline)
+ if color then
+ stopcolor()
+ end
if advance then
pos_h = pos_h + width
end
@@ -335,22 +353,24 @@ local function flush_vf_packet(pos_h,pos_v,pos_r,font,char,data,factor,vfcommand
end
elseif command == "lua" then
local code = packet[2]
- if type(code) ~= "function" then
+ local kind = type(code)
+ if kind ~= "function" then
code = loadstring(code)
+ kind = type(code)
end
- if type(code) == "function" then
+ if kind == "function" then
code(font,char,pos_h,pos_v)
end
elseif command == "node" then
local h = packet[2]
hlist_out(h,getlist(h))
- elseif command == "image" then
- -- doesn't work because intercepted by engine so we use a different
- -- mechanism (for now)
- local image = packet[2]
- -- to do
- elseif command == "pdfmode" then
- -- doesn't happen
+ -- elseif command == "image" then
+ -- -- doesn't work because intercepted by engine so we use a different
+ -- -- mechanism (for now)
+ -- local image = packet[2]
+ -- -- to do
+ -- elseif command == "pdfmode" then
+ -- -- doesn't happen
-- elseif command == "special" then
-- -- not supported
-- elseif command == "nop" then
diff --git a/tex/context/base/mkxl/font-chk.lmt b/tex/context/base/mkxl/font-chk.lmt
new file mode 100644
index 000000000..7141be2c7
--- /dev/null
+++ b/tex/context/base/mkxl/font-chk.lmt
@@ -0,0 +1,475 @@
+if not modules then modules = { } end modules ['font-chk'] = {
+ 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 kind of old but it makes no real sense to upgrade it to for instance
+-- using delayed type 3 fonts in order to be lean and mean and cut'n'paste
+-- compliant. When this kicks one needs to fix the choice of fonts anyway! So,
+-- instead we just keep the method we use but slightly adapted to the backend
+-- of lmtx.
+
+local next = next
+local floor = math.floor
+
+local context = context
+
+local formatters = string.formatters
+local bpfactor = number.dimenfactors.bp
+local fastcopy = table.fastcopy
+local sortedkeys = table.sortedkeys
+local sortedhash = table.sortedhash
+
+local report = logs.reporter("fonts")
+local report_checking = logs.reporter("fonts","checking")
+
+local allocate = utilities.storage.allocate
+
+local fonts = fonts
+
+fonts.checkers = fonts.checkers or { }
+local checkers = fonts.checkers
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+local fontcharacters = fonthashes.characters
+
+local currentfont = font.current
+local addcharacters = font.addcharacters
+
+local helpers = fonts.helpers
+
+local addprivate = helpers.addprivate
+local hasprivate = helpers.hasprivate
+local getprivateslot = helpers.getprivateslot
+local getprivatecharornode = helpers.getprivatecharornode
+
+local otffeatures = fonts.constructors.features.otf
+local afmfeatures = fonts.constructors.features.afm
+
+local registerotffeature = otffeatures.register
+local registerafmfeature = afmfeatures.register
+
+local is_character = characters.is_character
+local chardata = characters.data
+
+local tasks = nodes.tasks
+local enableaction = tasks.enableaction
+local disableaction = tasks.disableaction
+
+local implement = interfaces.implement
+
+local glyph_code = nodes.nodecodes.glyph
+
+local new_special = nodes.pool.special -- todo: literal
+local hpack_node = node.hpack
+
+local nuts = nodes.nuts
+local tonut = nuts.tonut
+
+local isglyph = nuts.isglyph
+local setchar = nuts.setchar
+
+local nextglyph = nuts.traversers.glyph
+
+local remove_node = nuts.remove
+local insert_node_after = nuts.insert_after
+
+local action = false
+
+-- to tfmdata.properties ?
+
+local function onetimemessage(font,char,message) -- char == false returns table
+ local tfmdata = fontdata[font]
+ local shared = tfmdata.shared
+ if not shared then
+ shared = { }
+ tfmdata.shared = shared
+ end
+ local messages = shared.messages
+ if not messages then
+ messages = { }
+ shared.messages = messages
+ end
+ local category = messages[message]
+ if not category then
+ category = { }
+ messages[message] = category
+ end
+ if char == false then
+ return sortedkeys(category), category
+ end
+ local cc = category[char]
+ if not cc then
+ report_checking("char %C in font %a with id %a: %s",char,tfmdata.properties.fullname,font,message)
+ category[char] = 1
+ else
+ category[char] = cc + 1
+ end
+end
+
+fonts.loggers.onetimemessage = onetimemessage
+
+local fakes = {
+ lowercase = { width = .45, height = .55, depth = .20 },
+ uppercase = { width = .65, height = .70, depth = .25 },
+ mark = { width = .15, height = .70, depth = -.50 },
+ punctuation = { width = .15, height = .55, depth = .20 },
+ unknown = { width = .45, height = .20, depth = 0 },
+}
+
+local mapping = allocate {
+ lu = { "uppercase", "darkred" },
+ ll = { "lowercase", "darkred" },
+ lt = { "uppercase", "darkred" },
+ lm = { "lowercase", "darkred" },
+ lo = { "lowercase", "darkred" },
+ mn = { "mark", "darkgreen" },
+ mc = { "mark", "darkgreen" },
+ me = { "mark", "darkgreen" },
+ nd = { "lowercase", "darkblue" },
+ nl = { "lowercase", "darkblue" },
+ no = { "lowercase", "darkblue" },
+ pc = { "punctuation", "darkcyan" },
+ pd = { "punctuation", "darkcyan" },
+ ps = { "punctuation", "darkcyan" },
+ pe = { "punctuation", "darkcyan" },
+ pi = { "punctuation", "darkcyan" },
+ pf = { "punctuation", "darkcyan" },
+ po = { "punctuation", "darkcyan" },
+ sm = { "lowercase", "darkmagenta" },
+ sc = { "lowercase", "darkyellow" },
+ sk = { "lowercase", "darkyellow" },
+ so = { "lowercase", "darkyellow" },
+}
+
+table.setmetatableindex(mapping, { "unknown", "darkgray" })
+
+-- We provide access by (private) name for tracing purposes. We also need
+-- to make sure the dimensions are known at the lua and tex end.
+
+local cache = { }
+
+local function add(tfmdata,name,color,size,collected)
+ local hash = formatters["%s_%s_%i"](name,color,floor(size))
+ local chardata = cache[hash]
+ if not chardata then
+ local fake = fakes[name]
+ local width = size * fake.width
+ local height = size * fake.height
+ local depth = size * fake.depth
+ chardata = {
+ width = width,
+ height = height,
+ depth = depth,
+ commands = {
+ { "frame", width, height, depth, 65536/5, false, true, true, color },
+ }
+ }
+ cache[hash] = chardata
+ end
+ if not hasprivate(tfmdata,privatename) then
+ local privatename = formatters["placeholder %s %s"](name,color)
+ local privatecode = addprivate(tfmdata, privatename, chardata)
+ collected[privatecode] = chardata
+ end
+ return chardata
+end
+
+local function addplaceholder(font,char)
+ local tfmdata = fontdata[font or true]
+ local characters = tfmdata.characters
+ local size = tfmdata.parameters.size
+ local scale = size * bpfactor
+ local collected = { }
+ local category = chardata[char].category or "unknown"
+ local fakedata = mapping[category]
+ local chardata = add(tfmdata,fakedata[1],fakedata[2],size,collected)
+ collected [char] = chardata
+ characters[char] = chardata
+ addcharacters(font, { characters = collected })
+ return "char", char -- needed for math-noa
+end
+
+-- For old times sake we keep this: a whole bunch of fake symbols
+
+local function addplaceholders(tfmdata)
+ local properties = tfmdata.properties
+ local size = tfmdata.parameters.size
+ local scale = size * bpfactor
+ local collected = { }
+ local colors = { "darkred", "darkgreen", "darkblue", "darkcyan", "darkmagenta", "darkyellow", "darkgray" }
+ for name, v in sortedhash(fakes) do
+ for i=1,#colors do
+ add(tfmdata,name,colors[i],size,collected)
+ end
+ end
+ if next(collected) then
+ local id = properties.id
+ if id then
+ addcharacters(id, { characters = collected })
+ end
+ end
+end
+
+registerotffeature {
+ name = "missing",
+ description = "missing symbols",
+ manipulators = {
+ base = addplaceholders,
+ node = addplaceholders,
+ }
+}
+
+-- fonts.loggers.add_placeholders = function(id) addplaceholders(fontdata[id or true]) end
+-- fonts.loggers.category_to_placeholder = mapping
+
+checkers.placeholder = addplaceholder
+
+function checkers.missing(head)
+ local lastfont = nil
+ local characters = nil
+ if action == "replace" then
+ for n, char, font in nextglyph, head do
+ if font ~= lastfont then
+ lastfont = font
+ characters = fontcharacters[font]
+ end
+ if font > 0 and not characters[char] and is_character[chardata[char].category or "unknown"] then
+ onetimemessage(font,char,"missing (will be flagged)")
+ addplaceholder(font,char)
+ end
+ end
+ elseif action == "remove" then
+ -- faster than while loop so we delay removal
+ local found = nil
+ for n, char, font in nextglyph, head do
+ if font ~= lastfont then
+ lastfont = font
+ characters = fontcharacters[font]
+ end
+ if font > 0 and not characters[char] and is_character[chardata[char].category or "unknown"] then
+ onetimemessage(font,char,"missing (will be deleted)")
+ if not found then
+ found = { n }
+ else
+ found[#found+1] = n
+ end
+ end
+ end
+ if found then
+ for i=1,#found do
+ head = remove_node(head,found[i],true)
+ end
+ end
+ else
+ for n, char, font in nextglyph, head do
+ if font ~= lastfont then
+ lastfont = font
+ characters = fontcharacters[font]
+ end
+ if font > 0 and not characters[char] and is_character[chardata[char].category or "unknown"] then
+ onetimemessage(font,char,"missing")
+ end
+ end
+ end
+ return head
+end
+
+local relevant = {
+ "missing (will be deleted)",
+ "missing (will be flagged)",
+ "missing"
+}
+
+local function getmissing(id)
+ if id then
+ local list = getmissing(currentfont())
+ if list then
+ local _, list = next(getmissing(currentfont()))
+ return list
+ else
+ return { }
+ end
+ else
+ local t = { }
+ for id, d in next, fontdata do
+ local shared = d.shared
+ local messages = shared and shared.messages
+ if messages then
+ local filename = d.properties.filename
+ if not filename then
+ filename = tostring(d)
+ end
+ local tf = t[filename] or { }
+ for i=1,#relevant do
+ local tm = messages[relevant[i]]
+ if tm then
+ for k, v in next, tm do
+ tf[k] = (tf[k] or 0) + v
+ end
+ end
+ end
+ if next(tf) then
+ t[filename] = tf
+ end
+ end
+ end
+ local l = { }
+ for k, v in next, t do
+ l[k] = sortedkeys(v)
+ end
+ return l, t
+ end
+end
+
+checkers.getmissing = getmissing
+
+
+do
+
+ local reported = true
+
+ callback.register("glyph_not_found",function(font,char)
+ if font > 0 then
+ if char > 0 then
+ onetimemessage(font,char,"missing")
+ else
+ -- we have a special case
+ end
+ elseif not reported then
+ report("nullfont is used, maybe no bodyfont is defined")
+ reported = true
+ end
+ end)
+
+ trackers.register("fonts.missing", function(v)
+ if v then
+ enableaction("processors","fonts.checkers.missing")
+ else
+ disableaction("processors","fonts.checkers.missing")
+ end
+ if v == "replace" then
+ otffeatures.defaults.missing = true
+ end
+ action = v
+ end)
+
+ logs.registerfinalactions(function()
+ local collected, details = getmissing()
+ if next(collected) then
+ for filename, list in sortedhash(details) do
+ logs.startfilelogging(report,"missing characters",filename)
+ for u, v in sortedhash(list) do
+ report("%4i %U %c %s",v,u,u,chardata[u].description)
+ end
+ logs.stopfilelogging()
+ end
+ if logs.loggingerrors() then
+ for filename, list in sortedhash(details) do
+ logs.starterrorlogging(report,"missing characters",filename)
+ for u, v in sortedhash(list) do
+ report("%4i %U %c %s",v,u,u,chardata[u].description)
+ end
+ logs.stoperrorlogging()
+ end
+ end
+ end
+ end)
+
+end
+
+-- for the moment here
+
+local function expandglyph(characters,index,done)
+ done = done or { }
+ if not done[index] then
+ local data = characters[index]
+ if data then
+ done[index] = true
+ local d = fastcopy(data)
+ local n = d.next
+ if n then
+ d.next = expandglyph(characters,n,done)
+ end
+ local h = d.horiz_variants
+ if h then
+ for i=1,#h do
+ h[i].glyph = expandglyph(characters,h[i].glyph,done)
+ end
+ end
+ local v = d.vert_variants
+ if v then
+ for i=1,#v do
+ v[i].glyph = expandglyph(characters,v[i].glyph,done)
+ end
+ end
+ return d
+ end
+ end
+end
+
+helpers.expandglyph = expandglyph
+
+-- should not be needed as we add .notdef in the engine
+
+local dummyzero = {
+ -- width = 0,
+ -- height = 0,
+ -- depth = 0,
+ commands = { { "special", "" } },
+}
+
+local function adddummysymbols(tfmdata)
+ local characters = tfmdata.characters
+ if not characters[0] then
+ characters[0] = dummyzero
+ end
+ -- if not characters[1] then
+ -- characters[1] = dummyzero -- test only
+ -- end
+end
+
+local dummies_specification = {
+ name = "dummies",
+ description = "dummy symbols",
+ default = true,
+ manipulators = {
+ base = adddummysymbols,
+ node = adddummysymbols,
+ }
+}
+
+registerotffeature(dummies_specification)
+registerafmfeature(dummies_specification)
+
+--
+
+local function addvisualspace(tfmdata)
+ local spacechar = tfmdata.characters[32]
+ if spacechar and not spacechar.commands then
+ local w = spacechar.width
+ local h = tfmdata.parameters.xheight
+ local c = {
+ width = w,
+ commands = { { "rule", h, w } }
+ }
+ local u = addprivate(tfmdata, "visualspace", c)
+ end
+end
+
+local visualspace_specification = {
+ name = "visualspace",
+ description = "visual space",
+ default = true,
+ manipulators = {
+ base = addvisualspace,
+ node = addvisualspace,
+ }
+}
+
+registerotffeature(visualspace_specification)
+registerafmfeature(visualspace_specification)
diff --git a/tex/context/base/mkxl/font-chk.mkxl b/tex/context/base/mkxl/font-chk.mkxl
index b84056e4e..29b159fc2 100644
--- a/tex/context/base/mkxl/font-chk.mkxl
+++ b/tex/context/base/mkxl/font-chk.mkxl
@@ -13,7 +13,7 @@
\writestatus{loading}{ConTeXt Font Macros / Checking}
-\registerctxluafile{font-chk}{}
+\registerctxluafile{font-chk}{autosuffix}
\tracinglostchars\zerocount
diff --git a/tex/context/base/mkxl/font-col.lmt b/tex/context/base/mkxl/font-col.lmt
new file mode 100644
index 000000000..a0e40d0ba
--- /dev/null
+++ b/tex/context/base/mkxl/font-col.lmt
@@ -0,0 +1,477 @@
+if not modules then modules = { } end modules ['font-col'] = {
+ 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"
+}
+
+-- possible optimization: delayed initialization of vectors
+-- we should also share equal vectors (math)
+
+local context, commands, trackers, logs = context, commands, trackers, logs
+local node, nodes, fonts, characters = node, nodes, fonts, characters
+local file, lpeg, table, string = file, lpeg, table, string
+
+local type, next, tonumber, toboolean = type, next, tonumber, toboolean
+local gmatch = string.gmatch
+local fastcopy = table.fastcopy
+local formatters = string.formatters
+
+local nuts = nodes.nuts
+
+local setfont = nuts.setfont
+
+----- traverse_char = nuts.traverse_char
+local nextchar = nuts.traversers.char
+
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+local trace_collecting = false trackers.register("fonts.collecting", function(v) trace_collecting = v end)
+
+local report_fonts = logs.reporter("fonts","collections")
+
+local enableaction = nodes.tasks.enableaction
+local disableaction = nodes.tasks.disableaction
+
+local collections = fonts.collections or { }
+fonts.collections = collections
+
+local definitions = collections.definitions or { }
+collections.definitions = definitions
+
+local vectors = collections.vectors or { }
+collections.vectors = vectors
+
+local helpers = fonts.helpers
+local charcommand = helpers.commands.char
+local rightcommand = helpers.commands.right
+local addprivate = helpers.addprivate
+local hasprivate = helpers.hasprivate
+local fontpatternhassize = helpers.fontpatternhassize
+
+local hashes = fonts.hashes
+local fontdata = hashes.identifiers
+local fontquads = hashes.quads
+local chardata = hashes.characters
+local propdata = hashes.properties
+local mathparameters = hashes.mathparameters
+
+local currentfont = font.current
+local addcharacters = font.addcharacters
+
+local implement = interfaces.implement
+
+local list = { }
+local current = 0
+local enabled = false
+
+local validvectors = table.setmetatableindex(function(t,k)
+ local v = false
+ if not mathparameters[k] then
+ v = vectors[k]
+ end
+ t[k] = v
+ return v
+end)
+
+local function checkenabled()
+ -- a bit ugly but nicer than a fuzzy state while defining math
+ if next(vectors) then
+ if not enabled then
+ enableaction("processors","fonts.collections.process")
+ enabled = true
+ end
+ else
+ if enabled then
+ disableaction("processors","fonts.collections.process")
+ enabled = false
+ end
+ end
+end
+
+collections.checkenabled = checkenabled
+
+function collections.reset(name,font)
+ if font and font ~= "" then
+ local d = definitions[name]
+ if d then
+ d[font] = nil
+ if not next(d) then
+ definitions[name] = nil
+ end
+ end
+ else
+ definitions[name] = nil
+ end
+end
+
+function collections.define(name,font,ranges,details)
+ -- todo: details -> method=force|conditional rscale=
+ -- todo: remap=name
+ local d = definitions[name]
+ if not d then
+ d = { }
+ definitions[name] = d
+ end
+ if name and trace_collecting then
+ report_fonts("extending collection %a using %a",name,font)
+ end
+ details = settings_to_hash(details)
+ -- todo, combine per font start/stop as arrays
+ local offset = details.offset
+ if type(offset) == "string" then
+ offset = characters.getrange(offset,true) or false
+ else
+ offset = tonumber(offset) or false
+ end
+ local target = details.target
+ if type(target) == "string" then
+ target = characters.getrange(target,true) or false
+ else
+ target = tonumber(target) or false
+ end
+ local rscale = tonumber (details.rscale) or 1
+ local force = toboolean(details.force,true)
+ local check = toboolean(details.check,true)
+ local factor = tonumber(details.factor)
+ local features = details.features
+ for s in gmatch(ranges,"[^, ]+") do
+ local start, stop, description, gaps = characters.getrange(s,true)
+ if start and stop then
+ if trace_collecting then
+ if description then
+ report_fonts("using range %a, slots %U - %U, description %a)",s,start,stop,description)
+ end
+ for i=1,#d do
+ local di = d[i]
+ if (start >= di.start and start <= di.stop) or (stop >= di.start and stop <= di.stop) then
+ report_fonts("overlapping ranges %U - %U and %U - %U",start,stop,di.start,di.stop)
+ end
+ end
+ end
+ d[#d+1] = {
+ font = font,
+ start = start,
+ stop = stop,
+ gaps = gaps,
+ offset = offset,
+ target = target,
+ rscale = rscale,
+ force = force,
+ check = check,
+ method = details.method,
+ factor = factor,
+ features = features,
+ }
+ end
+ end
+end
+
+-- todo: provide a lua variant (like with definefont)
+
+function collections.registermain(name)
+ local last = currentfont()
+ if trace_collecting then
+ report_fonts("registering font %a with name %a",last,name)
+ end
+ list[#list+1] = last
+end
+
+-- check: when true, only set when present in font
+-- force: when false, then not set when already set
+
+local uccodes = characters.uccodes
+local lccodes = characters.lccodes
+
+local methods = {
+ lowercase = function(oldchars,newchars,vector,start,stop,cloneid)
+ for k, v in next, oldchars do
+ if k >= start and k <= stop then
+ local lccode = lccodes[k]
+ if k ~= lccode and newchars[lccode] then
+ vector[k] = { cloneid, lccode }
+ end
+ end
+ end
+ end,
+ uppercase = function(oldchars,newchars,vector,start,stop,cloneid)
+ for k, v in next, oldchars do
+ if k >= start and k <= stop then
+ local uccode = uccodes[k]
+ if k ~= uccode and newchars[uccode] then
+ vector[k] = { cloneid, uccode }
+ end
+ end
+ end
+ end,
+}
+
+function collections.clonevector(name)
+ statistics.starttiming(fonts)
+ if trace_collecting then
+ report_fonts("processing collection %a",name)
+ end
+ local definitions = definitions[name]
+ local vector = { }
+ vectors[current] = vector
+ for i=1,#definitions do
+ local definition = definitions[i]
+ local name = definition.font
+ local start = definition.start
+ local stop = definition.stop
+ local check = definition.check
+ local force = definition.force
+ local offset = definition.offset or start
+ local remap = definition.remap -- not used
+ local target = definition.target
+ local method = definition.method
+ local cloneid = list[i]
+ local oldchars = fontdata[current].characters
+ local newchars = fontdata[cloneid].characters
+ local factor = definition.factor
+ if factor then
+ vector.factor = factor
+ end
+ if trace_collecting then
+ if target then
+ report_fonts("remapping font %a to %a for range %U - %U, offset %X, target %U",current,cloneid,start,stop,offset,target)
+ else
+ report_fonts("remapping font %a to %a for range %U - %U, offset %X",current,cloneid,start,stop,offset)
+ end
+ end
+ if method then
+ method = methods[method]
+ end
+ if method then
+ method(oldchars,newchars,vector,start,stop,cloneid)
+ elseif check then
+ if target then
+ for unicode = start, stop do
+ local unic = unicode + offset - start
+ if not newchars[target] then
+ -- not in font
+ elseif force or (not vector[unic] and not oldchars[unic]) then
+ vector[unic] = { cloneid, target }
+ end
+ target = target + 1
+ end
+ elseif remap then
+ -- not used
+ else
+ for unicode = start, stop do
+ local unic = unicode + offset - start
+ if not newchars[unicode] then
+ -- not in font
+ elseif force or (not vector[unic] and not oldchars[unic]) then
+ vector[unic] = cloneid
+ end
+ end
+ end
+ else
+ if target then
+ for unicode = start, stop do
+ local unic = unicode + offset - start
+ if force or (not vector[unic] and not oldchars[unic]) then
+ vector[unic] = { cloneid, target }
+ end
+ target = target + 1
+ end
+ elseif remap then
+ for unicode = start, stop do
+ local unic = unicode + offset - start
+ if force or (not vector[unic] and not oldchars[unic]) then
+ vector[unic] = { cloneid, remap[unicode] }
+ end
+ end
+ else
+ for unicode = start, stop do
+ local unic = unicode + offset - start
+ if force or (not vector[unic] and not oldchars[unic]) then
+ vector[unic] = cloneid
+ end
+ end
+ end
+ end
+ end
+ if trace_collecting then
+ report_fonts("activating collection %a for font %a",name,current)
+ end
+ statistics.stoptiming(fonts)
+ -- for WS: needs checking
+ if validvectors[current] then
+ checkenabled()
+ end
+end
+
+-- we already have this parser
+--
+-- local spec = (P("sa") + P("at") + P("scaled") + P("at") + P("mo")) * P(" ")^1 * (1-P(" "))^1 * P(" ")^0 * -1
+-- local okay = ((1-spec)^1 * spec * Cc(true)) + Cc(false)
+--
+-- if lpegmatch(okay,name) then
+
+function collections.prepare(name) -- we can do this in lua now .. todo
+ current = currentfont()
+ if vectors[current] then
+ return
+ end
+ local properties = propdata[current]
+ local mathsize = properties.mathsize
+ if mathsize == 1 or mathsize == 2 or mathsize == 3 then
+ return
+ end
+ local d = definitions[name]
+ if d then
+ if trace_collecting then
+ local filename = file.basename(properties.filename or "?")
+ report_fonts("applying collection %a to %a, file %a",name,current,filename)
+ end
+ list = { }
+ context.pushcatcodes("prt") -- context.unprotect()
+ context.font_fallbacks_start_cloning()
+ for i=1,#d do
+ local f = d[i]
+ local name = f.font
+ local scale = f.rscale or 1
+ if fontpatternhassize(name) then
+ context.font_fallbacks_clone_unique(name,scale)
+ else
+ context.font_fallbacks_clone_inherited(name,scale)
+ end
+ context.font_fallbacks_register_main(name)
+ end
+ context.font_fallbacks_prepare_clone_vectors(name)
+ context.font_fallbacks_stop_cloning()
+ context.popcatcodes() -- context.protect()
+ end
+end
+
+function collections.report(message)
+ if trace_collecting then
+ report_fonts("tex: %s",message)
+ end
+end
+
+local function monoslot(font,char,parent,factor)
+ local tfmdata = fontdata[font]
+ local privatename = formatters["faked mono %s"](char)
+ local privateslot = hasprivate(tfmdata,privatename)
+ if privateslot then
+ return privateslot
+ else
+ local characters = tfmdata.characters
+ local properties = tfmdata.properties
+ local width = factor * fontquads[parent]
+ local character = characters[char]
+ if character then
+ -- runtime patching of the font (can only be new characters)
+ -- instead of messing with existing dimensions
+ local data = {
+ -- no features so a simple copy
+ width = width,
+ height = character.height,
+ depth = character.depth,
+ commands = {
+ rightcommand[(width - character.width or 0)/2],
+ charcommand[char],
+ }
+ }
+ local u = addprivate(tfmdata, privatename, data)
+ addcharacters(properties.id, { characters = { [u] = data } } )
+ return u
+ else
+ return char
+ end
+ end
+end
+
+function collections.process(head) -- this way we keep feature processing
+ for n, char, font in nextchar, head do
+ local vector = validvectors[font]
+ if vector then
+ local vect = vector[char]
+ if not vect then
+ -- keep it
+ elseif type(vect) == "table" then
+ local newfont = vect[1]
+ local newchar = vect[2]
+ if trace_collecting then
+ report_fonts("remapping character %C in font %a to character %C in font %a%s",
+ char,font,newchar,newfont,not chardata[newfont][newchar] and " (missing)" or ""
+ )
+ end
+ setfont(n,newfont,newchar)
+ else
+ local fakemono = vector.factor
+ if trace_collecting then
+ report_fonts("remapping font %a to %a for character %C%s",
+ font,vect,char,not chardata[vect][char] and " (missing)" or ""
+ )
+ end
+ if fakemono then
+ setfont(n,vect,monoslot(vect,char,font,fakemono))
+ else
+ setfont(n,vect)
+ end
+ end
+ end
+ end
+ return head
+end
+
+function collections.found(font,char) -- this way we keep feature processing
+ if not char then
+ font, char = currentfont(), font
+ end
+ if chardata[font][char] then
+ return true -- in normal font
+ else
+ local v = vectors[font]
+ return v and v[char] and true or false
+ end
+end
+
+-- interface
+
+implement {
+ name = "fontcollectiondefine",
+ actions = collections.define,
+ arguments = "4 strings",
+}
+
+implement {
+ name = "fontcollectionreset",
+ actions = collections.reset,
+ arguments = "2 strings",
+}
+
+implement {
+ name = "fontcollectionprepare",
+ actions = collections.prepare,
+ arguments = "string"
+}
+
+implement {
+ name = "fontcollectionreport",
+ actions = collections.report,
+ arguments = "string"
+}
+
+implement {
+ name = "fontcollectionregister",
+ actions = collections.registermain,
+ arguments = "string"
+}
+
+implement {
+ name = "fontcollectionclone",
+ actions = collections.clonevector,
+ arguments = "string"
+}
+
+implement {
+ name = "doifelsecharinfont",
+ actions = { collections.found, commands.doifelse },
+ arguments = "integer"
+}
diff --git a/tex/context/base/mkxl/font-col.mklx b/tex/context/base/mkxl/font-col.mklx
index 1dd5b25c6..f9874958d 100644
--- a/tex/context/base/mkxl/font-col.mklx
+++ b/tex/context/base/mkxl/font-col.mklx
@@ -27,7 +27,7 @@
\writestatus{loading}{ConTeXt Font Macros / Collections}
-\registerctxluafile{font-col}{}
+\registerctxluafile{font-col}{autosuffix}
\unprotect
diff --git a/tex/context/base/mkxl/font-con.lmt b/tex/context/base/mkxl/font-con.lmt
new file mode 100644
index 000000000..f7a725160
--- /dev/null
+++ b/tex/context/base/mkxl/font-con.lmt
@@ -0,0 +1,1492 @@
+if not modules then modules = { } end modules ['font-con'] = {
+ 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"
+}
+
+-- some names of table entries will be changed (no _)
+
+local next, tostring, tonumber, rawget = next, tostring, tonumber, rawget
+local format, match, lower, gsub, find = string.format, string.match, string.lower, string.gsub, string.find
+local sort, insert, concat = table.sort, table.insert, table.concat
+local sortedkeys, sortedhash, serialize, fastcopy = table.sortedkeys, table.sortedhash, table.serialize, table.fastcopy
+local derivetable = table.derive
+local ioflush = io.flush
+local round = math.round
+local setmetatable, getmetatable, rawget, rawset = setmetatable, getmetatable, rawget, rawset
+
+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)
+
+local report_defining = logs.reporter("fonts","defining")
+
+-- watch out: no negative depths and negative eights permitted in regular fonts
+
+--[[ldx--
+<p>Here we only implement a few helper functions.</p>
+--ldx]]--
+
+local fonts = fonts
+local constructors = fonts.constructors or { }
+fonts.constructors = constructors
+local handlers = fonts.handlers or { } -- can have preloaded tables
+fonts.handlers = handlers
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+
+-- will be directives
+
+constructors.version = 1.01
+constructors.cache = containers.define("fonts", "constructors", constructors.version, false)
+
+constructors.privateoffset = fonts.privateoffsets.textbase or 0xF0000
+
+-- This might become an interface:
+
+local designsizes = allocate()
+constructors.designsizes = designsizes
+local loadedfonts = allocate()
+constructors.loadedfonts = loadedfonts
+
+--[[ldx--
+<p>We need to normalize the scale factor (in scaled points). This has to
+do with the fact that <l n='tex'/> uses a negative multiple of 1000 as
+a signal for a font scaled based on the design size.</p>
+--ldx]]--
+
+local factors = {
+ pt = 65536.0,
+ bp = 65781.8,
+}
+
+function constructors.setfactor(f)
+ constructors.factor = factors[f or 'pt'] or factors.pt
+end
+
+constructors.setfactor()
+
+function constructors.scaled(scaledpoints, designsize) -- handles designsize in sp as well
+ if scaledpoints < 0 then
+ local factor = constructors.factor
+ if designsize then
+ if designsize > factor then -- or just 1000 / when? mp?
+ return (- scaledpoints/1000) * designsize -- sp's
+ else
+ return (- scaledpoints/1000) * designsize * factor
+ end
+ else
+ return (- scaledpoints/1000) * 10 * factor
+ end
+ else
+ return scaledpoints
+ end
+end
+
+function constructors.getprivate(tfmdata)
+ local properties = tfmdata.properties
+ local private = properties.private
+ properties.private = private + 1
+ return private
+end
+
+function constructors.setmathparameter(tfmdata,name,value)
+ local m = tfmdata.mathparameters
+ local c = tfmdata.MathConstants
+ if m then
+ m[name] = value
+ end
+ if c and c ~= m then
+ c[name] = value
+ end
+end
+
+function constructors.getmathparameter(tfmdata,name)
+ local p = tfmdata.mathparameters or tfmdata.MathConstants
+ if p then
+ return p[name]
+ end
+end
+
+--[[ldx--
+<p>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.)</p>
+--ldx]]--
+
+-- The scaler is only used for otf and afm and virtual fonts. If a virtual font has italic
+-- correction make sure to set the hasitalics flag. Some more flags will be added in the
+-- future.
+
+--[[ldx--
+<p>The reason why the scaler was originally split, is that for a while we experimented
+with a helper function. However, in practice the <l n='api'/> calls are too slow to
+make this profitable and the <l n='lua'/> based variant was just faster. A days
+wasted day but an experience richer.</p>
+--ldx]]--
+
+-- experimental, sharing kerns (unscaled and scaled) saves memory
+-- local sharedkerns, basekerns = constructors.check_base_kerns(tfmdata)
+-- loop over descriptions (afm and otf have descriptions, tfm not)
+-- there is no need (yet) to assign a value to chr.tonunicode
+
+-- constructors.prepare_base_kerns(tfmdata) -- optimalization
+
+-- we have target.name=metricfile and target.fullname=RealName and target.filename=diskfilename
+-- when collapsing fonts, luatex looks as both target.name and target.fullname as ttc files
+-- can have multiple subfonts
+
+function constructors.calculatescale(tfmdata,scaledpoints)
+ -- implemented in font-ctx.lmt
+end
+
+function constructors.assignmathparameters(target,original)
+ -- implemented in math-act.lua
+end
+
+function constructors.beforecopyingcharacters(target,original)
+ -- can be used for additional tweaking
+end
+
+function constructors.aftercopyingcharacters(target,original)
+ -- can be used for additional tweaking
+end
+
+function constructors.trytosharefont(target,tfmdata)
+ -- implemented in font-def.lmt
+end
+
+local synonyms = {
+ exheight = "x_height",
+ xheight = "x_height",
+ ex = "x_height",
+ emwidth = "quad",
+ em = "quad",
+ spacestretch = "space_stretch",
+ stretch = "space_stretch",
+ spaceshrink = "space_shrink",
+ shrink = "space_shrink",
+ extraspace = "extra_space",
+ xspace = "extra_space",
+ slantperpoint = "slant",
+}
+
+function constructors.enhanceparameters(parameters)
+ local mt = getmetatable(parameters)
+ local getter = function(t,k)
+ if not k then
+ return nil
+ end
+ local s = synonyms[k]
+ if s then
+ return rawget(t,s) or (mt and mt[s]) or nil
+ end
+ if k == "spacing" then
+ return {
+ width = t.space,
+ stretch = t.space_stretch,
+ shrink = t.space_shrink,
+ extra = t.extra_space,
+ }
+ end
+ return mt and mt[k] or nil
+ end
+ local setter = function(t,k,v)
+ if not k then
+ return 0
+ end
+ local s = synonyms[k]
+ if s then
+ rawset(t,s,v)
+ elseif k == "spacing" then
+ if type(v) == "table" then
+ rawset(t,"space",v.width or 0)
+ rawset(t,"space_stretch",v.stretch or 0)
+ rawset(t,"space_shrink",v.shrink or 0)
+ rawset(t,"extra_space",v.extra or 0)
+ end
+ else
+ rawset(t,k,v)
+ end
+ end
+ setmetatable(parameters, {
+ __index = getter,
+ __newindex = setter,
+ })
+end
+
+local function mathkerns(v,vdelta)
+ local k = { }
+ for i=1,#v do
+ local entry = v[i]
+ local height = entry.height
+ local kern = entry.kern
+ k[i] = {
+ height = height and vdelta*height or 0,
+ kern = kern and vdelta*kern or 0,
+ }
+ end
+ return k
+end
+
+local psfake = 0
+
+local function fixedpsname(psname,fallback)
+ local usedname = psname
+ if psname and psname ~= "" then
+ if find(psname," ",1,true) then
+ usedname = gsub(psname,"[%s]+","-")
+ else
+ -- we assume that the name is sane enough (we might sanitize completely some day)
+ end
+ elseif not fallback or fallback == "" then
+ psfake = psfake + 1
+ psname = "fakename-" .. psfake
+ else
+ -- filenames can be a mess so we do a drastic cleanup
+ psname = fallback
+ usedname = gsub(psname,"[^a-zA-Z0-9]+","-")
+ end
+ return usedname, psname ~= usedname
+end
+
+function constructors.scale(tfmdata,specification)
+ local target = { } -- the new table
+ --
+ if tonumber(specification) then
+ specification = { size = specification }
+ end
+ target.specification = specification
+ --
+ local scaledpoints = specification.size
+ local relativeid = specification.relativeid
+ --
+ local properties = tfmdata.properties or { }
+ local goodies = tfmdata.goodies or { }
+ local resources = tfmdata.resources or { }
+ local descriptions = tfmdata.descriptions or { } -- bad news if empty
+ local characters = tfmdata.characters or { } -- bad news if empty
+ local changed = tfmdata.changed or { } -- for base mode
+ local shared = tfmdata.shared or { }
+ local parameters = tfmdata.parameters or { }
+ local mathparameters = tfmdata.mathparameters or { }
+ --
+ local targetcharacters = { }
+ local targetdescriptions = derivetable(descriptions)
+ local targetparameters = derivetable(parameters)
+ local targetproperties = derivetable(properties)
+ local targetgoodies = goodies -- we need to loop so no metatable
+ target.characters = targetcharacters
+ target.descriptions = targetdescriptions
+ target.parameters = targetparameters
+ -- target.mathparameters = targetmathparameters -- happens elsewhere
+ target.properties = targetproperties
+ target.goodies = targetgoodies
+ target.shared = shared
+ target.resources = resources
+ target.unscaled = tfmdata -- the original unscaled one
+ --
+ -- specification.mathsize : 1=text 2=script 3=scriptscript
+ -- specification.textsize : natural (text)size
+ -- parameters.mathsize : 1=text 2=script 3=scriptscript >1000 enforced size (feature value other than yes)
+ --
+ local mathsize = tonumber(specification.mathsize) or 0
+ local textsize = tonumber(specification.textsize) or scaledpoints
+ local forcedsize = tonumber(parameters.mathsize ) or 0 -- can be set by the feature "mathsize"
+ local extrafactor = tonumber(specification.factor ) or 1
+ if (mathsize == 2 or forcedsize == 2) and parameters.scriptpercentage then
+ scaledpoints = parameters.scriptpercentage * textsize / 100
+ elseif (mathsize == 3 or forcedsize == 3) and parameters.scriptscriptpercentage then
+ scaledpoints = parameters.scriptscriptpercentage * textsize / 100
+ elseif forcedsize > 1000 then -- safeguard
+ scaledpoints = forcedsize
+ else
+ -- in context x and xx also use mathsize
+ end
+ targetparameters.mathsize = mathsize
+ targetparameters.textsize = textsize
+ targetparameters.forcedsize = forcedsize
+ targetparameters.extrafactor = extrafactor
+ --
+ local defaultwidth = resources.defaultwidth or 0
+ local defaultheight = resources.defaultheight or 0
+ local defaultdepth = resources.defaultdepth or 0
+ local units = parameters.units or 1000
+ --
+ -- boundary keys are no longer needed as we now have a string 'right_boundary'
+ -- that can be used in relevant tables (kerns and ligatures) ... not that I ever
+ -- used them
+ --
+ -- boundarychar_label = 0, -- not needed
+ -- boundarychar = 65536, -- there is now a string 'right_boundary'
+ -- false_boundarychar = 65536, -- produces invalid tfm in luatex
+ --
+ targetproperties.language = properties.language or "dflt" -- inherited
+ targetproperties.script = properties.script or "dflt" -- inherited
+ targetproperties.mode = properties.mode or "base" -- inherited
+ targetproperties.method = properties.method
+ --
+ local askedscaledpoints = scaledpoints
+ local scaledpoints, delta = constructors.calculatescale(tfmdata,scaledpoints,nil,specification) -- no shortcut, dan be redefined
+ --
+ local hdelta = delta
+ local vdelta = delta
+ --
+ target.designsize = parameters.designsize -- not really needed so it might become obsolete
+ target.units = units
+ --
+ target.size = scaledpoints
+ --
+ target.subfont = properties.subfont
+ target.cidinfo = properties.cidinfo
+ target.format = properties.format
+ --
+ local original = properties.original or tfmdata.original
+ local fontname = properties.fontname or tfmdata.fontname
+ local fullname = properties.fullname or tfmdata.fullname
+ local filename = properties.filename or tfmdata.filename
+ local psname = properties.psname or tfmdata.psname
+ local name = properties.name or tfmdata.name
+ --
+ -- The psname used in pdf file as well as for selecting subfont in ttc although
+ -- we don't need that subfont look up here (mapfile stuff).
+ --
+ local psname, psfixed = fixedpsname(psname,fontname or fullname or file.nameonly(filename))
+ --
+ target.original = original
+ target.fontname = fontname
+ target.fullname = fullname
+ target.filename = filename
+ target.psname = psname
+ target.name = name
+ --
+ properties.fontname = fontname
+ properties.fullname = fullname
+ properties.filename = filename
+ properties.psname = psname
+ properties.name = name
+ -- expansion (hz)
+ local expansion = parameters.expansion
+ if expansion then
+ target.stretch = expansion.stretch
+ target.shrink = expansion.shrink
+ target.step = expansion.step
+ end
+ -- slanting
+ local slantfactor = parameters.slantfactor or 0
+ if slantfactor ~= 0 then
+ target.slant = slantfactor * 1000
+ else
+ target.slant = 0
+ end
+ -- widening
+ local extendfactor = parameters.extendfactor or 0
+ if extendfactor ~= 0 and extendfactor ~= 1 then
+ hdelta = hdelta * extendfactor
+ target.extend = extendfactor * 1000
+ else
+ target.extend = 1000 -- extent ?
+ end
+ -- squeezing
+ local squeezefactor = parameters.squeezefactor or 0
+ if squeezefactor ~= 0 and squeezefactor ~= 1 then
+ vdelta = vdelta * squeezefactor
+ target.squeeze = squeezefactor * 1000
+ else
+ target.squeeze = 1000 -- extent ?
+ end
+ -- effects
+ local mode = parameters.mode or 0
+ if mode ~= 0 then
+ target.mode = mode
+ end
+ local width = parameters.width or 0
+ if width ~= 0 then
+ target.width = width * delta * 1000 / 655360
+ end
+ --
+ targetparameters.factor = delta
+ targetparameters.hfactor = hdelta
+ targetparameters.vfactor = vdelta
+ targetparameters.size = scaledpoints
+ targetparameters.units = units
+ targetparameters.scaledpoints = askedscaledpoints
+ targetparameters.mode = mode
+ targetparameters.width = width
+ --
+ local hasquality = parameters.expansion or parameters.protrusion
+ local hasitalics = properties.hasitalics
+ local autoitalicamount = properties.autoitalicamount
+ local stackmath = not properties.nostackmath
+ local haskerns = properties.haskerns or properties.mode == "base" -- we can have afm in node mode
+ local hasligatures = properties.hasligatures or properties.mode == "base" -- we can have afm in node mode
+ local realdimensions = properties.realdimensions
+ local writingmode = properties.writingmode or "horizontal"
+ --
+ if changed and not next(changed) then
+ changed = false
+ end
+ --
+ target.writingmode = writingmode == "vertical" and "vertical" or "horizontal"
+ --
+ target.postprocessors = tfmdata.postprocessors
+ --
+ local targetslant = (parameters.slant or parameters[1] or 0) * factors.pt -- per point
+ local targetspace = (parameters.space or parameters[2] or 0) * hdelta
+ local targetspace_stretch = (parameters.space_stretch or parameters[3] or 0) * hdelta
+ local targetspace_shrink = (parameters.space_shrink or parameters[4] or 0) * hdelta
+ local targetx_height = (parameters.x_height or parameters[5] or 0) * vdelta
+ local targetquad = (parameters.quad or parameters[6] or 0) * hdelta
+ local targetextra_space = (parameters.extra_space or parameters[7] or 0) * hdelta
+ --
+ targetparameters.slant = targetslant -- slantperpoint
+ targetparameters.space = targetspace
+ targetparameters.space_stretch = targetspace_stretch
+ targetparameters.space_shrink = targetspace_shrink
+ targetparameters.x_height = targetx_height
+ targetparameters.quad = targetquad
+ targetparameters.extra_space = targetextra_space
+ --
+ local hshift = parameters.hshift
+ if hshift then
+ targetparameters.hshift = delta * hshift
+ end
+ local vshift = parameters.vshift
+ if vshift then
+ targetparameters.vshift = delta * vshift
+ end
+ --
+ local ascender = parameters.ascender
+ if ascender then
+ targetparameters.ascender = delta * ascender
+ end
+ local descender = parameters.descender
+ if descender then
+ targetparameters.descender = delta * descender
+ end
+ --
+ constructors.enhanceparameters(targetparameters) -- official copies for us, now virtual
+ --
+ local scaledwidth = defaultwidth * hdelta
+ local scaledheight = defaultheight * vdelta
+ local scaleddepth = defaultdepth * vdelta
+ --
+ local hasmath = (properties.hasmath or next(mathparameters)) and true
+ --
+ if hasmath then
+ constructors.assignmathparameters(target,tfmdata) -- does scaling and whatever is needed
+ properties.hasmath = true
+ target.nomath = false
+ target.MathConstants = target.mathparameters
+ else
+ properties.hasmath = false
+ target.nomath = true
+ target.mathparameters = nil -- nop
+ end
+ --
+ -- During the transition to opentype the engine had troubles with italics so we had some
+ -- additional code for fixing that. In node mode (text) we don't care much if italics gets
+ -- passed because the engine does nothing with them then.
+ --
+ if hasmath then
+ local mathitalics = properties.mathitalics
+ if mathitalics == false then
+ if trace_defining then
+ report_defining("%s italics %s for font %a, fullname %a, filename %a","math",hasitalics and "ignored" or "disabled",name,fullname,filename)
+ end
+ hasitalics = false
+ autoitalicamount = false
+ end
+ else
+ local textitalics = properties.textitalics
+ if textitalics == false then
+ if trace_defining then
+ report_defining("%s italics %s for font %a, fullname %a, filename %a","text",hasitalics and "ignored" or "disabled",name,fullname,filename)
+ end
+ hasitalics = false
+ autoitalicamount = false
+ end
+ end
+ --
+ -- end of context specific trickery
+ --
+ if trace_defining then
+ report_defining("defining tfm, name %a, fullname %a, filename %a, %spsname %a, hscale %a, vscale %a, math %a, italics %a",
+ name,fullname,filename,psfixed and "(fixed) " or "",psname,hdelta,vdelta,
+ hasmath and "enabled" or "disabled",hasitalics and "enabled" or "disabled")
+ end
+ --
+ constructors.beforecopyingcharacters(target,tfmdata)
+ --
+ local sharedkerns = { }
+ --
+ -- we can have a dumb mode (basemode without math etc) that skips most
+ --
+ for unicode, character in next, characters do
+ local chr, description, index
+ if changed then
+ local c = changed[unicode]
+ if c and c ~= unicode then
+ if c then
+ description = descriptions[c] or descriptions[unicode] or character
+ character = characters[c] or character
+ index = description.index or c
+ else
+ description = descriptions[unicode] or character
+ index = description.index or unicode
+ end
+ else
+ description = descriptions[unicode] or character
+ index = description.index or unicode
+ end
+ else
+ description = descriptions[unicode] or character
+ index = description.index or unicode
+ end
+ local width = description.width
+ local height = description.height
+ local depth = description.depth
+ local isunicode = description.unicode
+ if realdimensions then
+ -- this is mostly for checking issues
+ if not height or height == 0 then
+ local bb = description.boundingbox
+ local ht = bb[4]
+ if ht ~= 0 then
+ height = ht
+ end
+ if not depth or depth == 0 then
+ local dp = -bb[2]
+ if dp ~= 0 then
+ depth = dp
+ end
+ end
+ elseif not depth or depth == 0 then
+ local dp = -description.boundingbox[2]
+ if dp ~= 0 then
+ depth = dp
+ end
+ end
+ end
+ 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 isunicode then
+ chr = {
+ index = index,
+ height = height,
+ depth = depth,
+ width = width,
+ unicode = isunicode,
+ }
+ else
+ chr = {
+ index = index,
+ height = height,
+ depth = depth,
+ width = width,
+ }
+ end
+ else
+ if isunicode then
+ chr = {
+ index = index,
+ height = height,
+ width = width,
+ unicode = isunicode,
+ }
+ else
+ chr = {
+ index = index,
+ height = height,
+ width = width,
+ }
+ end
+ end
+ if hasquality then
+ -- we could move these calculations elsewhere (saves calculations)
+ local ve = character.expansion_factor
+ if ve then
+ chr.expansion_factor = ve*1000 -- expansionfactor, hm, can happen elsewhere
+ end
+ local vl = character.left_protruding
+ if vl then
+ chr.left_protruding = width*vl
+ end
+ local vr = character.right_protruding
+ if vr then
+ chr.right_protruding = width*vr
+ end
+ end
+ --
+ if hasmath then
+ --
+ -- todo, just operate on descriptions.math
+ local vn = character.next
+ if vn then
+ chr.next = vn
+ else
+ local vv = character.vert_variants
+ if vv then
+ local t = { }
+ for i=1,#vv do
+ local vvi = vv[i]
+ local s = vvi["start"] or 0
+ local e = vvi["end"] or 0
+ local a = vvi["advance"] or 0
+ t[i] = { -- zero check nicer for 5.3
+ ["start"] = s == 0 and 0 or s * vdelta,
+ ["end"] = e == 0 and 0 or e * vdelta,
+ ["advance"] = a == 0 and 0 or a * vdelta,
+ ["extender"] = vvi["extender"],
+ ["glyph"] = vvi["glyph"],
+ }
+ end
+ chr.vert_variants = t
+ else
+ local hv = character.horiz_variants
+ if hv then
+ local t = { }
+ for i=1,#hv do
+ local hvi = hv[i]
+ local s = hvi["start"] or 0
+ local e = hvi["end"] or 0
+ local a = hvi["advance"] or 0
+ t[i] = { -- zero check nicer for 5.3
+ ["start"] = s == 0 and 0 or s * hdelta,
+ ["end"] = e == 0 and 0 or e * hdelta,
+ ["advance"] = a == 0 and 0 or a * hdelta,
+ ["extender"] = hvi["extender"],
+ ["glyph"] = hvi["glyph"],
+ }
+ end
+ chr.horiz_variants = t
+ end
+ end
+ -- todo also check mathitalics (or that one can go away)
+ end
+ local vi = character.vert_italic
+ if vi and vi ~= 0 then
+ chr.vert_italic = vi*hdelta
+ end
+ local va = character.accent
+ if va then
+ chr.top_accent = vdelta*va
+ end
+ if stackmath then
+ local mk = character.mathkerns
+ if mk then
+ local tr = mk.topright
+ local tl = mk.topleft
+ local br = mk.bottomright
+ local bl = mk.bottomleft
+ chr.mathkern = { -- singular -> should be patched in luatex !
+ top_right = tr and mathkerns(tr,vdelta) or nil,
+ top_left = tl and mathkerns(tl,vdelta) or nil,
+ bottom_right = br and mathkerns(br,vdelta) or nil,
+ bottom_left = bl and mathkerns(bl,vdelta) or nil,
+ }
+ end
+ end
+ if hasitalics then
+ local vi = character.italic
+ if vi and vi ~= 0 then
+ chr.italic = vi*hdelta
+ end
+ end
+ elseif autoitalicamount then -- itlc feature
+ local vi = description.italic
+ if not vi then
+ local bb = description.boundingbox
+ if bb then
+ local vi = bb[3] - description.width + autoitalicamount
+ if vi > 0 then -- < 0 indicates no overshoot or a very small auto italic
+ chr.italic = vi*hdelta
+ end
+ else
+ -- report_defining("no boundingbox for character %C in font %a, fullname %a, filename %a",unicode,name,fullname,filename)
+ end
+ elseif vi ~= 0 then
+ chr.italic = vi*hdelta
+ end
+ elseif hasitalics then -- unlikely
+ local vi = character.italic
+ if vi and vi ~= 0 then
+ chr.italic = vi*hdelta
+ end
+ end
+ if haskerns then
+ local vk = character.kerns
+ if vk then
+ 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
+ end
+ if hasligatures then
+ local vl = character.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
+ local vc = character.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" or key == "rule" 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[i] = { key, ivc[2]*hdelta }
+ elseif key == "down" then
+ tt[i] = { key, ivc[2]*vdelta }
+ elseif key == "rule" then
+ tt[i] = { key, ivc[2]*vdelta, ivc[3]*hdelta }
+ else -- not comment
+ tt[i] = ivc -- shared since in cache and untouched
+ end
+ end
+ chr.commands = tt
+ else
+ chr.commands = vc
+ end
+ -- chr.index = nil
+ end
+ targetcharacters[unicode] = chr
+ end
+ --
+ properties.setitalics = hasitalics -- for postprocessing
+ --
+ constructors.aftercopyingcharacters(target,tfmdata)
+ --
+ constructors.trytosharefont(target,tfmdata)
+ --
+ -- catch inconsistencies (for now)
+ --
+ local vfonts = target.fonts
+ if not vfonts or #vfonts == 0 then
+ target.fonts = { { id = 0 } }
+ end
+ --
+ return target
+end
+
+function constructors.finalize(tfmdata)
+ if tfmdata.properties and tfmdata.properties.finalized then
+ return
+ end
+ --
+ if not tfmdata.characters then
+ return nil
+ end
+ --
+ if not tfmdata.goodies then
+ tfmdata.goodies = { }
+ end
+ --
+ local parameters = tfmdata.parameters
+ if not parameters then
+ return nil
+ end
+ --
+ if not parameters.expansion then
+ parameters.expansion = {
+ stretch = tfmdata.stretch or 0,
+ shrink = tfmdata.shrink or 0,
+ step = tfmdata.step or 0,
+ }
+ end
+ --
+ if not parameters.size then
+ parameters.size = tfmdata.size
+ end
+ --
+ if not parameters.mode then
+ parameters.mode = 0
+ end
+ --
+ if not parameters.width then
+ parameters.width = 0
+ end
+ --
+ if not parameters.slantfactor then
+ parameters.slantfactor = (tfmdata.slant or 0)/1000
+ end
+ --
+ if not parameters.extendfactor then
+ parameters.extendfactor = (tfmdata.extend or 1000)/1000
+ end
+ --
+ if not parameters.squeezefactor then
+ parameters.squeezefactor = (tfmdata.squeeze or 1000)/1000
+ end
+ --
+ local designsize = parameters.designsize
+ if designsize then
+ parameters.minsize = tfmdata.minsize or designsize
+ parameters.maxsize = tfmdata.maxsize or designsize
+ else
+ designsize = factors.pt * 10
+ parameters.designsize = designsize
+ parameters.minsize = designsize
+ parameters.maxsize = designsize
+ end
+ parameters.minsize = tfmdata.minsize or parameters.designsize
+ parameters.maxsize = tfmdata.maxsize or parameters.designsize
+ --
+ if not parameters.units then
+ parameters.units = tfmdata.units or 1000
+ end
+ --
+ if not tfmdata.descriptions then
+ local descriptions = { } -- yes or no
+ setmetatableindex(descriptions, function(t,k) local v = { } t[k] = v return v end)
+ tfmdata.descriptions = descriptions
+ end
+ --
+ local properties = tfmdata.properties
+ if not properties then
+ properties = { }
+ tfmdata.properties = properties
+ end
+ --
+ properties.fontname = properties.fontname or tfmdata.fontname
+ properties.filename = properties.filename or tfmdata.filename
+ properties.fullname = properties.fullname or tfmdata.fullname
+ properties.name = properties.name or tfmdata.name
+ properties.psname = properties.psname or tfmdata.psname
+ --
+ properties.subfont = tfmdata.subfont or nil
+ properties.cidinfo = tfmdata.cidinfo or nil
+ properties.format = tfmdata.format or "type1"
+ properties.writingmode = tfmdata.writingmode or "horizontal"
+ properties.usedbitmap = tfmdata.usedbitmap
+ --
+ if not tfmdata.resources then
+ tfmdata.resources = { }
+ end
+ if not tfmdata.shared then
+ tfmdata.shared = { }
+ end
+ --
+ -- tfmdata.fonts
+ -- tfmdata.unscaled
+ --
+ if not properties.hasmath then
+ properties.hasmath = not tfmdata.nomath
+ end
+ --
+ tfmdata.MathConstants = nil
+ tfmdata.postprocessors = nil
+ --
+ tfmdata.fontname = nil
+ tfmdata.filename = nil
+ tfmdata.fullname = nil
+ tfmdata.name = nil -- most tricky part
+ tfmdata.psname = nil
+ --
+ tfmdata.subfont = nil
+ tfmdata.cidinfo = nil
+ tfmdata.format = nil
+ tfmdata.nomath = nil
+ tfmdata.designsize = nil
+ --
+ tfmdata.size = nil
+ tfmdata.stretch = nil
+ tfmdata.shrink = nil
+ tfmdata.step = nil
+ tfmdata.slant = nil
+ tfmdata.extend = nil
+ tfmdata.squeeze = nil
+ tfmdata.mode = nil
+ tfmdata.width = nil
+ tfmdata.units = nil
+ --
+ tfmdata.cache = nil
+ --
+ properties.finalized = true
+ --
+ return tfmdata
+end
+
+--[[ldx--
+<p>A unique hash value is generated by:</p>
+--ldx]]--
+
+local hashmethods = { }
+constructors.hashmethods = hashmethods
+
+function constructors.hashfeatures(specification) -- will be overloaded
+ local features = specification.features
+ if features then
+ local t, n = { }, 0
+ for category, list in sortedhash(features) do
+ if next(list) then
+ local hasher = hashmethods[category]
+ if hasher then
+ local hash = hasher(list)
+ if hash then
+ n = n + 1
+ t[n] = category .. ":" .. hash
+ end
+ end
+ end
+ end
+ if n > 0 then
+ return concat(t," & ")
+ end
+ end
+ return "unknown"
+end
+
+hashmethods.normal = function(list)
+ local s = { }
+ local n = 0
+ for k, v in next, list do
+ if not k then
+ -- no need to add to hash
+ elseif k == "number" or k == "features" then
+ -- no need to add to hash (maybe we need a skip list)
+ else
+ n = n + 1
+ if type(v) == "table" then
+ -- table.sequenced
+ local t = { }
+ local m = 0
+ for k, v in next, v do
+ m = m + 1
+ t[m] = k .. '=' .. tostring(v)
+ end
+ sort(t)
+ s[n] = k .. '={' .. concat(t,",") .. "}"
+ else
+ s[n] = k .. '=' .. tostring(v)
+ end
+ end
+ end
+ if n > 0 then
+ sort(s)
+ return concat(s,"+")
+ end
+end
+
+--[[ldx--
+<p>In principle we can share tfm tables when we are in need 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 <l n='luatex'/>.</p>
+--ldx]]--
+
+function constructors.hashinstance(specification,force)
+ -- implemented in font-ctx.lmt
+end
+
+function constructors.setname(tfmdata,specification)
+ -- dummy (still called in generic font-otl)
+end
+
+function constructors.checkedfilename(data)
+ local foundfilename = data.foundfilename
+ if not foundfilename then
+ local askedfilename = data.filename or ""
+ if askedfilename ~= "" then
+ askedfilename = resolvers.resolve(askedfilename) -- no shortcut
+ foundfilename = resolvers.findbinfile(askedfilename,"") or ""
+ if foundfilename == "" then
+ report_defining("source file %a is not found",askedfilename)
+ foundfilename = resolvers.findbinfile(file.basename(askedfilename),"") or ""
+ if foundfilename ~= "" then
+ report_defining("using source file %a due to cache mismatch",foundfilename)
+ end
+ end
+ end
+ data.foundfilename = foundfilename
+ end
+ return foundfilename
+end
+
+local formats = allocate()
+fonts.formats = formats
+
+setmetatableindex(formats, function(t,k)
+ local l = lower(k)
+ if rawget(t,k) then
+ t[k] = l
+ return l
+ end
+ return rawget(t,file.suffix(l))
+end)
+
+do
+
+ local function setindeed(mode,source,target,group,name,position)
+ local action = source[mode]
+ if not action then
+ return
+ end
+ local t = target[mode]
+ if not t then
+ report_defining("fatal error in setting feature %a, group %a, mode %a",name,group,mode)
+ os.exit()
+ elseif position then
+ -- todo: remove existing
+ insert(t, position, { name = name, action = action })
+ else
+ for i=1,#t do
+ local ti = t[i]
+ if ti.name == name then
+ ti.action = action
+ return
+ end
+ end
+ insert(t, { name = name, action = action })
+ end
+ end
+
+ local function set(group,name,target,source)
+ target = target[group]
+ if not target then
+ report_defining("fatal target error in setting feature %a, group %a",name,group)
+ os.exit()
+ end
+ local source = source[group]
+ if not source then
+ report_defining("fatal source error in setting feature %a, group %a",name,group)
+ os.exit()
+ end
+ local position = source.position
+ setindeed("node",source,target,group,name,position)
+ setindeed("base",source,target,group,name,position)
+ setindeed("plug",source,target,group,name,position)
+ end
+
+ local function register(where,specification)
+ local name = specification.name
+ if name and name ~= "" then
+ local default = specification.default
+ local description = specification.description
+ local initializers = specification.initializers
+ local processors = specification.processors
+ local manipulators = specification.manipulators
+ local modechecker = specification.modechecker
+ if default then
+ where.defaults[name] = default
+ end
+ if description and description ~= "" then
+ where.descriptions[name] = description
+ end
+ if initializers then
+ set('initializers',name,where,specification)
+ end
+ if processors then
+ set('processors', name,where,specification)
+ end
+ if manipulators then
+ set('manipulators',name,where,specification)
+ end
+ if modechecker then
+ where.modechecker = modechecker
+ end
+ end
+ end
+
+ constructors.registerfeature = register
+
+ function constructors.getfeatureaction(what,where,mode,name)
+ what = handlers[what].features
+ if what then
+ where = what[where]
+ if where then
+ mode = where[mode]
+ if mode then
+ for i=1,#mode do
+ local m = mode[i]
+ if m.name == name then
+ return m.action
+ end
+ end
+ end
+ end
+ end
+ end
+
+ local newfeatures = { }
+ constructors.newfeatures = newfeatures -- downward compatible
+ constructors.features = newfeatures
+
+ local function setnewfeatures(what)
+ local handler = handlers[what]
+ local features = handler.features
+ if not features then
+ local tables = handler.tables -- can be preloaded
+ local statistics = handler.statistics -- can be preloaded
+ features = allocate {
+ defaults = { },
+ descriptions = tables and tables.features or { },
+ used = statistics and statistics.usedfeatures or { },
+ initializers = { base = { }, node = { }, plug = { } },
+ processors = { base = { }, node = { }, plug = { } },
+ manipulators = { base = { }, node = { }, plug = { } },
+ }
+ features.register = function(specification) return register(features,specification) end
+ handler.features = features -- will also become hidden
+ end
+ return features
+ end
+
+ setmetatable(newfeatures, {
+ __call = function(t,k) local v = t[k] return v end,
+ __index = function(t,k) local v = setnewfeatures(k) t[k] = v return v end,
+ })
+
+end
+
+do
+
+ local newhandler = { }
+ constructors.handlers = newhandler -- downward compatible
+ constructors.newhandler = newhandler
+
+ local function setnewhandler(what) -- could be a metatable newindex
+ local handler = handlers[what]
+ if not handler then
+ handler = { }
+ handlers[what] = handler
+ end
+ return handler
+ end
+
+ setmetatable(newhandler, {
+ __call = function(t,k) local v = t[k] return v end,
+ __index = function(t,k) local v = setnewhandler(k) t[k] = v return v end,
+ })
+
+end
+
+do
+ -- a pitty that we need to be generic as we have nicer mechanisms for this ...
+
+ local newenhancer = { }
+ constructors.enhancers = newenhancer
+ constructors.newenhancer = newenhancer
+
+ local function setnewenhancer(format)
+
+ local handler = handlers[format]
+ local enhancers = handler.enhancers
+
+ if not enhancers then
+
+ local actions = allocate() -- no need to allocate thee
+ local before = allocate()
+ local after = allocate()
+ local order = allocate()
+ local known = { }
+ local nofsteps = 0
+ local patches = { before = before, after = after }
+
+ local trace = false
+ local report = logs.reporter("fonts",format .. " enhancing")
+
+ trackers.register(format .. ".loading", function(v) trace = v end)
+
+ local function enhance(name,data,filename,raw)
+ local enhancer = actions[name]
+ if enhancer then
+ if trace then
+ report("apply enhancement %a to file %a",name,filename)
+ ioflush()
+ end
+ enhancer(data,filename,raw)
+ else
+ -- no message as we can have private ones
+ end
+ end
+
+ local function apply(data,filename,raw)
+ local basename = file.basename(lower(filename))
+ if trace then
+ report("%s enhancing file %a","start",filename)
+ end
+ ioflush() -- we want instant messages
+ for e=1,nofsteps do
+ local enhancer = order[e]
+ local b = before[enhancer]
+ if b then
+ for pattern, action in next, b do
+ if find(basename,pattern) then
+ action(data,filename,raw)
+ end
+ end
+ end
+ enhance(enhancer,data,filename,raw) -- we have one installed: check extra features
+ local a = after[enhancer]
+ if a then
+ for pattern, action in next, a do
+ if find(basename,pattern) then
+ action(data,filename,raw)
+ end
+ end
+ end
+ ioflush() -- we want instant messages
+ end
+ if trace then
+ report("%s enhancing file %a","stop",filename)
+ end
+ ioflush() -- we want instant messages
+ end
+
+ local function register(what,action)
+ if action then
+ if actions[what] then
+ -- overloading, e.g."check extra features"
+ else
+ nofsteps = nofsteps + 1
+ order[nofsteps] = what
+ known[what] = nofsteps
+ end
+ actions[what] = action
+ else
+ report("bad enhancer %a",what)
+ end
+ end
+
+ -- We used to have a lot of enhancers but no longer with the new font loader. The order of enhancers
+ -- is the order of definition. The before/after patches are there for old times sake and happen
+ -- before or after a (named) enhancer. An example of a set enhancer is "check extra features" so one
+ -- one set patches before or after that is applied. Unknown enhancers are auto-registered. It's a bit
+ -- messy but we keep it for compatibility reasons.
+ --
+ -- fonts.handlers.otf.enhancers.patches.register("before","some patches","somefont",function(data,filename)
+ -- print("!!!!!!!") -- before | after
+ -- end)
+ --
+ -- fonts.handlers.otf.enhancers.register("more patches",function(data,filename)
+ -- print("???????") -- enhance
+ -- end)
+
+ local function patch(what,where,pattern,action)
+ local pw = patches[what]
+ if pw then
+ local ww = pw[where]
+ if ww then
+ ww[pattern] = action
+ else
+ pw[where] = { [pattern] = action }
+ if not known[where] then
+ nofsteps = nofsteps + 1
+ order[nofsteps] = where
+ known[where] = nofsteps
+ end
+ end
+ end
+ end
+
+ enhancers = {
+ register = register,
+ apply = apply,
+ patch = patch,
+ report = report,
+ patches = {
+ register = patch,
+ report = report,
+ }, -- for old times sake
+ }
+
+ handler.enhancers = enhancers
+ end
+ return enhancers
+ end
+
+ setmetatable(newenhancer, {
+ __call = function(t,k) local v = t[k] return v end,
+ __index = function(t,k) local v = setnewenhancer(k) t[k] = v return v end,
+ })
+
+end
+
+--[[ldx--
+<p>We need to check for default features. For this we provide
+a helper function.</p>
+--ldx]]--
+
+function constructors.checkedfeatures(what,features)
+ local defaults = handlers[what].features.defaults
+ if features and next(features) then
+ features = fastcopy(features) -- can be inherited (mt) but then no loops possible
+ for key, value in next, defaults do
+ if features[key] == nil then
+ features[key] = value
+ end
+ end
+ return features
+ else
+ return fastcopy(defaults) -- we can change features in place
+ end
+end
+
+-- before scaling
+
+function constructors.initializefeatures(what,tfmdata,features,trace,report)
+ if features and next(features) then
+ local properties = tfmdata.properties or { } -- brrr
+ local whathandler = handlers[what]
+ local whatfeatures = whathandler.features
+ local whatmodechecker = whatfeatures.modechecker
+ -- properties.mode can be enforces (for instance in font-otd)
+ local mode = properties.mode or (whatmodechecker and whatmodechecker(tfmdata,features,features.mode)) or features.mode or "base"
+ properties.mode = mode -- also status
+ features.mode = mode -- both properties.mode or features.mode can be changed
+ --
+ local done = { }
+ while true do
+ local redo = false
+ local initializers = whatfeatures.initializers[mode]
+ if initializers then
+ for i=1,#initializers do
+ local step = initializers[i]
+ local feature = step.name
+-- we could intercept mode here .. needs a rewrite of this whole loop then but it's cleaner that way
+ local value = features[feature]
+ if not value then
+ -- disabled
+ elseif done[feature] then
+ -- already done
+ else
+ local action = step.action
+ if trace then
+ report("initializing feature %a to %a for mode %a for font %a",feature,
+ value,mode,tfmdata.properties.fullname)
+ end
+ action(tfmdata,value,features) -- can set mode (e.g. goodies) so it can trigger a restart
+ if mode ~= properties.mode or mode ~= features.mode then
+ if whatmodechecker then
+ properties.mode = whatmodechecker(tfmdata,features,properties.mode) -- force checking
+ features.mode = properties.mode
+ end
+ if mode ~= properties.mode then
+ mode = properties.mode
+ redo = true
+ end
+ end
+ done[feature] = true
+ end
+ if redo then
+ break
+ end
+ end
+ if not redo then
+ break
+ end
+ else
+ break
+ end
+ end
+ properties.mode = mode -- to be sure
+ return true
+ else
+ return false
+ end
+end
+
+-- while typesetting
+
+function constructors.collectprocessors(what,tfmdata,features,trace,report)
+ local processes = { }
+ local nofprocesses = 0
+ if features and next(features) then
+ local properties = tfmdata.properties
+ local whathandler = handlers[what]
+ local whatfeatures = whathandler.features
+ local whatprocessors = whatfeatures.processors
+ local mode = properties.mode
+ local processors = whatprocessors[mode]
+ if processors then
+ for i=1,#processors do
+ local step = processors[i]
+ local feature = step.name
+ if features[feature] then
+ local action = step.action
+ if trace then
+ report("installing feature processor %a for mode %a for font %a",feature,mode,tfmdata.properties.fullname)
+ end
+ if action then
+ nofprocesses = nofprocesses + 1
+ processes[nofprocesses] = action
+ end
+ end
+ end
+ elseif trace then
+ report("no feature processors for mode %a for font %a",mode,properties.fullname)
+ end
+ end
+ return processes
+end
+
+-- after scaling
+
+function constructors.applymanipulators(what,tfmdata,features,trace,report)
+ if features and next(features) then
+ local properties = tfmdata.properties
+ local whathandler = handlers[what]
+ local whatfeatures = whathandler.features
+ local whatmanipulators = whatfeatures.manipulators
+ local mode = properties.mode
+ local manipulators = whatmanipulators[mode]
+ if manipulators then
+ for i=1,#manipulators do
+ local step = manipulators[i]
+ local feature = step.name
+ local value = features[feature]
+ if value then
+ local action = step.action
+ if trace then
+ report("applying feature manipulator %a for mode %a for font %a",feature,mode,properties.fullname)
+ end
+ if action then
+ action(tfmdata,feature,value)
+ end
+ end
+ end
+ end
+ end
+end
+
+function constructors.addcoreunicodes(unicodes) -- maybe make this a metatable if used at all
+ if not unicodes then
+ unicodes = { }
+ end
+ unicodes.space = 0x0020
+ unicodes.hyphen = 0x002D
+ unicodes.zwj = 0x200D
+ unicodes.zwnj = 0x200C
+ return unicodes
+end
diff --git a/tex/context/base/mkxl/font-ctx.lmt b/tex/context/base/mkxl/font-ctx.lmt
new file mode 100644
index 000000000..3d269d8cb
--- /dev/null
+++ b/tex/context/base/mkxl/font-ctx.lmt
@@ -0,0 +1,3180 @@
+if not modules then modules = { } end modules ['font-ctx'] = {
+ 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"
+}
+
+-- At some point I will clean up the code here so that at the tex end
+-- the table interface is used.
+--
+-- Todo: make a proper 'next id' mechanism (register etc) or wait till 'true'
+-- in virtual fonts indices is implemented.
+--
+-- Olders code can be found in the mkiv lua variant.
+
+local tostring, next, type, rawget, tonumber = tostring, next, type, rawget, tonumber
+
+local format, gmatch, match, find, lower, upper, gsub, byte, topattern = string.format, string.gmatch, string.match, string.find, string.lower, string.upper, string.gsub, string.byte, string.topattern
+local concat, serialize, sort, fastcopy, mergedtable = table.concat, table.serialize, table.sort, table.fastcopy, table.merged
+local sortedhash, sortedkeys, sequenced = table.sortedhash, table.sortedkeys, table.sequenced
+local parsers = utilities.parsers
+local settings_to_hash, hash_to_string, settings_to_array = parsers.settings_to_hash, parsers.hash_to_string, parsers.settings_to_array
+local formatcolumns = utilities.formatters.formatcolumns
+local mergehashes = utilities.parsers.mergehashes
+local formatters = string.formatters
+local basename = file.basename
+
+local utfchar, utfbyte = utf.char, utf.byte
+local round = math.round
+
+local context, commands = context, commands
+
+local P, S, C, Cc, Cf, Cg, Ct, lpegmatch = lpeg.P, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Ct, lpeg.match
+
+local trace_features = false trackers.register("fonts.features", function(v) trace_features = v end)
+local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end)
+local trace_designsize = false trackers.register("fonts.designsize", function(v) trace_designsize = v end)
+local trace_usage = false trackers.register("fonts.usage", function(v) trace_usage = v end)
+local trace_mapfiles = false trackers.register("fonts.mapfiles", function(v) trace_mapfiles = v end)
+local trace_automode = false trackers.register("fonts.automode", function(v) trace_automode = v end)
+local trace_merge = false trackers.register("fonts.merge", function(v) trace_merge = v end)
+
+local report = logs.reporter("fonts")
+local report_features = logs.reporter("fonts","features")
+local report_cummulative = logs.reporter("fonts","cummulative")
+local report_defining = logs.reporter("fonts","defining")
+local report_status = logs.reporter("fonts","status")
+local report_mapfiles = logs.reporter("fonts","mapfiles")
+
+local setmetatableindex = table.setmetatableindex
+
+local implement = interfaces.implement
+
+local chardata = characters.data
+
+local fonts = fonts
+local handlers = fonts.handlers
+local otf = handlers.otf -- brrr
+local afm = handlers.afm -- brrr
+local tfm = handlers.tfm -- brrr
+local names = fonts.names
+local definers = fonts.definers
+local specifiers = fonts.specifiers
+local constructors = fonts.constructors
+local loggers = fonts.loggers
+local fontgoodies = fonts.goodies
+local helpers = fonts.helpers
+local hashes = fonts.hashes
+local currentfont = font.current
+local definefont = font.define
+
+local getprivateslot = helpers.getprivateslot
+
+local cleanname = names.cleanname
+
+local encodings = fonts.encodings
+----- aglunicodes = encodings.agl.unicodes
+local aglunicodes = nil -- delayed loading
+
+local nuts = nodes.nuts
+local tonut = nuts.tonut
+
+local nextchar = nuts.traversers.char
+
+local getattr = nuts.getattr
+local setattr = nuts.setattr
+local getstate = nuts.getstate
+local setsubtype = nuts.setsubtype
+
+local texgetdimen = tex.getdimen
+local texsetcount = tex.setcount
+local texget = tex.get
+
+local texdefinefont = tex.definefont
+local texsp = tex.sp
+
+local fontdata = hashes.identifiers
+local characters = hashes.characters
+local descriptions = hashes.descriptions
+local properties = hashes.properties
+local resources = hashes.resources
+local unicodes = hashes.unicodes
+local csnames = hashes.csnames
+local lastmathids = hashes.lastmathids
+local exheights = hashes.exheights
+local emwidths = hashes.emwidths
+local parameters = hashes.parameters
+
+local designsizefilename = fontgoodies.designsizes.filename
+
+local ctx_char = context.char
+local ctx_safechar = context.safechar
+local ctx_getvalue = context.getvalue
+
+local otffeatures = otf.features
+local otftables = otf.tables
+
+local registerotffeature = otffeatures.register
+
+local sequencers = utilities.sequencers
+local appendgroup = sequencers.appendgroup
+local appendaction = sequencers.appendaction
+
+specifiers.contextsetups = specifiers.contextsetups or { }
+specifiers.contextnumbers = specifiers.contextnumbers or { }
+specifiers.contextmerged = specifiers.contextmerged or { }
+specifiers.synonyms = specifiers.synonyms or { }
+
+local setups = specifiers.contextsetups
+local numbers = specifiers.contextnumbers
+local merged = specifiers.contextmerged
+local synonyms = specifiers.synonyms
+
+storage.register("fonts/setups" , setups , "fonts.specifiers.contextsetups" )
+storage.register("fonts/numbers", numbers, "fonts.specifiers.contextnumbers")
+storage.register("fonts/merged", merged, "fonts.specifiers.contextmerged")
+storage.register("fonts/synonyms", synonyms, "fonts.specifiers.synonyms")
+
+-- inspect(setups)
+
+setmetatableindex(setups, environment.initex and
+ function(t,k)
+ return type(k) == "number" and rawget(t,numbers[k]) or nil
+ end
+or
+ function(t,k)
+ local v = type(k) == "number" and rawget(t,numbers[k])
+ if v then
+ t[k] = v
+ return v
+ end
+ end
+)
+
+-- this will move elsewhere ...
+
+local function getfontname(tfmdata)
+ return basename(type(tfmdata) == "number" and properties[tfmdata].name or tfmdata.properties.name)
+end
+
+helpers.name = getfontname
+
+local addformatter = utilities.strings.formatters.add
+
+addformatter(formatters,"font:name", [["'"..fontname(%s).."'"]], { fontname = helpers.name })
+addformatter(formatters,"font:features",[["'"..sequenced(%s," ",true).."'"]],{ sequenced = table.sequenced })
+
+-- ... like font-sfm or so
+
+constructors.resolvevirtualtoo = true -- context specific (due to resolver)
+constructors.sharefonts = true -- experimental
+constructors.nofsharedfonts = 0
+constructors.nofsharedhashes = 0
+constructors.nofsharedvectors = 0
+constructors.noffontsloaded = 0
+
+-- we can get rid of the tfm instance when we have fast access to the
+-- scaled character dimensions at the tex end, e.g. a fontobject.width
+-- actually we already have some of that now as virtual keys in glyphs
+--
+-- flushing the kern and ligature tables from memory saves a lot (only
+-- base mode) but it complicates vf building where the new characters
+-- demand this data .. solution: functions that access them
+
+-- font.getcopy = font.getfont -- we always want the table that context uses
+
+do
+
+ -- Does this still make sense?
+
+ local shares = { }
+ local hashes = { }
+ local nofinstances = 0
+ local instances = setmetatableindex(function(t,k)
+ nofinstances = nofinstances + 1
+ t[k] = nofinstances
+ return nofinstances
+ end)
+
+ function constructors.trytosharefont(target,tfmdata)
+ constructors.noffontsloaded = constructors.noffontsloaded + 1
+ if constructors.sharefonts then
+ local fonthash = target.specification.hash
+ if fonthash then
+ local properties = target.properties
+ local fullname = target.fullname
+ local fontname = target.fontname
+ local psname = target.psname
+ -- for the moment here:
+ local instance = properties.instance
+ if instance then
+ local format = tfmdata.properties.format
+ if format == "opentype" then
+ target.streamprovider = 1
+ elseif format == "truetype" then
+ target.streamprovider = 2
+ else
+ target.streamprovider = 0
+ end
+ if target.streamprovider > 0 then
+ if fullname then
+ fullname = fullname .. ":" .. instances[instance]
+ target.fullname = fullname
+ end
+ if fontname then
+ fontname = fontname .. ":" .. instances[instance]
+ target.fontname = fontname
+ end
+ if psname then
+ -- this one is used for the funny prefix in font names in pdf
+ -- so it has to be kind of unique in order to avoid subset prefix
+ -- clashes being reported
+ psname = psname .. ":" .. instances[instance]
+ target.psname = psname
+ end
+ end
+ end
+ --
+ local sharedname = hashes[fonthash]
+ if sharedname then
+ -- this is ok for context as we know that only features can mess with font definitions
+ -- so a similar hash means that the fonts are similar too
+ if trace_defining then
+ report_defining("font %a uses backend resources of font %a (%s)",target.fullname,sharedname,"common hash")
+ end
+ target.fullname = sharedname
+ properties.sharedwith = sharedname
+ constructors.nofsharedfonts = constructors.nofsharedfonts + 1
+ constructors.nofsharedhashes = constructors.nofsharedhashes + 1
+ else
+ -- the one takes more time (in the worst case of many cjk fonts) but it also saves
+ -- embedding time .. haha, this is interesting: when i got a clash on subset tag
+ -- collision i saw in the source that these tags are also using a hash like below
+ -- so maybe we should have an option to pass it from lua
+ local characters = target.characters
+ local n = 1
+ local t = { target.psname }
+ -- for the moment here:
+ if instance then
+ n = n + 1
+ t[n] = instance
+ end
+ --
+ local u = sortedkeys(characters)
+ for i=1,#u do
+ local k = u[i]
+ n = n + 1 ; t[n] = k
+ n = n + 1 ; t[n] = characters[k].index or k
+ end
+ local checksum = md5.HEX(concat(t," "))
+ local sharedname = shares[checksum]
+ local fullname = target.fullname
+ if sharedname then
+ if trace_defining then
+ report_defining("font %a uses backend resources of font %a (%s)",fullname,sharedname,"common vector")
+ end
+ fullname = sharedname
+ properties.sharedwith = sharedname
+ constructors.nofsharedfonts = constructors.nofsharedfonts + 1
+ constructors.nofsharedvectors = constructors.nofsharedvectors + 1
+ else
+ shares[checksum] = fullname
+ end
+ target.fullname = fullname
+ hashes[fonthash] = fullname
+ end
+ end
+ end
+ end
+
+end
+
+directives.register("fonts.checksharing",function(v)
+ if not v then
+ report_defining("font sharing in backend is disabled")
+ end
+ constructors.sharefonts = v
+end)
+
+function definers.resetnullfont()
+ -- resetting is needed because tikz misuses nullfont
+ local parameters = fonts.nulldata.parameters
+ --
+ parameters.slant = 0 -- 1
+ parameters.space = 0 -- 2
+ parameters.space_stretch = 0 -- 3
+ parameters.space_shrink = 0 -- 4
+ parameters.x_height = 0 -- 5
+ parameters.quad = 0 -- 6
+ parameters.extra_space = 0 -- 7
+ parameters.designsize = 655360
+ --
+ constructors.enhanceparameters(parameters) -- official copies for us
+ --
+ definers.resetnullfont = function() end
+end
+
+-- This is some initialization code and we don't want (tracing) clutter in lmtx
+-- which is why we follow a different route there. I admit that is sounds freaky.
+-- Watch out: in lmtx the font dimen array is no longer resized automatically.
+
+implement {
+ name = "resetnullfont",
+ onlyonce = true,
+ permanent = false,
+ actions = function()
+ for i=1,7 do
+ font.setfontdimen(0,i,0)
+ end
+ definers.resetnullfont()
+ end
+}
+
+-- this cannot be a feature initializer as there is no auto namespace
+-- so we never enter the loop then; we can store the defaults in the tma
+-- file (features.gpos.mkmk = 1 etc)
+
+local needsnodemode = { -- we will have node mode by default anyway
+ -- gsub_single = true,
+ gsub_multiple = true,
+ -- gsub_alternate = true,
+ -- gsub_ligature = true,
+ gsub_context = true,
+ gsub_contextchain = true,
+ gsub_reversecontextchain = true,
+ -- chainsub = true,
+ -- reversesub = true,
+ gpos_mark2base = true,
+ gpos_mark2ligature = true,
+ gpos_mark2mark = true,
+ gpos_cursive = true,
+ -- gpos_single = true,
+ -- gpos_pair = true,
+ gpos_context = true,
+ gpos_contextchain = true,
+}
+
+otftables.scripts.auto = "automatic fallback to latn when no dflt present"
+
+-- setmetatableindex(otffeatures.descriptions,otftables.features)
+
+local function checkedscript(tfmdata,resources,features)
+ local latn = false
+ local script = false
+ if resources.features then
+ for g, list in next, resources.features do
+ for f, scripts in next, list do
+ if scripts.dflt then
+ script = "dflt"
+ break
+ elseif scripts.latn then
+ latn = true
+ end
+ end
+ end
+ end
+ if not script then
+ script = latn and "latn" or "dflt"
+ end
+ if trace_automode then
+ report_defining("auto script mode, using script %a in font %!font:name!",script,tfmdata)
+ end
+ features.script = script
+ return script
+end
+
+-- basemode combined with dynamics is somewhat tricky
+
+local function checkedmode(tfmdata,resources,features)
+ local sequences = resources.sequences
+ if sequences and #sequences > 0 then
+ local script = features.script or "dflt"
+ local language = features.language or "dflt"
+ for feature, value in next, features do
+ if value then
+ local found = false
+ for i=1,#sequences do
+ local sequence = sequences[i]
+ local features = sequence.features
+ if features then
+ local scripts = features[feature]
+ if scripts then
+ local languages = scripts[script]
+ if languages and languages[language] then
+ if found then
+ -- more than one lookup
+ if trace_automode then
+ report_defining("forcing mode %a, font %!font:name!, feature %a, script %a, language %a, %s",
+ "node",tfmdata,feature,script,language,"multiple lookups")
+ end
+ features.mode = "node"
+ return "node"
+ elseif needsnodemode[sequence.type] then
+ if trace_automode then
+ report_defining("forcing mode %a, font %!font:name!, feature %a, script %a, language %a, %s",
+ "node",tfmdata,feature,script,language,"no base support")
+ end
+ features.mode = "node"
+ return "node"
+ else
+ -- at least one lookup
+ found = true
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ if trace_automode then
+ report_defining("forcing mode base, font %!font:name!",tfmdata)
+ end
+ features.mode = "base" -- new, or is this wrong?
+ return "base"
+end
+
+definers.checkedscript = checkedscript
+definers.checkedmode = checkedmode
+
+-- We only set the checker and leave other settings of the mode feature as
+-- they are.
+
+local function modechecker(tfmdata,features,mode) -- we cannot adapt features as they are shared!
+ if trace_features then
+ report_features("fontname %!font:name!, features %!font:features!",tfmdata,features)
+ end
+ local rawdata = tfmdata.shared.rawdata
+ local resources = rawdata and rawdata.resources
+ local script = features.script
+ if resources then
+ if script == "auto" then
+ script = checkedscript(tfmdata,resources,features)
+ end
+ if mode == "auto" then
+ mode = checkedmode(tfmdata,resources,features)
+ end
+ else
+ report_features("missing resources for font %!font:name!",tfmdata)
+ end
+ return mode
+end
+
+registerotffeature {
+ name = "mode",
+ modechecker = modechecker,
+}
+
+do
+
+ -- copying will end up here too
+
+ local beforecopyingcharacters = sequencers.new {
+ name = "beforecopyingcharacters",
+ arguments = "target,original",
+ }
+
+ appendgroup(beforecopyingcharacters,"before") -- user
+ appendgroup(beforecopyingcharacters,"system") -- private
+ appendgroup(beforecopyingcharacters,"after" ) -- user
+
+ function constructors.beforecopyingcharacters(original,target)
+ local runner = beforecopyingcharacters.runner
+ if runner then
+ runner(original,target)
+ end
+ end
+
+ local aftercopyingcharacters = sequencers.new {
+ name = "aftercopyingcharacters",
+ arguments = "target,original",
+ }
+
+ appendgroup(aftercopyingcharacters,"before") -- user
+ appendgroup(aftercopyingcharacters,"system") -- private
+ appendgroup(aftercopyingcharacters,"after" ) -- user
+
+ function constructors.aftercopyingcharacters(original,target)
+ local runner = aftercopyingcharacters.runner
+ if runner then
+ runner(original,target)
+ end
+ end
+
+end
+
+--[[ldx--
+<p>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:</p>
+situations:</p>
+
+<code>
+name:xetex like specs
+name@virtual font spec
+name*context specification
+</code>
+--ldx]]--
+
+-- Currently fonts are scaled while constructing the font, so we have to do scaling
+-- of commands in the vf at that point using e.g. "local scale = g.parameters.factor
+-- or 1" after all, we need to work with copies anyway and scaling needs to be done
+-- at some point; however, when virtual tricks are used as feature (makes more
+-- sense) we scale the commands in fonts.constructors.scale (and set the factor
+-- there).
+
+local loadfont = definers.loadfont
+
+function definers.loadfont(specification,size,id) -- overloads the one in font-def
+ local variants = definers.methods.variants
+ local virtualfeatures = specification.features.virtual
+ if virtualfeatures and virtualfeatures.preset then
+ local variant = variants[virtualfeatures.preset]
+ if variant then
+ return variant(specification,size,id)
+ end
+ else
+ return loadfont(specification,size,id)
+ end
+end
+
+local function predefined(specification)
+ local variants = definers.methods.variants
+ local detail = specification.detail
+ if detail ~= "" and variants[detail] then
+ specification.features.virtual = { preset = detail }
+ end
+ return specification
+end
+
+definers.registersplit("@", predefined,"virtual")
+
+local normalize_features = otffeatures.normalize -- should be general
+
+local function definecontext(name,t) -- can be shared
+ local number = setups[name] and setups[name].number or 0 -- hm, numbers[name]
+ if number == 0 then
+ number = #numbers + 1
+ numbers[number] = name
+ end
+ t.number = number
+ setups[name] = t
+ return number, t
+end
+
+-- {a,b,c} as table (so we don' need to parse again when it gets applied)
+-- we will update this ... when we have foo={a,b,c} then we can keep the table
+
+-- \definefontfeature[demo][a={b,c}]
+-- \definefontfeature[demo][a={b=12,c={34,35}}]
+
+local h = setmetatableindex(function(t,k)
+ local v = "," .. k .. ","
+ t[k] = v
+ return v
+end)
+
+-- local function removefromhash(hash,key)
+-- local pattern = h[key]
+-- for k in next, hash do
+-- if k ~= key and find(h[k],pattern) then -- if find(k,",") and ...
+-- hash[k] = nil
+-- end
+-- end
+-- end
+
+local function presetcontext(name,parent,features) -- will go to con and shared
+ if features == "" and find(parent,"=",1,true) then
+ features = parent
+ parent = ""
+ end
+ if not features or features == "" then
+ features = { }
+ elseif type(features) == "string" then
+ features = normalize_features(settings_to_hash(features))
+ -- if type(value) == "string" and find(value,"[=:]") then
+ -- local t = settings_to_hash_colon_too(value) -- clashes with foo=file:bar
+ for key, value in next, features do
+ if type(value) == "string" and find(value,"[=]") then
+ local t = settings_to_hash(value)
+ if next(t) then
+ features[key] = sequenced(normalize_features(t,true),",")
+ end
+ end
+ end
+ else
+ features = normalize_features(features)
+ end
+ -- 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
+ -- no, as then we cannot overload: e.g. math,mathextra
+ -- reverted, so we only take from parent when not set
+ if features[k] == nil then
+ features[k] = v
+ end
+ end
+ else
+ -- just ignore an undefined one .. i.e. we can refer to not yet defined
+ end
+ end
+ end
+ -- these are auto set so in order to prevent redundant definitions
+ -- we need to preset them (we hash the features and adding a default
+ -- setting during initialization may result in a different hash)
+ --
+ -- for k,v in next, triggers do
+ -- if features[v] == nil then -- not false !
+ -- local vv = default_features[v]
+ -- if vv then features[v] = vv end
+ -- end
+ -- end
+ --
+ for feature,value in next, features do
+ if value == nil then -- not false !
+ local default = default_features[feature]
+ if default ~= nil then
+ features[feature] = default
+ 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
+ t[k] = v
+ end
+ -- the number is needed for dynamic features; maybe number should always be
+ -- renewed as we can redefine features ... i need a test
+ local number = setups[name] and setups[name].number or 0
+ if number == 0 then
+ number = #numbers + 1
+ numbers[number] = name
+ end
+ --
+ t.number = number
+ -- there is the special case of combined features as we have in math but maybe
+ -- this has to change some day ... otherwise we mess up dynamics (ok, we could
+ -- impose a limit there: no combined features)
+ --
+ -- done elsewhere (!)
+ --
+ -- removefromhash(setups,name) -- can have changed (like foo,extramath)
+ --
+ setups[name] = t
+ return number, t
+end
+
+local function adaptcontext(pattern,features)
+ local pattern = topattern(pattern,false,true)
+ for name in next, setups do
+ if find(name,pattern) then
+ presetcontext(name,name,features)
+ end
+ end
+end
+
+-- See for old code in the lua file. We now share code.
+
+local function contextnumber(name)
+ local t = setups[name]
+ return t and t.number or 0
+end
+
+local function mergecontext(currentnumber,extraname,option) -- number string number (used in scrp-ini
+ local extra = setups[extraname]
+ if extra then
+ local current = setups[numbers[currentnumber]]
+ local mergedfeatures = { }
+ local 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
+ else
+ return currentnumber
+ end
+end
+
+local extrasets = { }
+
+setmetatableindex(extrasets,function(t,k)
+ local v = mergehashes(setups,k)
+ t[k] = v
+ return v
+end)
+
+local function mergecontextfeatures(currentname,extraname,how,mergedname) -- string string
+ local extra = setups[extraname] or extrasets[extraname]
+ if extra then
+ local current = setups[currentname]
+ local mergedfeatures = { }
+ if how == "+" then
+ if current then
+ for k, v in next, current do
+ mergedfeatures[k] = v
+ end
+ end
+ for k, v in next, extra do
+ mergedfeatures[k] = v
+ end
+ if trace_merge then
+ report_features("merge %a, method %a, current %|T, extra %|T, result %|T",mergedname,"add",current or { },extra,mergedfeatures)
+ end
+ elseif how == "-" then
+ if current then
+ for k, v in next, current do
+ mergedfeatures[k] = v
+ end
+ end
+ for k, v in next, extra do
+ -- only boolean features
+ if v == true then
+ mergedfeatures[k] = false
+ end
+ end
+ if trace_merge then
+ report_features("merge %a, method %a, current %|T, extra %|T, result %|T",mergedname,"subtract",current or { },extra,mergedfeatures)
+ end
+ else -- =
+ for k, v in next, extra do
+ mergedfeatures[k] = v
+ end
+ if trace_merge then
+ report_features("merge %a, method %a, result %|T",mergedname,"replace",mergedfeatures)
+ end
+ end
+ local number = #numbers + 1
+ mergedfeatures.number = number
+ numbers[number] = mergedname
+ merged[number] = option
+ setups[mergedname] = mergedfeatures
+ return number
+ else
+ return numbers[currentname] or 0
+ end
+end
+
+local function registercontext(fontnumber,extraname,option)
+ local extra = setups[extraname]
+ if extra then
+ local mergedfeatures = { }
+ local 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
+ else
+ return 0
+ end
+end
+
+local function registercontextfeature(mergedname,extraname,how)
+ local extra = setups[extraname]
+ if extra then
+ local mergedfeatures = { }
+ for k, v in next, extra do
+ mergedfeatures[k] = v
+ end
+ local number = #numbers + 1 -- we somehow end up with steps of 2
+ mergedfeatures.number = number
+ numbers[number] = mergedname
+ merged[number] = how == "=" and 1 or 2 -- 1=replace, 2=combine
+ setups[mergedname] = mergedfeatures
+ return number
+ else
+ report_features("unknown feature %a cannot be merged into %a using method %a",extraname,mergedname,how)
+ return 0
+ end
+end
+
+specifiers.presetcontext = presetcontext
+specifiers.contextnumber = contextnumber
+specifiers.mergecontext = mergecontext
+specifiers.registercontext = registercontext
+specifiers.definecontext = definecontext
+
+constructors.hashmethods.normal = function(list)
+ local s = { }
+ local n = 0
+ for k, v in next, list do
+ if not k then
+ -- no need to add to hash
+ elseif k == "number" or k == "features" then
+ -- no need to add to hash (maybe we need a skip list)
+ else
+ n = n + 1
+ if type(v) == "table" then
+ -- table.sequenced
+ local t = { }
+ local m = 0
+ for k, v in next, v do
+ m = m + 1
+ t[m] = format("%q=%q",k,v)
+ end
+ sort(t)
+ s[n] = format("%q={%s}",k,concat(t,","))
+ else
+ s[n] = format("%q=%q",k,v)
+ end
+ end
+ end
+ if n > 0 then
+ sort(s)
+ return concat(s,"+")
+ end
+end
+
+constructors.hashmethods.virtual = function(list)
+ local s = { }
+ local n = 0
+ for k, v in next, list do
+ n = n + 1
+ s[n] = format("%q=%q",k,v)
+ end
+ if n > 0 then
+ sort(s)
+ return concat(s,"+")
+ end
+end
+
+-- end of redefine
+
+-- local withcache = { } -- concat might be less efficient than nested tables
+--
+-- local function withset(name,what)
+-- local zero = texgetattribute(0)
+-- local hash = zero .. "+" .. name .. "*" .. what
+-- local done = withcache[hash]
+-- if not done then
+-- done = mergecontext(zero,name,what)
+-- withcache[hash] = done
+-- end
+-- texsetattribute(0,done)
+-- end
+--
+-- local function withfnt(name,what,font)
+-- local font = font or currentfont()
+-- local hash = font .. "*" .. name .. "*" .. what
+-- local done = withcache[hash]
+-- if not done then
+-- done = registercontext(font,name,what)
+-- withcache[hash] = done
+-- end
+-- texsetattribute(0,done)
+-- end
+
+function specifiers.showcontext(name)
+ return setups[name] or setups[numbers[name]] or setups[numbers[tonumber(name)]] or { }
+end
+
+-- we need a copy as we will add (fontclass) goodies to the features and
+-- that is bad for a shared table
+
+-- local function splitcontext(features) -- presetcontext creates dummy here
+-- return fastcopy(setups[features] or (presetcontext(features,"","") and setups[features]))
+-- end
+
+-- local function splitcontext(features) -- presetcontext creates dummy here
+-- local sf = setups[features]
+-- if not sf then
+-- local n -- number
+-- if find(features,",") then
+-- -- let's assume a combination which is not yet defined but just specified (as in math)
+-- n, sf = presetcontext(features,features,"")
+-- else
+-- -- we've run into an unknown feature and or a direct spec so we create a dummy
+-- n, sf = presetcontext(features,"","")
+-- end
+-- end
+-- return fastcopy(sf)
+-- end
+
+local function splitcontext(features) -- presetcontext creates dummy here
+ local n, sf
+ -- We can have: "a=yes,b=yes" "a,b" "a" "a=yes" etc.
+ if find(features,"[,=]") then
+ --
+ -- from elsewhere (!)
+ --
+ -- this will become:
+ --
+ -- if find(features,"^reset," then
+ setups[features] = nil
+ -- end
+ -- let's assume a combination which is not yet defined but just specified (as in math)
+ n, sf = presetcontext(features,features,"")
+ else
+ sf = setups[features]
+ if not sf then
+ -- we've run into an unknown feature and or a direct spec so we create a dummy
+ n, sf = presetcontext(features,"","")
+ end
+ end
+ return fastcopy(sf)
+end
+
+-- local splitter = lpeg.splitat("=")
+--
+-- local function splitcontext(features)
+-- local setup = setups[features]
+-- if setup then
+-- return setup
+-- elseif find(features,",",1,true) then
+-- -- This is not that efficient but handy anyway for quick and dirty tests
+-- -- beware, due to the way of caching setups you can get the wrong results
+-- -- when components change. A safeguard is to nil the cache.
+-- local merge = nil
+-- for feature in gmatch(features,"[^, ]+") do
+-- if find(feature,"=",1,true) then
+-- local k, v = lpegmatch(splitter,feature)
+-- if k and v then
+-- if not merge then
+-- merge = { k = v }
+-- else
+-- merge[k] = v
+-- end
+-- end
+-- else
+-- local s = setups[feature]
+-- if not s then
+-- -- skip
+-- elseif not merge then
+-- merge = s
+-- else
+-- for k, v in next, s do
+-- merge[k] = v
+-- end
+-- end
+-- end
+-- end
+-- setup = merge and presetcontext(features,"",merge) and setups[features]
+-- -- actually we have to nil setups[features] in order to permit redefinitions
+-- setups[features] = nil
+-- end
+-- return setup or (presetcontext(features,"","") and setups[features]) -- creates dummy
+-- end
+
+specifiers.splitcontext = splitcontext
+
+function specifiers.contexttostring(name,kind,separator,yes,no,strict,omit) -- not used
+ return hash_to_string(
+ mergedtable(handlers[kind].features.defaults or {},setups[name] or {}),
+ separator, yes, no, strict, omit or { "number" }
+ )
+end
+
+local function starred(features) -- no longer fallbacks here
+ local detail = features.detail
+ if detail and detail ~= "" then
+ features.features.normal = splitcontext(detail)
+ else
+ features.features.normal = { }
+ end
+ return features
+end
+
+definers.registersplit('*',starred,"featureset")
+
+-- sort of xetex mode, but without [] and / as we have file: and name: etc
+
+local space = P(" ")
+local spaces = space^0
+local separator = S(";,")
+local equal = P("=")
+local sometext = C((1-equal-space-separator)^1)
+local truevalue = P("+") * spaces * sometext * Cc(true)
+local falsevalue = P("-") * spaces * sometext * Cc(false)
+local somevalue = sometext * spaces * Cc(true)
+local keyvalue = sometext * spaces * equal * spaces * sometext
+local pattern = Cf(Ct("") * (space + separator + Cg(falsevalue + truevalue + keyvalue + somevalue))^0, rawset)
+
+local function colonized(specification)
+ specification.features.normal = normalize_features(lpegmatch(pattern,specification.detail))
+ return specification
+end
+
+definers.registersplit(":",colonized,"direct")
+
+-- define (two steps)
+
+local sizepattern, splitpattern, specialscale do
+
+ ----- space = P(" ")
+ ----- spaces = space^0
+ local leftparent = (P"(")
+ local rightparent = (P")")
+ local leftbrace = (P"{")
+ local rightbrace = (P"}")
+ local withinparents = leftparent * (1-rightparent)^0 * rightparent
+ local withinbraces = leftbrace * (1-rightbrace )^0 * rightbrace
+ local value = C((withinparents + withinbraces + (1-space))^1)
+ local dimension = C((space/"" + P(1))^1)
+ local rest = C(P(1)^0)
+ local scale_none = Cc(0)
+ local scale_at = (P("at") +P("@")) * Cc(1) * spaces * dimension -- dimension
+ local scale_sa = P("sa") * Cc(2) * spaces * dimension -- number
+ local scale_mo = P("mo") * Cc(3) * spaces * dimension -- number
+ local scale_scaled = P("scaled") * Cc(4) * spaces * dimension -- number
+ local scale_ht = P("ht") * Cc(5) * spaces * dimension -- dimension
+ local scale_cp = P("cp") * Cc(6) * spaces * dimension -- dimension
+
+ specialscale = { [5] = "ht", [6] = "cp" }
+
+ sizepattern = spaces * (scale_at + scale_sa + scale_mo + scale_ht + scale_cp + scale_scaled + scale_none)
+ splitpattern = spaces * value * spaces * rest
+
+end
+
+function helpers.splitfontpattern(str)
+ local name, size = lpegmatch(splitpattern,str)
+ local kind, size = lpegmatch(sizepattern,size)
+ return name, kind, size
+end
+
+function helpers.fontpatternhassize(str)
+ local name, size = lpegmatch(splitpattern,str)
+ local kind, size = lpegmatch(sizepattern,size)
+ return size or false
+end
+
+local specification -- still needed as local ?
+
+local getspecification = definers.getspecification
+
+-- 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)
+
+local specifiers = { }
+
+do -- else too many locals
+
+ local starttiming = statistics.starttiming
+ local stoptiming = statistics.stoptiming
+
+ local setmacro = tokens.setters.macro
+
+ implement {
+ name = "definefont_one",
+ arguments = "string",
+ actions = function(str)
+ starttiming(fonts)
+ if trace_defining then
+ report_defining("memory usage before: %s",statistics.memused())
+ report_defining("start stage one: %s",str)
+ end
+ local fullname, size = lpegmatch(splitpattern,str)
+ local lookup, name, sub, method, detail = getspecification(fullname)
+ if not name then
+ report_defining("strange definition %a",str)
+ -- ctx_setdefaultfontname()
+ elseif name == "unknown" then
+ -- ctx_setdefaultfontname()
+ else
+ -- ctx_setsomefontname(name)
+ setmacro("somefontname",name,"global")
+ 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
+ texsetcount("scaledfontmode",mode)
+ -- ctx_setsomefontsize(size)
+ setmacro("somefontsize",size)
+ else
+ texsetcount("scaledfontmode",0)
+ -- ctx_setemptyfontsize()
+ end
+ elseif true then
+ -- so we don't need to check in tex
+ texsetcount("scaledfontmode",2)
+ -- ctx_setemptyfontsize()
+ else
+ texsetcount("scaledfontmode",0)
+ -- ctx_setemptyfontsize()
+ end
+ specification = definers.makespecification(str,lookup,name,sub,method,detail,size)
+-- specification.original = str
+ if trace_defining then
+ report_defining("stop stage one")
+ end
+ end
+ }
+
+ local function nice_cs(cs)
+ return (gsub(cs,".->", ""))
+ end
+
+ local n = 0
+ local busy = false
+ local combinefeatures = false
+
+ directives.register("fonts.features.combine",function(v)
+ combinefeatures = v
+ end)
+
+ implement {
+ name = "definefont_two",
+ arguments = {
+ "boolean", "string", "string", "integer", "integer", "string", "string", "string", "string",
+ "integer", "integer", "integer", "string", "string", "string", "string", "integer",
+ },
+ actions = function (
+ global, -- \ifx\fontclass\empty\s!false\else\s!true\fi
+ cs, -- {#csname}%
+ str, -- \somefontfile
+ size, -- \d_font_scaled_font_size
+ inheritancemode, -- \c_font_feature_inheritance_mode
+ classfeatures, -- \m_font_class_features
+ fontfeatures, -- \m_font_features
+ classfallbacks, -- \m_font_class_fallbacks
+ fontfallbacks, -- \m_font_fallbacks
+ mathsize, -- \fontface
+ textsize, -- \d_font_scaled_text_face
+ relativeid, -- \relativefontid
+ classgoodies, -- \m_font_class_goodies
+ goodies, -- \m_font_goodies
+ classdesignsize, -- \m_font_class_designsize
+ fontdesignsize, -- \m_font_designsize
+ scaledfontmode -- \scaledfontmode
+ )
+ if trace_defining then
+ report_defining("start stage two: %s, size %s, features %a & %a, mode %a",str,size,classfeatures,fontfeatures,inheritancemode)
+ end
+ -- name is now resolved and size is scaled cf sa/mo
+ local lookup, name, sub, method, detail = getspecification(str or "")
+ -- new (todo: inheritancemode)
+ local designsize = fontdesignsize ~= "" and fontdesignsize or classdesignsize or ""
+ local designname = designsizefilename(name,designsize,size)
+ if designname and designname ~= "" then
+ if trace_defining or trace_designsize then
+ report_defining("remapping name %a, specification %a, size %a, designsize %a",name,designsize,size,designname)
+ end
+ -- we don't catch detail here
+ local o_lookup, o_name, o_sub, o_method, o_detail = getspecification(designname)
+ if o_lookup and o_lookup ~= "" then lookup = o_lookup end
+ if o_method and o_method ~= "" then method = o_method end
+ if o_detail and o_detail ~= "" then detail = o_detail end
+ name = o_name
+ sub = o_sub
+ end
+ -- so far
+ -- some settings can have been 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
+ specification.goodies = goodies
+ specification.cs = cs
+ specification.global = global
+ specification.scalemode = scaledfontmode -- context specific
+ if detail and detail ~= "" then
+ specification.method = method or "*"
+ specification.detail = detail
+ elseif specification.detail and specification.detail ~= "" then
+ -- already set
+ elseif inheritancemode == 0 then
+ -- nothing
+ elseif inheritancemode == 1 then
+ -- fontonly
+ if fontfeatures and fontfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = fontfeatures
+ end
+ if fontfallbacks and fontfallbacks ~= "" then
+ specification.fallbacks = fontfallbacks
+ end
+ elseif inheritancemode == 2 then
+ -- classonly
+ if classfeatures and classfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = classfeatures
+ end
+ if classfallbacks and classfallbacks ~= "" then
+ specification.fallbacks = classfallbacks
+ end
+ elseif inheritancemode == 3 then
+ -- fontfirst
+ if combinefeatures then
+ if classfeatures and classfeatures ~= "" then
+ specification.method = "*"
+ if fontfeatures and fontfeatures ~= "" and fontfeatures ~= classfeatures then
+ specification.detail = classfeatures .. "," .. fontfeatures
+ else
+ specification.detail = classfeatures
+ end
+ elseif fontfeatures and fontfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = fontfeatures
+ end
+ else
+ if fontfeatures and fontfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = fontfeatures
+ elseif classfeatures and classfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = classfeatures
+ end
+ end
+ if fontfallbacks and fontfallbacks ~= "" then
+ specification.fallbacks = fontfallbacks
+ elseif classfallbacks and classfallbacks ~= "" then
+ specification.fallbacks = classfallbacks
+ end
+ elseif inheritancemode == 4 then
+ -- classfirst
+ if combinefeatures then
+ if fontfeatures and fontfeatures ~= "" then
+ specification.method = "*"
+ if classfeatures and classfeatures ~= "" and classfeatures ~= fontfeatures then
+ specification.detail = fontfeatures .. "," .. classfeatures
+ else
+ specification.detail = fontfeatures
+ end
+ elseif classfeatures and classfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = classfeatures
+ end
+ else
+ if classfeatures and classfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = classfeatures
+ elseif fontfeatures and fontfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = fontfeatures
+ end
+ end
+ if classfallbacks and classfallbacks ~= "" then
+ specification.fallbacks = classfallbacks
+ elseif fontfallbacks and fontfallbacks ~= "" then
+ specification.fallbacks = fontfallbacks
+ end
+ end
+ --
+ local tfmdata = definers.read(specification,size) -- id not yet known (size in spec?)
+ --
+ local lastfontid = 0
+ local tfmtype = type(tfmdata)
+ if tfmtype == "table" then
+ -- setting the extra characters will move elsewhere
+ local characters = tfmdata.characters
+ local parameters = tfmdata.parameters
+ local properties = tfmdata.properties
+ -- we use char0 as signal; cf the spec pdf can handle this (no char in slot)
+ characters[0] = nil
+ -- characters[0x00A0] = { width = parameters.space }
+ -- characters[0x2007] = { width = characters[0x0030] and characters[0x0030].width or parameters.space } -- figure
+ -- characters[0x2008] = { width = characters[0x002E] and characters[0x002E].width or parameters.space } -- period
+ --
+ local fallbacks = specification.fallbacks or ""
+ local mathsize = (mathsize == 1 or mathsize == 2 or mathsize == 3) and mathsize or nil -- can be unset so we test 1 2 3
+ if fallbacks ~= "" and mathsize and not busy then
+ busy = true
+ -- We need this ugly hack in order to resolve fontnames (at the \TEX end). Originally
+ -- math was done in Lua after loading (plugged into aftercopying).
+ --
+ -- After tl 2017 I'll also do text fallbacks this way (although backups there are done
+ -- in a completely different way.)
+ if trace_defining then
+ report_defining("defining %a, id %a, target %a, features %a / %a, fallbacks %a / %a, step %a",
+ name,id,nice_cs(cs),classfeatures,fontfeatures,classfallbacks,fontfallbacks,1)
+ end
+ mathematics.resolvefallbacks(tfmdata,specification,fallbacks)
+ context(function()
+ busy = false
+ mathematics.finishfallbacks(tfmdata,specification,fallbacks)
+tfmdata.original = specification.specification
+ local id = definefont(tfmdata)
+ csnames[id] = specification.cs
+ properties.id = id
+ definers.register(tfmdata,id) -- to be sure, normally already done
+ texdefinefont(global,cs,id)
+ constructors.finalize(tfmdata)
+ if trace_defining then
+ report_defining("defining %a, id %a, target %a, features %a / %a, fallbacks %a / %a, step %a",
+ name,id,nice_cs(cs),classfeatures,fontfeatures,classfallbacks,fontfallbacks,2)
+ end
+ -- resolved (when designsize is used):
+ local size = round(tfmdata.parameters.size or 655360)
+ setmacro("somefontsize",size.."sp")
+ -- ctx_setsomefontsize(size .. "sp")
+ texsetcount("scaledfontsize",size)
+ lastfontid = id
+ --
+ if trace_defining then
+ report_defining("memory usage after: %s",statistics.memused())
+ report_defining("stop stage two")
+ end
+ --
+ texsetcount("global","lastfontid",lastfontid)
+ specifiers[lastfontid] = { str, size }
+ if not mathsize then
+ -- forget about it (can't happen here)
+ elseif mathsize == 0 then
+ -- can't happen (here)
+ else
+ -- maybe only 1 2 3 (we already test for this)
+ lastmathids[mathsize] = lastfontid
+ end
+ stoptiming(fonts)
+ end)
+ return
+ else
+tfmdata.original = specification.specification
+ local id = definefont(tfmdata)
+ csnames[id] = specification.cs
+ properties.id = id
+ definers.register(tfmdata,id) -- to be sure, normally already done
+ texdefinefont(global,cs,id)
+ constructors.finalize(tfmdata)
+ if trace_defining then
+ report_defining("defining %a, id %a, target %a, features %a / %a, fallbacks %a / %a, step %a",
+ name,id,nice_cs(cs),classfeatures,fontfeatures,classfallbacks,fontfallbacks,"-")
+ end
+ -- resolved (when designsize is used):
+ local size = round(tfmdata.parameters.size or 655360)
+ setmacro("somefontsize",size.."sp")
+ -- ctx_setsomefontsize(size .. "sp")
+ texsetcount("scaledfontsize",size)
+ lastfontid = id
+ end
+ elseif tfmtype == "number" then
+ if trace_defining then
+ report_defining("reusing %s, id %a, target %a, features %a / %a, fallbacks %a / %a, goodies %a / %a, designsize %a / %a",
+ name,tfmdata,nice_cs(cs),classfeatures,fontfeatures,classfallbacks,fontfallbacks,classgoodies,goodies,classdesignsize,fontdesignsize)
+ end
+ csnames[tfmdata] = specification.cs
+ texdefinefont(global,cs,tfmdata)
+ -- resolved (when designsize is used):
+ local size = round(fontdata[tfmdata].parameters.size or 0)
+ -- ctx_setsomefontsize(size .. "sp")
+ setmacro("somefontsize",size.."sp")
+ texsetcount("scaledfontsize",size)
+ lastfontid = tfmdata
+ else
+ report_defining("unable to define %a as %a",name,nice_cs(cs))
+ lastfontid = -1
+ texsetcount("scaledfontsize",0)
+ -- ctx_letvaluerelax(cs) -- otherwise the current definition takes the previous one
+ end
+ if trace_defining then
+ report_defining("memory usage after: %s",statistics.memused())
+ report_defining("stop stage two")
+ end
+ --
+ texsetcount("global","lastfontid",lastfontid)
+ specifiers[lastfontid] = { str, size }
+ if not mathsize then
+ -- forget about it
+ elseif mathsize == 0 then
+ -- can't happen (here)
+ else
+ -- maybe only 1 2 3
+ lastmathids[mathsize] = lastfontid
+ end
+ --
+ stoptiming(fonts)
+ end
+ }
+
+ implement {
+ name = "specifiedfontspec",
+ arguments = "integer",
+ actions = function(id)
+ local f = specifiers[id]
+ if f then
+ context(f[1])
+ end
+ end
+ }
+
+ implement {
+ name = "specifiedfontsize",
+ arguments = "integer",
+ actions = function(id)
+ local f = specifiers[id]
+ if f then
+ context(f[2])
+ end
+ end
+ }
+
+ implement {
+ name = "specifiedfont",
+ arguments = { "integer", "number" },
+ actions = function(id,size)
+ local f = specifiers[id]
+ if f and size then
+ context("%s at %0.2p",f[1],size * f[2]) -- we round to 2 decimals (as at the tex end)
+ end
+ end
+ }
+ --
+
+ local function define(specification)
+ --
+ local name = specification.name
+ if not name or name == "" then
+ return -1
+ else
+ starttiming(fonts)
+ --
+ -- following calls expect a few properties to be set:
+ --
+ local lookup, name, sub, method, detail = getspecification(name or "")
+ --
+ specification.name = (name ~= "" and name) or specification.name
+ --
+ specification.lookup = specification.lookup or (lookup ~= "" and lookup) or "file"
+ specification.size = specification.size or 655260
+ specification.sub = specification.sub or (sub ~= "" and sub) or ""
+ specification.method = specification.method or (method ~= "" and method) or "*"
+ specification.detail = specification.detail or (detail ~= "" and detail) or ""
+ --
+ if type(specification.size) == "string" then
+ specification.size = texsp(specification.size) or 655260
+ end
+ --
+ specification.specification = "" -- not used
+ specification.resolved = ""
+ specification.forced = ""
+ specification.features = { } -- via detail, maybe some day
+ --
+ -- we don't care about mathsize textsize goodies fallbacks
+ --
+ local cs = specification.cs
+ if cs == "" then
+ cs = nil
+ specification.cs = nil
+ specification.global = false
+ elseif specification.global == nil then
+ specification.global = false
+ end
+ --
+ local tfmdata = definers.read(specification,specification.size)
+ if not tfmdata then
+ return -1, nil
+ elseif type(tfmdata) == "number" then
+ if cs then
+ texdefinefont(specification.global,cs,tfmdata)
+ csnames[tfmdata] = cs
+ end
+ stoptiming(fonts)
+ return tfmdata, fontdata[tfmdata]
+ else
+ local id = definefont(tfmdata)
+ tfmdata.properties.id = id
+ definers.register(tfmdata,id)
+ if cs then
+ texdefinefont(specification.global,cs,id)
+ csnames[id] = cs
+ end
+ constructors.finalize(tfmdata)
+ stoptiming(fonts)
+ return id, tfmdata
+ end
+ end
+ end
+
+ definers.define = define
+
+ -- local id, cs = fonts.definers.internal { }
+ -- local id, cs = fonts.definers.internal { number = 2 }
+ -- local id, cs = fonts.definers.internal { name = "dejavusans" }
+
+ local n = 0
+
+ function definers.internal(specification,cs)
+ specification = specification or { }
+ local name = specification.name
+ local size = tonumber(specification.size)
+ local number = tonumber(specification.number)
+ local id = nil
+ if not size then
+ size = texgetdimen("bodyfontsize")
+ end
+ if number then
+ id = number
+ elseif name and name ~= "" then
+ local cs = cs or specification.cs
+ if not cs then
+ n = n + 1 -- beware ... there can be many and they are often used once
+ -- cs = formatters["internal font %s"](n)
+ cs = "internal font " .. n
+ else
+ specification.cs = cs
+ end
+ id = define {
+ name = name,
+ size = size,
+ cs = cs,
+ }
+ end
+ if not id then
+ id = currentfont()
+ end
+ return id, csnames[id]
+ end
+
+ local function read(name,size)
+ return (define { name = name, size = size } or 0)
+ end
+
+ callbacks.register('define_font', read, "definition of fonts (tfmdata preparation)")
+
+ -- here
+
+ local infofont = 0
+
+ function fonts.infofont()
+ if infofont == 0 then
+ infofont = define { name = "dejavusansmono", size = texsp("6pt") }
+ end
+ return infofont
+ end
+
+ -- abstract interfacing
+
+ implement { name = "tf", actions = function() setmacro("fontalternative","tf") end }
+ implement { name = "bf", actions = function() setmacro("fontalternative","bf") end }
+ implement { name = "it", actions = function() setmacro("fontalternative","it") end }
+ implement { name = "sl", actions = function() setmacro("fontalternative","sl") end }
+ implement { name = "bi", actions = function() setmacro("fontalternative","bi") end }
+ implement { name = "bs", actions = function() setmacro("fontalternative","bs") end }
+
+end
+
+-- Not ok, we can best use a database for this. The problem is that we
+-- have delayed definitions and so we never know what style is taken
+-- as start.
+
+function constructors.calculatescale(tfmdata,scaledpoints,relativeid,specification)
+ local parameters = tfmdata.parameters
+ local units = parameters.units or 1000
+ if specification then
+ local scalemode = specification.scalemode
+ local special = scalemode and specialscale[scalemode]
+ if special == "ht" then
+ local height = parameters.ascender / units
+ scaledpoints = scaledpoints / height
+ elseif special == "cp" then
+ local glyph = tfmdata.descriptions[utfbyte("X")]
+ local height = (glyph and glyph.height or parameters.ascender) / units
+ scaledpoints = scaledpoints / height
+ end
+ end
+ if scaledpoints < 0 then
+ scaledpoints = (- scaledpoints/1000) * (tfmdata.designsize or parameters.designsize) -- already in sp
+ end
+ return round(scaledpoints), round(scaledpoints/units) -- delta
+end
+
+local designsizes = constructors.designsizes
+
+-- called quite often when in mp labels
+-- otf.normalizedaxis
+
+function constructors.hashinstance(specification,force)
+ local hash = specification.hash
+ local size = specification.size
+ local fallbacks = specification.fallbacks
+ if force or not hash then
+ hash = constructors.hashfeatures(specification)
+ specification.hash = hash
+ end
+ if size < 1000 and designsizes[hash] then
+ size = round(constructors.scaled(size,designsizes[hash]))
+ else
+ size = round(size)
+ end
+ specification.size = size
+ if fallbacks then
+ return hash .. ' @ ' .. size .. ' @ ' .. fallbacks
+ else
+ local scalemode = specification.scalemode
+ local special = scalemode and specialscale[scalemode]
+ if special then
+ return hash .. ' @ ' .. size .. ' @ ' .. special
+ else
+ return hash .. ' @ ' .. size
+ end
+ end
+end
+
+-- We overload the (generic) resolver:
+
+local resolvers = definers.resolvers
+local hashfeatures = constructors.hashfeatures
+
+function definers.resolve(specification) -- overload function in font-con.lua
+ if not specification.resolved or specification.resolved == "" then -- resolved itself not per se in mapping hash
+ local r = resolvers[specification.lookup]
+ if r then
+ r(specification)
+ end
+ end
+ if specification.forced == "" then
+ specification.forced = nil
+ else
+ specification.forced = specification.forced
+ end
+ -- goodies are a context specific thing and are not always defined
+ -- as feature, so we need to make sure we add them here before
+ -- hashing because otherwise we get funny goodies applied
+ local goodies = specification.goodies
+ if goodies and goodies ~= "" then
+ -- this adapts the features table so it has best be a copy
+ local normal = specification.features.normal
+ if not normal then
+ specification.features.normal = { goodies = goodies }
+ elseif not normal.goodies then
+ local g = normal.goodies
+ if g and g ~= "" then
+ normal.goodies = formatters["%s,%s"](g,goodies)
+ else
+ normal.goodies = goodies
+ end
+ end
+ end
+ -- so far for goodie hacks
+ local hash = hashfeatures(specification)
+ local name = specification.name or "badfont"
+ local sub = specification.sub
+ if sub and sub ~= "" then
+ specification.hash = lower(name .. " @ " .. sub .. ' @ ' .. hash)
+ else
+ specification.hash = lower(name .. " @ " .. ' @ ' .. hash)
+ end
+ --
+ return specification
+end
+
+-- we need an 'do after the banner hook'
+
+-- => commands
+
+local pattern = P("P")
+ * (lpeg.patterns.hexdigit^4 / function(s) return tonumber(s,16) end)
+ * P(-1)
+
+local function nametoslot(name) -- also supports PXXXXX (4+ positions)
+ local t = type(name)
+ if t == "string" then
+ local unic = unicodes[true]
+ local slot = unic[name]
+ if slot then
+ return slot
+ end
+ --
+ local slot = unic[gsub(name,"_"," ")] or unic[gsub(name,"_","-")] or
+ unic[gsub(name,"-"," ")] or unic[gsub(name,"-","_")] or
+ unic[gsub(name," ","_")] or unic[gsub(name," ","-")]
+ if slot then
+ return slot
+ end
+ --
+ if not aglunicodes then
+ aglunicodes = encodings.agl.unicodes
+ end
+ local char = characters[true]
+ local slot = aglunicodes[name]
+ if slot and char[slot] then
+ return slot
+ end
+ local slot = lpegmatch(pattern,name)
+ if slot and char[slot] then
+ return slot
+ end
+ -- not in font
+ elseif t == "number" then
+ if characters[true][name] then
+ return slot
+ else
+ -- not in font
+ end
+ end
+end
+
+local found = { }
+
+local function descriptiontoslot(name)
+ local t = type(name)
+ if t == "string" then
+ -- slow
+ local list = sortedkeys(chardata) -- can be a cache with weak tables
+ local slot = found[name]
+ local char = characters[true]
+ if slot then
+ return char[slot] and slot or nil
+ end
+ local NAME = upper(name)
+ for i=1,#list do
+ slot = list[i]
+ local c = chardata[slot]
+ local d = c.description
+ if d == NAME then
+ found[name] = slot
+ return char[slot] and slot or nil
+ end
+ end
+ for i=1,#list do
+ slot = list[i]
+ local c = chardata[slot]
+ local s = c.synonyms
+ if s then
+ for i=1,#s do
+ local si = s[i]
+ if si == name then
+ found[name] = si
+ return char[slot] and slot or nil
+ end
+ end
+ end
+ end
+ for i=1,#list do
+ slot = list[i]
+ local c = chardata[slot]
+ local d = c.description
+ if d and find(d,NAME) then
+ found[name] = slot
+ return char[slot] and slot or nil
+ end
+ end
+ for i=1,#list do
+ slot = list[i]
+ local c = chardata[slot]
+ local s = c.synonyms
+ if s then
+ for i=1,#s do
+ local si = s[i]
+ if find(s[i],name) then
+ found[name] = si
+ return char[slot] and slot or nil
+ end
+ end
+ end
+ end
+ -- not in font
+ elseif t == "number" then
+ if characters[true][name] then
+ return slot
+ else
+ -- not in font
+ end
+ end
+end
+
+local function indextoslot(font,index)
+ if not index then
+ index = font
+ font = true
+ end
+ local r = resources[font]
+ if r then
+ local indices = r.indices
+ if not indices then
+ indices = { }
+ local c = characters[font]
+ for unicode, data in next, c do
+ local di = data.index
+ if di then
+ indices[di] = unicode
+ end
+ end
+ r.indices = indices
+ end
+ return indices[tonumber(index)]
+ end
+end
+
+do -- else too many locals
+
+ local entities = characters.entities
+ local lowered = { } -- delayed initialization
+
+ setmetatableindex(lowered,function(t,k)
+ for k, v in next, entities do
+ local l = lower(k)
+ if not entities[l] then
+ lowered[l] = v
+ end
+ end
+ setmetatableindex(lowered,nil)
+ return lowered[k]
+ end)
+
+ local methods = {
+ -- entity
+ e = function(name)
+ return entities[name] or lowered[name] or name
+ end,
+ -- hexadecimal unicode
+ x = function(name)
+ local n = tonumber(name,16)
+ return n and utfchar(n) or name
+ end,
+ -- decimal unicode
+ d = function(name)
+ local n = tonumber(name)
+ return n and utfchar(n) or name
+ end,
+ -- hexadecimal index (slot)
+ s = function(name)
+ local n = tonumber(name,16)
+ local n = n and indextoslot(n)
+ return n and utfchar(n) or name
+ end,
+ -- decimal index
+ i = function(name)
+ local n = tonumber(name)
+ local n = n and indextoslot(n)
+ return n and utfchar(n) or name
+ end,
+ -- name
+ n = function(name)
+ local n = nametoslot(name)
+ return n and utfchar(n) or name
+ end,
+ -- unicode description (synonym)
+ u = function(name)
+ local n = descriptiontoslot(name,false)
+ return n and utfchar(n) or name
+ end,
+ -- all
+ a = function(name)
+ local n = nametoslot(name) or descriptiontoslot(name)
+ return n and utfchar(n) or name
+ end,
+ -- char
+ c = function(name)
+ return name
+ end,
+ }
+
+ -- -- nicer:
+ --
+ -- setmetatableindex(methods,function(t,k) return methods.c end)
+ --
+ -- local splitter = (C(1) * P(":") + Cc("c")) * C(P(1)^1) / function(method,name)
+ -- return methods[method](name)
+ -- end
+ --
+ -- -- more efficient:
+
+ local splitter = C(1) * P(":") * C(P(1)^1) / function(method,name)
+ local action = methods[method]
+ return action and action(name) or name
+ end
+
+ local function tochar(str)
+ local t = type(str)
+ if t == "number" then
+ return utfchar(str)
+ elseif t == "string" then
+ return lpegmatch(splitter,str) or str
+ else
+ return str
+ end
+ end
+
+ helpers.nametoslot = nametoslot
+ helpers.descriptiontoslot = descriptiontoslot
+ helpers.indextoslot = indextoslot
+ helpers.tochar = tochar
+
+ -- interfaces:
+
+ implement {
+ name = "fontchar",
+ actions = { nametoslot, ctx_char },
+ arguments = "string",
+ }
+
+ implement {
+ name = "fontcharbyindex",
+ actions = { indextoslot, ctx_char },
+ arguments = "integer",
+ }
+
+ implement {
+ name = "tochar",
+ actions = { tochar, ctx_safechar },
+ arguments = "string",
+ }
+
+end
+
+-- this will change ...
+
+function loggers.reportdefinedfonts()
+ if trace_usage then
+ local t, tn = { }, 0
+ for id, data in sortedhash(fontdata) do
+ local properties = data.properties or { }
+ local parameters = data.parameters or { }
+ tn = tn + 1
+ t[tn] = {
+ formatters["%03i"](id or 0),
+ formatters["%p" ](parameters.size or 0),
+ properties.format or "unknown",
+ properties.name or "",
+ properties.psname or "",
+ properties.fullname or "",
+ properties.sharedwith or "",
+ }
+ end
+ formatcolumns(t," ")
+ --
+ logs.startfilelogging(report,"defined fonts")
+ for k=1,tn do
+ report(t[k])
+ end
+ logs.stopfilelogging()
+ end
+end
+
+logs.registerfinalactions(loggers.reportdefinedfonts)
+
+function loggers.reportusedfeatures()
+ -- numbers, setups, merged
+ if trace_usage then
+ local t, n = { }, #numbers
+ for i=1,n do
+ local name = numbers[i]
+ local setup = setups[name]
+ local n = setup.number
+ setup.number = nil -- we have no reason to show this
+ t[i] = { i, name, sequenced(setup,false,true) } -- simple mode
+ setup.number = n -- restore it (normally not needed as we're done anyway)
+ end
+ formatcolumns(t," ")
+ logs.startfilelogging(report,"defined featuresets")
+ for k=1,n do
+ report(t[k])
+ end
+ logs.stopfilelogging()
+ end
+end
+
+logs.registerfinalactions(loggers.reportusedfeatures)
+
+-- maybe move this to font-log.lua:
+
+statistics.register("font engine", function()
+ local elapsed = statistics.elapsedseconds(fonts)
+ local nofshared = constructors.nofsharedfonts or 0
+ local nofloaded = constructors.noffontsloaded or 0
+ if nofshared > 0 then
+ return format("otf %0.3f, afm %0.3f, tfm %0.3f, %s instances, %s shared in backend, %s common vectors, %s common hashes, load time %s",
+ otf.version,afm.version,tfm.version,nofloaded,
+ nofshared,constructors.nofsharedvectors,constructors.nofsharedhashes,
+ elapsed)
+ elseif nofloaded > 0 and elapsed then
+ return format("otf %0.3f, afm %0.3f, tfm %0.3f, %s instances, load time %s",
+ otf.version,afm.version,tfm.version,nofloaded,
+ elapsed)
+ else
+ return format("otf %0.3f, afm %0.3f, tfm %0.3f",
+ otf.version,afm.version,tfm.version)
+ end
+end)
+
+-- experimental mechanism for Mojca:
+--
+-- fonts.definetypeface {
+-- name = "mainbodyfont-light",
+-- preset = "antykwapoltawskiego-light",
+-- }
+--
+-- fonts.definetypeface {
+-- name = "mojcasfavourite",
+-- preset = "antykwapoltawskiego",
+-- normalweight = "light",
+-- boldweight = "bold",
+-- width = "condensed",
+-- }
+
+local Shapes = {
+ serif = "Serif",
+ sans = "Sans",
+ mono = "Mono",
+}
+
+local ctx_startfontclass = context.startfontclass
+local ctx_stopfontclass = context.stopfontclass
+local ctx_definefontsynonym = context.definefontsynonym
+local ctx_dofastdefinetypeface = context.dofastdefinetypeface
+
+function fonts.definetypeface(name,t)
+ if type(name) == "table" then
+ -- {name=abc,k=v,...}
+ t = name
+ elseif t then
+ if type(t) == "string" then
+ -- "abc", "k=v,..."
+ t = settings_to_hash(name)
+ else
+ -- "abc", {k=v,...}
+ end
+ t.name = t.name or name
+ else
+ -- "name=abc,k=v,..."
+ t = settings_to_hash(name)
+ end
+ local p = t.preset and fonts.typefaces[t.preset] or { }
+ local name = t.name or "unknowntypeface"
+ local shortcut = t.shortcut or p.shortcut or "rm"
+ local size = t.size or p.size or "default"
+ local shape = t.shape or p.shape or "serif"
+ local fontname = t.fontname or p.fontname or "unknown"
+ local normalweight = t.normalweight or t.weight or p.normalweight or p.weight or "normal"
+ local boldweight = t.boldweight or t.weight or p.boldweight or p.weight or "normal"
+ local normalwidth = t.normalwidth or t.width or p.normalwidth or p.width or "normal"
+ local boldwidth = t.boldwidth or t.width or p.boldwidth or p.width or "normal"
+ Shape = Shapes[shape] or "Serif"
+ ctx_startfontclass { name }
+ ctx_definefontsynonym( { formatters["%s"] (Shape) }, { formatters["spec:%s-%s-regular-%s"] (fontname, normalweight, normalwidth) } )
+ ctx_definefontsynonym( { formatters["%sBold"] (Shape) }, { formatters["spec:%s-%s-regular-%s"] (fontname, boldweight, boldwidth ) } )
+ ctx_definefontsynonym( { formatters["%sBoldItalic"](Shape) }, { formatters["spec:%s-%s-italic-%s"] (fontname, boldweight, boldwidth ) } )
+ ctx_definefontsynonym( { formatters["%sItalic"] (Shape) }, { formatters["spec:%s-%s-italic-%s"] (fontname, normalweight, normalwidth) } )
+ ctx_stopfontclass()
+ local settings = sequenced({ features= t.features },",")
+ ctx_dofastdefinetypeface(name, shortcut, shape, size, settings)
+end
+
+implement {
+ name = "definetypeface",
+ actions = fonts.definetypeface,
+ arguments = "2 strings"
+}
+
+function fonts.current() -- todo: also handle name
+ return fontdata[currentfont()] or fontdata[0]
+end
+
+function fonts.currentid()
+ return currentfont() or 0
+end
+
+-- for the moment here, this will become a chain of extras that is
+-- hooked into the ctx registration (or scaler or ...)
+
+local dimenfactors = number.dimenfactors
+
+function helpers.dimenfactor(unit,id)
+ if unit == "ex" then
+ return id and exheights[id] or 282460 -- lm 10pt
+ elseif unit == "em" then
+ return id and emwidths [id] or 655360 -- lm 10pt
+ else
+ local du = dimenfactors[unit]
+ return du and 1/du or tonumber(unit) or 1
+ end
+end
+
+local function digitwidth(font) -- max(quad/2,wd(0..9))
+ local tfmdata = fontdata[font]
+ local parameters = tfmdata.parameters
+ local width = parameters.digitwidth
+ if not width then
+ width = round(parameters.quad/2) -- maybe tex.scale
+ local characters = tfmdata.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
+
+helpers.getdigitwidth = digitwidth
+helpers.setdigitwidth = digitwidth
+
+--
+
+function helpers.getparameters(tfmdata)
+ local p = { }
+ local m = p
+ local parameters = tfmdata.parameters
+ while true do
+ for k, v in next, parameters do
+ m[k] = v
+ end
+ parameters = getmetatable(parameters)
+ parameters = parameters and parameters.__index
+ if type(parameters) == "table" then
+ m = { }
+ p.metatable = m
+ else
+ break
+ end
+ end
+ return p
+end
+
+if environment.initex then
+
+ local function names(t)
+ local nt = #t
+ if nt > 0 then
+ local n = { }
+ for i=1,nt do
+ n[i] = t[i].name
+ end
+ return concat(n," ")
+ else
+ return "-"
+ end
+ end
+
+ statistics.register("font processing", function()
+ local l = { }
+ for what, handler in table.sortedpairs(handlers) do
+ local features = handler and handler.features
+ if features then
+ l[#l+1] = format("[%s (base initializers: %s) (base processors: %s) (base manipulators: %s) (node initializers: %s) (node processors: %s) (node manipulators: %s)]",
+ what,
+ names(features.initializers.base),
+ names(features.processors .base),
+ names(features.manipulators.base),
+ names(features.initializers.node),
+ names(features.processors .node),
+ names(features.manipulators.node)
+ )
+ end
+ end
+ return concat(l, " | ")
+ end)
+
+end
+
+-- redefinition
+
+-- local hashes = fonts.hashes
+-- local emwidths = hashes.emwidths
+-- local exheights = hashes.exheights
+
+setmetatableindex(dimenfactors, function(t,k)
+ if k == "ex" then
+ return 1/exheights[currentfont()]
+ elseif k == "em" then
+ return 1/emwidths[currentfont()]
+ elseif k == "pct" or k == "%" then
+ return 1/(texget("hsize")/100)
+ else
+ -- error("wrong dimension: " .. (s or "?")) -- better a message
+ return false
+ end
+end)
+
+dimenfactors.ex = nil
+dimenfactors.em = nil
+dimenfactors["%"] = nil
+dimenfactors.pct = nil
+
+--[[ldx--
+<p>Before a font is passed to <l n='tex'/> we scale it. Here we also need
+to scale virtual characters.</p>
+--ldx]]--
+
+do
+
+ -- can become luat-tex.lua
+
+ local texsetglyphdata = tex.setglyphdata
+ local texgetglyphdata = tex.getglyphdata
+
+ if not texsetglyphdata then
+
+ local texsetattribute = tex.setattribute
+ local texgetattribute = tex.getattribute
+
+ texsetglyphdata = function(n) return texsetattribute(0,n) end
+ texgetglyphdata = function() return texgetattribute(0) end
+
+ tex.setglyphdata = texsetglyphdata
+ tex.getglyphdata = texgetglyphdata
+
+ end
+
+ -- till here
+
+ local setmacro = tokens.setters.macro
+
+ function constructors.currentfonthasfeature(n)
+ local f = fontdata[currentfont()]
+ if not f then return end f = f.shared
+ if not f then return end f = f.rawdata
+ if not f then return end f = f.resources
+ if not f then return end f = f.features
+ return f and (f.gpos[n] or f.gsub[n])
+ end
+
+ local ctx_doifelse = commands.doifelse
+ local ctx_doif = commands.doif
+
+ implement {
+ name = "doifelsecurrentfonthasfeature",
+ actions = { constructors.currentfonthasfeature, ctx_doifelse },
+ arguments = "string"
+ }
+
+ local f_strip = formatters["%0.2fpt"] -- normally this value is changed only once
+ local stripper = lpeg.patterns.stripzeros
+
+ local cache = { }
+
+ local hows = {
+ ["+"] = "add",
+ ["-"] = "subtract",
+ ["="] = "replace",
+ }
+
+ local function setfeature(how,parent,name,font) -- 0/1 test temporary for testing
+ if not how or how == 0 then
+ if trace_features and texgetglyphdata() ~= 0 then
+ report_cummulative("font %!font:name!, reset",fontdata[font or true])
+ end
+ texsetglyphdata(0)
+ elseif how == true or how == 1 then
+ local hash = "feature > " .. parent
+ local done = cache[hash]
+ if trace_features and done then
+ report_cummulative("font %!font:name!, revive %a : %!font:features!",fontdata[font or true],parent,setups[numbers[done]])
+ end
+ texsetglyphdata(done or 0)
+ else
+ local full = parent .. how .. name
+ local hash = "feature > " .. full
+ local done = cache[hash]
+ if not done then
+ local n = setups[full]
+ if n then
+ -- already defined
+ else
+ n = mergecontextfeatures(parent,name,how,full)
+ end
+ done = registercontextfeature(hash,full,how)
+ cache[hash] = done
+ if trace_features then
+ report_cummulative("font %!font:name!, %s %a : %!font:features!",fontdata[font or true],hows[how],full,setups[numbers[done]])
+ end
+ end
+ texsetglyphdata(done)
+ end
+ end
+
+ local function resetfeature()
+ if trace_features and texgetglyphdata() ~= 0 then
+ report_cummulative("font %!font:name!, reset",fontdata[true])
+ end
+ texsetglyphdata(0)
+ end
+
+ local function setfontfeature(tag)
+ texsetglyphdata(contextnumber(tag))
+ end
+
+ local function resetfontfeature()
+ texsetglyphdata(0)
+ end
+
+ implement {
+ name = "nbfs",
+ arguments = "dimen",
+ actions = function(d)
+ context(lpegmatch(stripper,f_strip(d/65536)))
+ end
+ }
+
+ implement {
+ name = "featureattribute",
+ arguments = "string",
+ actions = { contextnumber, context }
+ }
+
+ implement {
+ name = "setfontfeature",
+ arguments = "string",
+ actions = setfontfeature,
+ }
+
+ implement {
+ name = "resetfontfeature",
+ -- arguments = { 0, 0 },
+ actions = resetfontfeature,
+ }
+
+ implement {
+ name = "setfontofid",
+ arguments = "integer",
+ actions = function(id)
+ ctx_getvalue(csnames[id])
+ end
+ }
+
+ implement {
+ name = "definefontfeature",
+ arguments = "3 strings",
+ actions = presetcontext,
+ }
+
+ implement {
+ name = "doifelsefontfeature",
+ arguments = "string",
+ actions = function(name) ctx_doifelse(contextnumber(name) > 1) end,
+ }
+
+ implement {
+ name = "doifunknownfontfeature",
+ arguments = "string",
+ actions = function(name) ctx_doif(contextnumber(name) == 0) end,
+ }
+
+ implement {
+ name = "adaptfontfeature",
+ arguments = "2 strings",
+ actions = adaptcontext
+ }
+
+ local function registerlanguagefeatures()
+ local specifications = languages.data.specifications
+ for i=1,#specifications do
+ local specification = specifications[i]
+ local language = specification.opentype
+ if language then
+ local script = specification.opentypescript or specification.script
+ if script then
+ local context = specification.context
+ if type(context) == "table" then
+ for i=1,#context do
+ definecontext(context[i], { language = language, script = script})
+ end
+ elseif type(context) == "string" then
+ definecontext(context, { language = language, script = script})
+ end
+ end
+ end
+ end
+ end
+
+ constructors.setfeature = setfeature
+ constructors.resetfeature = resetfeature
+
+ implement { name = "resetfeature", actions = resetfeature }
+ implement { name = "addfeature", actions = setfeature, arguments = { "'+'", "string", "string" } }
+ implement { name = "subtractfeature", actions = setfeature, arguments = { "'-'", "string", "string" } }
+ implement { name = "replacefeature", actions = setfeature, arguments = { "'='", "string", "string" } }
+ implement { name = "revivefeature", actions = setfeature, arguments = { true, "string" } }
+
+ implement {
+ name = "featurelist",
+ actions = { fonts.specifiers.contexttostring, context },
+ arguments = { "string", "'otf'", "string", "'yes'", "'no'", true }
+ }
+
+ implement {
+ name = "registerlanguagefeatures",
+ actions = registerlanguagefeatures,
+ }
+
+end
+
+-- a fontkern plug:
+
+-- nodes.injections.installnewkern(nuts.pool.fontkern)
+
+do
+
+ local report = logs.reporter("otf","variants")
+
+ local function replace(tfmdata,feature,value)
+ local characters = tfmdata.characters
+ local variants = tfmdata.resources.variants
+ if variants then
+ local t = { }
+ for k, v in sortedhash(variants) do
+ t[#t+1] = formatters["0x%X (%i)"](k,k)
+ end
+ value = tonumber(value) or 0xFE00 -- 917762
+ report("fontname : %s",tfmdata.properties.fontname)
+ report("available: % t",t)
+ local v = variants[value]
+ if v then
+ report("using : %X (%i)",value,value)
+ for k, v in next, v do
+ local c = characters[v]
+ if c then
+ characters[k] = c
+ end
+ end
+ else
+ report("unknown : %X (%i)",value,value)
+ end
+ end
+ end
+
+ registerotffeature {
+ name = 'variant',
+ description = 'unicode variant',
+ manipulators = {
+ base = replace,
+ node = replace,
+ }
+ }
+
+end
+
+-- here (todo: closure)
+
+-- make a closure (200 limit):
+
+do
+
+ local trace_analyzing = false trackers.register("otf.analyzing", function(v) trace_analyzing = v end)
+
+ local analyzers = fonts.analyzers
+ local methods = analyzers.methods
+
+ local unsetvalue = attributes.unsetvalue
+
+ local a_color = attributes.private('color')
+ local a_colormodel = attributes.private('colormodel')
+ local m_color = attributes.list[a_color] or { }
+
+ local glyph_code = nodes.nodecodes.glyph
+
+ local states = analyzers.states
+
+ local colornames = {
+ [states.init] = "font:1",
+ [states.medi] = "font:2",
+ [states.fina] = "font:3",
+ [states.isol] = "font:4",
+ [states.mark] = "font:5",
+ [states.rest] = "font:6",
+ [states.rphf] = "font:1",
+ [states.half] = "font:2",
+ [states.pref] = "font:3",
+ [states.blwf] = "font:4",
+ [states.pstf] = "font:5",
+ }
+
+ -- todo: traversers
+ -- todo: check attr_list so that we can use the same .. helper: setcolorattr
+
+ local function markstates(head)
+ if head then
+ head = tonut(head)
+ local model = getattr(head,a_colormodel) or 1
+ for glyph in nextchar, head do
+ local a = getstate(glyph)
+ if a then
+ local name = colornames[a]
+ if name then
+ local color = m_color[name]
+ if color then
+ setattr(glyph,a_colormodel,model)
+ setattr(glyph,a_color,color)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ local function analyzeprocessor(head,font,attr)
+ local tfmdata = fontdata[font]
+ local script, language = otf.scriptandlanguage(tfmdata,attr)
+ local action = methods[script]
+ if not action then
+ return head, false
+ end
+ if type(action) == "function" then
+ local head, done = action(head,font,attr)
+ if done and trace_analyzing then
+ markstates(head)
+ end
+ return head, done
+ end
+ action = action[language]
+ if action then
+ local head, done = action(head,font,attr)
+ if done and trace_analyzing then
+ markstates(head)
+ end
+ return head, done
+ else
+ return head, false
+ end
+ end
+
+ registerotffeature { -- adapts
+ name = "analyze",
+ processors = {
+ node = analyzeprocessor,
+ }
+ }
+
+
+ function methods.nocolor(head,font,attr)
+ for n, c, f in nextchar, head do
+ if not font or f == font then
+ setattr(n,a_color,unsetvalue)
+ end
+ end
+ return head, true
+ end
+
+end
+
+
+local function purefontname(name)
+ if type(name) == "number" then
+ name = getfontname(name)
+ end
+ if type(name) == "string" then
+ return basename(name)
+ end
+end
+
+implement {
+ name = "purefontname",
+ actions = { purefontname, context },
+ arguments = "string",
+}
+
+local sharedstorage = storage.shared
+
+local list = sharedstorage.bodyfontsizes or { }
+local unknown = sharedstorage.unknownbodyfontsizes or { }
+
+sharedstorage.bodyfontsizes = list
+sharedstorage.unknownbodyfontsizes = unknown
+
+implement {
+ name = "registerbodyfontsize",
+ arguments = "string",
+ actions = function(size)
+ list[size] = true
+ end
+}
+
+interfaces.implement {
+ name = "registerunknownbodysize",
+ arguments = "string",
+ actions = function(size)
+ if not unknown[size] then
+ interfaces.showmessage("fonts",14,size)
+ end
+ unknown[size] = true
+ end,
+}
+
+implement {
+ name = "getbodyfontsizes",
+ arguments = "string",
+ actions = function(separator)
+ context(concat(sortedkeys(list),separator))
+ end
+}
+
+implement {
+ name = "processbodyfontsizes",
+ arguments = "string",
+ actions = function(command)
+ local keys = sortedkeys(list)
+ if command then
+ local action = context[command]
+ for i=1,#keys do
+ action(keys[i])
+ end
+ else
+ context(concat(keys,","))
+ end
+ end
+}
+
+implement {
+ name = "cleanfontname",
+ actions = { cleanname, context },
+ arguments = "string"
+}
+
+implement {
+ name = "fontlookupinitialize",
+ actions = names.lookup,
+ arguments = "string",
+}
+
+implement {
+ name = "fontlookupnoffound",
+ actions = { names.noflookups, context },
+}
+
+implement {
+ name = "fontlookupgetkeyofindex",
+ actions = { names.getlookupkey, context },
+ arguments = { "string", "integer"}
+}
+
+implement {
+ name = "fontlookupgetkey",
+ actions = { names.getlookupkey, context },
+ arguments = "string"
+}
+
+-- this might move to a runtime module:
+
+function commands.showchardata(n)
+ local tfmdata = fontdata[currentfont()]
+ if tfmdata then
+ if type(n) == "string" then
+ n = utfbyte(n)
+ end
+ local chr = tfmdata.characters[n]
+ if chr then
+ report_status("%s @ %s => %U => %c => %s",tfmdata.properties.fullname,tfmdata.parameters.size,n,n,serialize(chr,false))
+ end
+ end
+end
+
+function commands.showfontparameters(tfmdata)
+ -- this will become more clever
+ local tfmdata = tfmdata or fontdata[currentfont()]
+ if tfmdata then
+ local parameters = tfmdata.parameters
+ local mathparameters = tfmdata.mathparameters
+ local properties = tfmdata.properties
+ local hasparameters = parameters and next(parameters)
+ local hasmathparameters = mathparameters and next(mathparameters)
+ if hasparameters then
+ report_status("%s @ %s => text parameters => %s",properties.fullname,parameters.size,serialize(parameters,false))
+ end
+ if hasmathparameters then
+ report_status("%s @ %s => math parameters => %s",properties.fullname,parameters.size,serialize(mathparameters,false))
+ end
+ if not hasparameters and not hasmathparameters then
+ report_status("%s @ %s => no text parameters and/or math parameters",properties.fullname,parameters.size)
+ end
+ end
+end
+
+implement {
+ name = "currentdesignsize",
+ actions = function()
+ context(parameters[currentfont()].designsize)
+ end
+}
+
+implement {
+ name = "doifelsefontpresent",
+ actions = { names.exists, commands.doifelse },
+ arguments = "string"
+}
+
+-- we use 0xFE000+ and 0xFF000+ in math and for runtime (text) extensions we
+-- use 0xFD000+
+
+constructors.privateslots = constructors.privateslots or { }
+
+storage.register("fonts/constructors/privateslots", constructors.privateslots, "fonts.constructors.privateslots")
+
+do
+
+ local privateslots = constructors.privateslots
+ local lastprivateslot = 0xFD000
+
+ constructors.privateslots = setmetatableindex(privateslots,function(t,k)
+ local v = lastprivateslot
+ lastprivateslot = lastprivateslot + 1
+ t[k] = v
+ return v
+ end)
+
+ implement {
+ name = "getprivateglyphslot",
+ actions = function(name) context(privateslots[name]) end,
+ arguments = "string",
+ }
+
+end
+
+-- an extra helper
+
+function helpers.getcoloredglyphs(tfmdata)
+ if type(tfmdata) == "number" then
+ tfmdata = fontdata[tfmdata]
+ end
+ if not tfmdata then
+ tfmdata = fontdata[true]
+ end
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local collected = { }
+ for unicode, character in next, characters do
+ local description = descriptions[unicode]
+ if description and (description.colors or character.svg) then
+ collected[#collected+1] = unicode
+ end
+ end
+ table.sort(collected)
+ return collected
+end
+
+-- for the font manual
+
+statistics.register("body font sizes", function()
+ if next(unknown) then
+ return formatters["defined: % t, undefined: % t"](sortedkeys(list),sortedkeys(unknown))
+ end
+end)
+
+statistics.register("used fonts",function()
+ if trace_usage then
+ local filename = file.nameonly(environment.jobname) .. "-fonts-usage.lua"
+ if next(fontdata) then
+ local files = { }
+ local list = { }
+ for id, tfmdata in sortedhash(fontdata) do
+ local filename = tfmdata.properties.filename
+ if filename then
+ local filedata = files[filename]
+ if filedata then
+ filedata.instances = filedata.instances + 1
+ else
+ local rawdata = tfmdata.shared and tfmdata.shared.rawdata
+ local metadata = rawdata and rawdata.metadata
+ files[filename] = {
+ instances = 1,
+ filename = filename,
+ version = metadata and metadata.version,
+ size = rawdata and rawdata.size,
+ }
+ end
+ else
+ -- what to do
+ end
+ end
+ for k, v in sortedhash(files) do
+ list[#list+1] = v
+ end
+ table.save(filename,list)
+ else
+ os.remove(filename)
+ end
+ end
+end)
+
+-- new
+
+do
+
+ local settings_to_array = utilities.parsers.settings_to_array
+
+ implement {
+ name = "definefontcolorpalette",
+ arguments = "2 strings",
+ actions = function(name,set)
+ otf.registerpalette(name,settings_to_array(set))
+ end
+ }
+
+end
+
+do
+
+ local pattern = C((1-S("* "))^1) -- strips all after * or ' at'
+
+ implement {
+ name = "truefontname",
+ arguments = "string",
+ actions = function(s)
+ -- context(match(s,"[^* ]+") or s)
+ context(lpegmatch(pattern,s) or s)
+ end
+ }
+
+end
+
+do
+
+ local function getinstancespec(id)
+ local data = fontdata[id or true]
+ local shared = data.shared
+ local resources = shared and shared.rawdata.resources
+ if resources then
+ local instancespec = data.properties.instance
+ if instancespec then
+ local variabledata = resources.variabledata
+ if variabledata then
+ local instances = variabledata.instances
+ if instances then
+ for i=1,#instances do
+ local instance = instances[i]
+ if cleanname(instance.subfamily)== instancespec then
+ local values = table.copy(instance.values)
+ local axis = variabledata.axis
+ for i=1,#values do
+ for j=1,#axis do
+ if values[i].axis == axis[j].tag then
+ values[i].name = axis[j].name
+ break
+ end
+ end
+ end
+ return values
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ helpers.getinstancespec = getinstancespec
+
+ implement {
+ name = "currentfontinstancespec",
+ actions = function()
+ local t = getinstancespec() -- current font
+ if t then
+ for i=1,#t do
+ if i > 1 then
+ context.space()
+ end
+ local ti = t[i]
+ context("%s=%s",ti.name,ti.value)
+ end
+ end
+ end
+ }
+
+end
+
+-- for the moment here (and not in font-con.lua):
+
+do
+
+ local identical = table.identical
+ local copy = table.copy
+ local fontdata = fonts.hashes.identifiers
+ local addcharacters = font.addcharacters
+
+ -- This helper is mostly meant to add last-resort (virtual) characters
+ -- or runtime generated fonts (so we forget about features and such). It
+ -- will probably take a while before it get used.
+
+ local trace_adding = false
+ local report_adding = logs.reporter("fonts","add characters")
+
+ trackers.register("fonts.addcharacters",function(v) trace_adding = v end)
+
+ function constructors.addcharacters(id,list)
+ local newchar = list.characters
+ if newchar then
+ local data = fontdata[id]
+ local newfont = list.fonts
+ local oldchar = data.characters
+ local oldfont = data.fonts
+ addcharacters(id, {
+ characters = newchar,
+ fonts = newfont,
+ nomath = not data.properties.hasmath,
+ })
+ -- this is just for tracing, as the assignment only uses the fonts list
+ -- and doesn't store it otherwise
+ if newfont then
+ if oldfont then
+ local oldn = #oldfont
+ local newn = #newfont
+ for n=1,newn do
+ local ok = false
+ local nf = newfont[n]
+ for o=1,oldn do
+ if identical(nf,oldfont[o]) then
+ ok = true
+ break
+ end
+ end
+ if not ok then
+ oldn = oldn + 1
+ oldfont[oldn] = newfont[i]
+ end
+ end
+ else
+ data.fonts = newfont
+ end
+ end
+ -- this is because we need to know what goes on and also might
+ -- want to access character data
+ for u, c in next, newchar do
+ if trace_adding then
+ report_adding("adding character %U to font %!font:name!",u,id)
+ end
+ oldchar[u] = c
+ end
+ end
+ end
+
+ implement {
+ name = "addfontpath",
+ arguments = "string",
+ actions = function(list)
+ names.addruntimepath(settings_to_array(list))
+ end
+ }
+
+end
+
+-- moved here
+
+do
+
+ local getfontoffamily = font.getfontoffamily
+ local new_glyph = nodes.pool.glyph
+ local fontproperties = fonts.hashes.properties
+
+ local function getprivateslot(id,name)
+ if not name then
+ name = id
+ id = currentfont()
+ end
+ local properties = fontproperties[id]
+ local privates = properties and properties.privates
+ return privates and privates[name]
+ end
+
+ local function getprivatenode(tfmdata,name)
+ if type(tfmdata) == "number" then
+ tfmdata = fontdata[tfmdata]
+ end
+ local properties = tfmdata.properties
+ local font = properties.id
+ local slot = getprivateslot(font,name)
+ if slot then
+ -- todo: set current attribibutes
+ local char = tfmdata.characters[slot]
+ local tonode = char.tonode
+ if tonode then
+ return tonode(font,char)
+ else
+ return new_glyph(font,slot)
+ end
+ end
+ end
+
+ local function getprivatecharornode(tfmdata,name)
+ if type(tfmdata) == "number" then
+ tfmdata = fontdata[tfmdata]
+ end
+ local properties = tfmdata.properties
+ local font = properties.id
+ local slot = getprivateslot(font,name)
+ if slot then
+ -- todo: set current attributes
+ local char = tfmdata.characters[slot]
+ local tonode = char.tonode
+ if tonode then
+ return "node", tonode(tfmdata,char)
+ else
+ return "char", slot
+ end
+ end
+ end
+
+ helpers.getprivateslot = getprivateslot
+ helpers.getprivatenode = getprivatenode
+ helpers.getprivatecharornode = getprivatecharornode
+
+ implement {
+ name = "getprivatechar",
+ arguments = "string",
+ actions = function(name)
+ local p = getprivateslot(name)
+ if p then
+ context(utfchar(p))
+ end
+ end
+ }
+
+ implement {
+ name = "getprivatemathchar",
+ arguments = "string",
+ actions = function(name)
+ local p = getprivateslot(getfontoffamily(0),name)
+ if p then
+ context(utfchar(p))
+ end
+ end
+ }
+
+ implement {
+ name = "getprivateslot",
+ arguments = "string",
+ actions = function(name)
+ local p = getprivateslot(name)
+ if p then
+ context(p)
+ end
+ end
+ }
+
+end
+
+-- handy, for now here:
+
+function fonts.helpers.collectanchors(tfmdata)
+
+ local resources = tfmdata.resources -- todo: use shared
+
+ if not resources or resources.anchors then
+ return resources.anchors
+ end
+
+ local anchors = { }
+
+ local function set(unicode,target,class,anchor)
+ local a = anchors[unicode]
+ if not a then
+ anchors[unicode] = { [target] = { anchor } }
+ return
+ end
+ local t = a[target]
+ if not t then
+ a[target] = { anchor }
+ return
+ end
+ local x = anchor[1]
+ local y = anchor[2]
+ for k, v in next, t do
+ if v[1] == x and v[2] == y then
+ return
+ end
+ end
+ t[#t+1] = anchor
+ end
+
+ local function getanchors(steps,target)
+ for i=1,#steps do
+ local step = steps[i]
+ local coverage = step.coverage
+ for unicode, data in next, coverage do
+ local class = data[1]
+ local anchor = data[2]
+ if anchor[1] ~= 0 or anchor[2] ~= 0 then
+ set(unicode,target,class,anchor)
+ end
+ end
+ end
+ end
+
+ local function getcursives(steps)
+ for i=1,#steps do
+ local step = steps[i]
+ local coverage = step.coverage
+ for unicode, data in next, coverage do
+ local class = data[1]
+ local en = data[2]
+ local ex = data[3]
+ if en then
+ set(unicode,"entry",class,en)
+ end
+ if ex then
+ set(unicode,"exit", class,ex)
+ end
+ end
+ end
+ end
+
+ local function collect(list)
+ if list then
+ for i=1,#list do
+ local entry = list[i]
+ local steps = entry.steps
+ local kind = entry.type
+ if kind == "gpos_mark2mark" then
+ getanchors(steps,"mark")
+ elseif kind == "gpos_mark2base" then
+ getanchors(steps,"base")
+ elseif kind == "gpos_mark2ligature" then
+ getanchors(steps,"ligature")
+ elseif kind == "gpos_cursive" then
+ getcursives(steps)
+ end
+ end
+ end
+ end
+
+ collect(resources.sequences)
+ collect(resources.sublookups)
+
+ local function sorter(a,b)
+ if a[1] == b[1] then
+ return a[2] < b[2]
+ else
+ return a[1] < b[1]
+ end
+ end
+
+ for unicode, old in next, anchors do
+ for target, list in next, old do
+ sort(list,sorter)
+ end
+ end
+
+ resources.anchors = anchors
+
+ return anchors
+
+end
diff --git a/tex/context/base/mkxl/font-def.lmt b/tex/context/base/mkxl/font-def.lmt
new file mode 100644
index 000000000..4b6616cd9
--- /dev/null
+++ b/tex/context/base/mkxl/font-def.lmt
@@ -0,0 +1,504 @@
+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"
+}
+
+-- We can overload some of the definers.functions so we don't local them.
+
+local lower, gsub = string.lower, string.gsub
+local tostring, next = tostring, next
+local lpegmatch = lpeg.match
+local suffixonly, removesuffix, basename = file.suffix, file.removesuffix, file.basename
+local formatters = string.formatters
+local sortedhash, sortedkeys = table.sortedhash, table.sortedkeys
+
+local allocate = utilities.storage.allocate
+
+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")
+
+local report_defining = logs.reporter("fonts","defining")
+
+--[[ldx--
+<p>Here we deal with defining fonts. We do so by intercepting the
+default loader that only handles <l n='tfm'/>.</p>
+--ldx]]--
+
+local fonts = fonts
+local fontdata = fonts.hashes.identifiers
+local readers = fonts.readers
+local definers = fonts.definers
+local specifiers = fonts.specifiers
+local constructors = fonts.constructors
+local fontgoodies = fonts.goodies
+
+readers.sequence = allocate { 'otf', 'ttf', 'afm', 'tfm', 'lua' } -- dfont ttc
+
+local variants = allocate()
+specifiers.variants = variants
+
+definers.methods = definers.methods or { }
+
+local internalized = allocate() -- internal tex numbers (private)
+
+local loadedfonts = constructors.loadedfonts
+local designsizes = constructors.designsizes
+
+-- not in generic (some day I'll make two defs, one for context, one for generic)
+
+local resolvefile = fontgoodies and fontgoodies.filenames and fontgoodies.filenames.resolve or function(s) return s end
+
+--[[ldx--
+<p>We hardly gain anything when we cache the final (pre scaled)
+<l n='tfm'/> table. But it can be handy for debugging, so we no
+longer carry this code along. Also, we now have quite some reference
+to other tables so we would end up with lots of catches.</p>
+--ldx]]--
+
+--[[ldx--
+<p>We can prefix a font specification by <type>name:</type> or
+<type>file:</type>. The first case will result in a lookup in the
+synonym table.</p>
+
+<typing>
+[ name: | file: ] identifier [ separator [ specification ] ]
+</typing>
+
+<p>The following function split the font specification into components
+and prepares a table that will move along as we proceed.</p>
+--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 function makespecification(specification,lookup,name,sub,method,detail,size)
+ size = size or 655360
+ if not lookup or lookup == "" then
+ lookup = definers.defaultlookup
+ end
+ if trace_defining then
+ report_defining("specification %a, lookup %a, name %a, sub %a, method %a, detail %a",
+ specification, lookup, name, sub, method, detail)
+ 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
+
+definers.makespecification = makespecification
+
+do
+
+ local splitter, splitspecifiers = nil, "" -- not so nice
+
+ local P, C, S, Cc, Cs = lpeg.P, lpeg.C, lpeg.S, lpeg.Cc, lpeg.Cs
+
+ local left = P("(")
+ local right = P(")")
+ local colon = P(":")
+ local space = P(" ")
+ local lbrace = P("{")
+ local rbrace = P("}")
+
+ definers.defaultlookup = "file"
+
+ local prefixpattern = P(false)
+
+ local function addspecifier(symbol)
+ splitspecifiers = splitspecifiers .. symbol
+ local method = S(splitspecifiers)
+ 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 = Cs((lbrace/"") * (1-rbrace)^1 * (rbrace/"") + (1-sub-specification)^1)
+ splitter = P((lookup + Cc("")) * name * (sub + Cc("")) * (specification + Cc("")))
+ end
+
+ local function addlookup(str)
+ prefixpattern = prefixpattern + P(str)
+ end
+
+ definers.addlookup = addlookup
+
+ addlookup("file")
+ addlookup("name")
+ addlookup("spec")
+
+ local function getspecification(str)
+ return lpegmatch(splitter,str or "") -- weird catch
+ end
+
+ definers.getspecification = getspecification
+
+ function definers.registersplit(symbol,action,verbosename)
+ addspecifier(symbol)
+ variants[symbol] = action
+ if verbosename then
+ variants[verbosename] = action
+ end
+ end
+
+ function definers.analyze(specification, size)
+ -- can be optimized with locals
+ local lookup, name, sub, method, detail = getspecification(specification or "")
+ return makespecification(specification, lookup, name, sub, method, detail, size)
+ end
+
+end
+
+--[[ldx--
+<p>We can resolve the filename using the next function:</p>
+--ldx]]--
+
+definers.resolvers = definers.resolvers or { }
+local resolvers = definers.resolvers
+
+-- todo: reporter
+
+function resolvers.file(specification)
+ local name = resolvefile(specification.name) -- catch for renames
+ local suffix = lower(suffixonly(name))
+ if fonts.formats[suffix] then
+ specification.forced = suffix
+ specification.forcedname = name
+ specification.name = removesuffix(name)
+ else
+ specification.name = name -- can be resolved
+ end
+end
+
+function resolvers.name(specification)
+ local resolve = fonts.names.resolve
+ if resolve then
+ local resolved, sub, subindex, instance = resolve(specification.name,specification.sub,specification) -- we pass specification for overloaded versions
+ if resolved then
+ specification.resolved = resolved
+ specification.sub = sub
+ specification.subindex = subindex
+ -- new, needed for experiments
+ if instance then
+ specification.instance = instance
+ local features = specification.features
+ if not features then
+ features = { }
+ specification.features = features
+ end
+ local normal = features.normal
+ if not normal then
+ normal = { }
+ features.normal = normal
+ end
+ normal.instance = instance
+ end
+ --
+ local suffix = lower(suffixonly(resolved))
+ if fonts.formats[suffix] then
+ specification.forced = suffix
+ specification.forcedname = resolved
+ specification.name = removesuffix(resolved)
+ else
+ specification.name = resolved
+ end
+ end
+ else
+ resolvers.file(specification)
+ end
+end
+
+function resolvers.spec(specification)
+ local resolvespec = fonts.names.resolvespec
+ if resolvespec then
+ local resolved, sub, subindex = resolvespec(specification.name,specification.sub,specification) -- we pass specification for overloaded versions
+ if resolved then
+ specification.resolved = resolved
+ specification.sub = sub
+ specification.subindex = subindex
+ specification.forced = lower(suffixonly(resolved))
+ specification.forcedname = resolved
+ specification.name = removesuffix(resolved)
+ end
+ else
+ resolvers.name(specification)
+ end
+end
+
+function definers.resolve(specification)
+ if not specification.resolved or specification.resolved == "" then -- resolved itself not per se in mapping hash
+ local r = resolvers[specification.lookup]
+ if r then
+ r(specification)
+ end
+ end
+ if specification.forced == "" then
+ specification.forced = nil
+ specification.forcedname = nil
+ end
+ specification.hash = lower(specification.name .. ' @ ' .. constructors.hashfeatures(specification))
+ if specification.sub and specification.sub ~= "" then
+ specification.hash = specification.sub .. ' @ ' .. specification.hash
+ end
+ return specification
+end
+
+--[[ldx--
+<p>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.</p>
+
+<p>We need to cache when possible. We do cache raw tfm data (from <l
+n='tfm'/>, <l n='afm'/> or <l n='otf'/>). After that we can cache based
+on specificstion (name) and size, that is, <l n='tex'/> only needs a number
+for an already loaded fonts. However, it may make sense to cache fonts
+before they're scaled as well (store <l n='tfm'/>'s with applied methods
+and features). However, there may be a relation between the size and
+features (esp in virtual fonts) so let's not do that now.</p>
+
+<p>Watch out, here we do load a font, but we don't prepare the
+specification yet.</p>
+--ldx]]--
+
+-- very experimental:
+
+function definers.applypostprocessors(tfmdata)
+ local postprocessors = tfmdata.postprocessors
+ if postprocessors then
+ local properties = tfmdata.properties
+ for i=1,#postprocessors do
+ local extrahash = postprocessors[i](tfmdata) -- after scaling etc
+ if type(extrahash) == "string" and extrahash ~= "" then
+ -- e.g. a reencoding needs this
+ extrahash = gsub(lower(extrahash),"[^a-z]","-")
+ properties.fullname = formatters["%s-%s"](properties.fullname,extrahash)
+ end
+ end
+ end
+ return tfmdata
+end
+
+-- function definers.applypostprocessors(tfmdata)
+-- return tfmdata
+-- end
+
+local function checkfeatures(tfmdata)
+ local resources = tfmdata.resources
+ local shared = tfmdata.shared
+ if resources and shared then
+ local features = resources.features
+ local usedfeatures = shared.features
+ if features and usedfeatures then
+ local usedlanguage = usedfeatures.language or "dflt"
+ local usedscript = usedfeatures.script or "dflt"
+ local function check(what)
+ if what then
+ local foundlanguages = { }
+ for feature, scripts in next, what do
+ if usedscript == "auto" or scripts["*"] then
+ -- ok
+ elseif not scripts[usedscript] then
+ -- report_defining("font %!font:name!, feature %a, no script %a",
+ -- tfmdata,feature,usedscript)
+ else
+ for script, languages in next, scripts do
+ if languages["*"] then
+ -- ok
+ elseif not languages[usedlanguage] then
+ report_defining("font %!font:name!, feature %a, script %a, no language %a",
+ tfmdata,feature,script,usedlanguage)
+ end
+ end
+ end
+ for script, languages in next, scripts do
+ for language in next, languages do
+ foundlanguages[language] = true
+ end
+ end
+ end
+ if false then
+ foundlanguages["*"] = nil
+ foundlanguages = sortedkeys(foundlanguages)
+ for feature, scripts in sortedhash(what) do
+ for script, languages in next, scripts do
+ if not languages["*"] then
+ for i=1,#foundlanguages do
+ local language = foundlanguages[i]
+ if not languages[language] then
+ report_defining("font %!font:name!, feature %a, script %a, no language %a",
+ tfmdata,feature,script,language)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ check(features.gsub)
+ check(features.gpos)
+ end
+ end
+end
+
+function definers.loadfont(specification)
+ local hash = constructors.hashinstance(specification)
+ -- todo: also hash by instance / factors
+ local tfmdata = loadedfonts[hash] -- hashes by size !
+ if not tfmdata then
+ -- normally context will not end up here often (if so there is an issue somewhere)
+ local forced = specification.forced or ""
+ if forced ~= "" then
+ local reader = readers[lower(forced)] -- normally forced is already lowered
+ tfmdata = reader and reader(specification)
+ if not tfmdata then
+ report_defining("forced type %a of %a not found",forced,specification.name)
+ end
+ else
+ local sequence = readers.sequence -- can be overloaded so only a shortcut here
+ for s=1,#sequence do
+ local reader = sequence[s]
+ if readers[reader] then -- we skip not loaded readers
+ if trace_defining then
+ report_defining("trying (reader sequence driven) type %a for %a with file %a",reader,specification.name,specification.filename)
+ end
+ tfmdata = readers[reader](specification)
+ if tfmdata then
+ break
+ else
+ specification.filename = nil
+ end
+ end
+ end
+ end
+ if tfmdata then
+ tfmdata = definers.applypostprocessors(tfmdata)
+ loadedfonts[hash] = tfmdata
+ designsizes[specification.hash] = tfmdata.parameters.designsize
+ checkfeatures(tfmdata)
+ end
+ end
+ if not tfmdata then
+ report_defining("font with asked name %a is not found using lookup %a",specification.name,specification.lookup)
+ end
+ return tfmdata
+end
+
+function constructors.readanddefine(name,size) -- no id -- maybe a dummy first
+ local specification = definers.analyze(name,size)
+ local method = specification.method
+ if method and variants[method] then
+ specification = variants[method](specification)
+ end
+ specification = definers.resolve(specification)
+ local hash = constructors.hashinstance(specification)
+ local id = definers.registered(hash)
+ if not id then
+ local tfmdata = definers.loadfont(specification)
+ if tfmdata then
+ tfmdata.properties.hash = hash
+ id = font.define(tfmdata)
+ definers.register(tfmdata,id)
+ else
+ id = 0 -- signal
+ end
+ end
+ return fontdata[id], id
+end
+
+--[[ldx--
+<p>So far the specifiers. 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).</p>
+
+In the previously defined reader (the one resulting in a <l n='tfm'/>
+table) we cached the (scaled) instances. Here we cache them again, but
+this time based on id. We could combine this in one cache but this does
+not gain much. By the way, passing id's back to in the callback was
+introduced later in the development.</p>
+--ldx]]--
+
+function definers.registered(hash)
+ local id = internalized[hash]
+ return id, id and fontdata[id]
+end
+
+function definers.register(tfmdata,id)
+ if tfmdata and id then
+ local hash = tfmdata.properties.hash
+ if not hash then
+ report_defining("registering font, id %a, name %a, invalid hash",id,tfmdata.properties.filename or "?")
+ elseif not internalized[hash] then
+ internalized[hash] = id
+ if trace_defining then
+ report_defining("registering font, id %s, hash %a",id,hash)
+ end
+ fontdata[id] = tfmdata
+ end
+ end
+end
+
+function definers.read(specification,size,id) -- id can be optional, name can already be table
+ statistics.starttiming(fonts)
+ if type(specification) == "string" then
+ specification = definers.analyze(specification,size)
+ end
+ local method = specification.method
+ if method and variants[method] then
+ specification = variants[method](specification)
+ end
+ specification = definers.resolve(specification)
+ local hash = constructors.hashinstance(specification)
+ local tfmdata = definers.registered(hash) -- id
+ if tfmdata then
+ if trace_defining then
+ report_defining("already hashed: %s",hash)
+ end
+ else
+ tfmdata = definers.loadfont(specification) -- can be overloaded
+-- put in properties instead
+ if tfmdata then
+ tfmdata.original = specification.specification
+ if trace_defining then
+ report_defining("loaded and hashed: %s",hash)
+ end
+ tfmdata.properties.hash = hash
+ if id then
+ definers.register(tfmdata,id)
+ end
+ else
+ if trace_defining then
+ report_defining("not loaded and hashed: %s",hash)
+ end
+ end
+ end
+ if not tfmdata then -- or id?
+ report_defining( "unknown font %a, loading aborted",specification.name)
+ elseif trace_defining and type(tfmdata) == "table" then
+ local properties = tfmdata.properties or { }
+ local parameters = tfmdata.parameters or { }
+ report_defining("using %a font with id %a, name %a, size %a, fullname %a, filename %a",
+ properties.format or "unknown", id or "-", properties.name, parameters.size,
+ properties.fullname, basename(properties.filename))
+ end
+ statistics.stoptiming(fonts)
+ return tfmdata
+end
+
+function font.getfont(id)
+ return fontdata[id] -- otherwise issues
+end
diff --git a/tex/context/base/mkxl/font-enh.lmt b/tex/context/base/mkxl/font-enh.lmt
new file mode 100644
index 000000000..1e532d4ad
--- /dev/null
+++ b/tex/context/base/mkxl/font-enh.lmt
@@ -0,0 +1,90 @@
+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"
+}
+
+-- This module is already stripped from old stuff but more might go away
+-- in lmtx. Stay tuned.
+
+local next = next
+
+local trace_unicoding = false
+
+trackers.register("fonts.defining", function(v) trace_unicoding = v end)
+trackers.register("fonts.unicoding", function(v) trace_unicoding = v end)
+
+local report_unicoding = logs.reporter("fonts","unicoding")
+
+local fonts = fonts
+local constructors = fonts.constructors
+
+local afmfeatures = constructors.features.afm
+local otffeatures = constructors.features.otf
+
+local registerafmfeature = afmfeatures.register
+local registerotffeature = otffeatures.register
+
+local function initialize(tfmdata)
+ local goodies = tfmdata.goodies
+ local newcoding = nil
+ for i=1,#goodies do
+ local remapping = goodies[i].remapping
+ if remapping and remapping.unicodes then
+ newcoding = remapping.unicodes -- names to unicodes
+ end
+ end
+ if newcoding then
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local oldcoding = tfmdata.resources.unicodes
+ local originals = { }
+ for name, newcode in next, newcoding do
+ local oldcode = oldcoding[name]
+ if characters[newcode] and not originals[newcode] then
+ originals[newcode] = {
+ character = characters [newcode],
+ description = descriptions[newcode],
+ }
+ end
+ if oldcode then
+ local original = originals[oldcode]
+ local character, description
+ if original then
+ character = original.character
+ description = original.description
+ else
+ character = characters [oldcode]
+ description = descriptions[oldcode]
+ end
+ characters [newcode] = character
+ descriptions[newcode] = description
+ character .unicode = newcode
+ description.unicode = newcode
+ else
+ oldcoding[name] = newcode
+ end
+ if trace_unicoding then
+ if oldcode then
+ report_unicoding("aliasing glyph %a from %U to %U",name,oldcode,newcode)
+ else
+ report_unicoding("aliasing glyph %a to %U",name,newcode)
+ end
+ end
+ end
+ end
+end
+
+local specification = {
+ name = "unicoding",
+ description = "adapt unicode table",
+ initializers = {
+ base = initialize,
+ node = initialize,
+ },
+}
+
+registerotffeature(specification)
+registerafmfeature(specification)
diff --git a/tex/context/base/mkxl/font-fbk.lmt b/tex/context/base/mkxl/font-fbk.lmt
new file mode 100644
index 000000000..0e104aca7
--- /dev/null
+++ b/tex/context/base/mkxl/font-fbk.lmt
@@ -0,0 +1,357 @@
+if not modules then modules = { } end modules ['font-fbk'] = {
+ 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 cos, tan, rad, format = math.cos, math.tan, math.rad, string.format
+local utfbyte, utfchar = utf.byte, utf.char
+local next = next
+
+--[[ldx--
+<p>This is very experimental code!</p>
+--ldx]]--
+
+local trace_visualize = false trackers.register("fonts.composing.visualize", function(v) trace_visualize = v end)
+local trace_define = false trackers.register("fonts.composing.define", function(v) trace_define = v end)
+
+local report = logs.reporter("fonts","combining")
+
+local allocate = utilities.storage.allocate
+
+local fonts = fonts
+local handlers = fonts.handlers
+local constructors = fonts.constructors
+local helpers = fonts.helpers
+
+local otf = handlers.otf
+local afm = handlers.afm
+local registerotffeature = otf.features.register
+local registerafmfeature = afm.features.register
+
+local addotffeature = otf.addfeature
+
+local unicodecharacters = characters.data
+local unicodefallbacks = characters.fallbacks
+
+local vfcommands = helpers.commands
+local charcommand = vfcommands.char
+local rightcommand = vfcommands.right
+local downcommand = vfcommands.down
+local upcommand = vfcommands.up
+local push = vfcommands.push
+local pop = vfcommands.pop
+
+local force_combining = false -- just for demo purposes (see mk)
+local fraction = 0.15 -- 30 units for lucida
+
+-- todo: we also need to update the feature hashes ... i'll do that when i'm in the mood
+-- and/or when i need it
+
+local function composecharacters(tfmdata)
+ -- this assumes that slot 1 is self, there will be a proper self some day
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local parameters = tfmdata.parameters
+ local properties = tfmdata.properties
+ local Xdesc = descriptions[utfbyte("X")]
+ local xdesc = descriptions[utfbyte("x")]
+ if Xdesc and xdesc then
+ local scale = parameters.factor or 1
+ local deltaxheight = scale * (Xdesc.boundingbox[4] - xdesc.boundingbox[4])
+ local extraxheight = fraction * deltaxheight -- maybe use compose value
+ local italicfactor = parameters.italicfactor or 0
+ local vfspecials = backends.tables.vfspecials --brr
+ local red, green, blue, black
+ if trace_visualize then
+ red = vfspecials.startcolor("red")
+ green = vfspecials.startcolor("green")
+ blue = vfspecials.startcolor("blue")
+ black = vfspecials.stopcolor
+ end
+ local compose = fonts.goodies.getcompositions(tfmdata)
+ if compose and trace_visualize then
+ report("using compose information from goodies file")
+ end
+ local done = false
+ for i, c in next, unicodecharacters do -- loop over all characters ... not that efficient but a specials hash takes memory
+ if force_combining or not characters[i] then
+ local s = c.specials
+ if s and s[1] == 'char' then
+ local chr = s[2]
+ local charschr = characters[chr]
+ if charschr then
+ local cc = c.category
+ if cc == 'll' or cc == 'lu' or cc == 'lt' then -- characters.is_letter[cc]
+ local acc = s[3]
+ local t = { }
+ for k, v in next, charschr do
+ if k ~= "commands" then
+ t[k] = v
+ end
+ end
+ local charsacc = characters[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 -- fallback accents
+ acc = unicodefallbacks[acc]
+ charsacc = acc and characters[acc]
+ end
+ local chr_t = charcommand[chr]
+ if charsacc then
+ if trace_define then
+ report("composed %C, base %C, accent %C",i,chr,acc)
+ end
+ local acc_t = charcommand[acc]
+ local cb = descriptions[chr].boundingbox
+ local ab = descriptions[acc].boundingbox
+ -- todo: adapt height
+ if cb and ab then
+ local c_llx = scale*cb[1]
+ local c_lly = scale*cb[2]
+ local c_urx = scale*cb[3]
+ local c_ury = scale*cb[4]
+ local a_llx = scale*ab[1]
+ local a_lly = scale*ab[2]
+ local a_urx = scale*ab[3]
+ local a_ury = scale*ab[4]
+ local done = false
+ if compose then
+ local i_compose = compose[i]
+ local i_anchored = i_compose and i_compose.anchored
+ if i_anchored then
+ local c_compose = compose[chr]
+ local a_compose = compose[acc]
+ local c_anchors = c_compose and c_compose.anchors
+ local a_anchors = a_compose and a_compose.anchors
+ if c_anchors and a_anchors then
+ local c_anchor = c_anchors[i_anchored]
+ local a_anchor = a_anchors[i_anchored]
+ if c_anchor and a_anchor then
+ local cx = c_anchor.x or 0
+ local cy = c_anchor.y or 0
+ local ax = a_anchor.x or 0
+ local ay = a_anchor.y or 0
+ local dx = cx - ax
+ local dy = cy - ay
+ if trace_define then
+ report("building %C from %C and %C",i,chr,acc)
+ report(" boundingbox:")
+ report(" chr: %3i %3i %3i %3i",unpack(cb))
+ report(" acc: %3i %3i %3i %3i",unpack(ab))
+ report(" anchors:")
+ report(" chr: %3i %3i",cx,cy)
+ report(" acc: %3i %3i",ax,ay)
+ report(" delta:")
+ report(" %s: %3i %3i",i_anchored,dx,dy)
+ end
+ local right = rightcommand[scale*dx]
+ local down = upcommand[scale*dy]
+ if trace_visualize then
+ t.commands = {
+ push, right, down,
+ green, acc_t, black,
+ pop, chr_t,
+ }
+ else
+ t.commands = {
+ push, right, down,
+ acc_t, pop, chr_t,
+ }
+ end
+ done = true
+ end
+ end
+ end
+ end
+ if not done then
+ -- can be sped up for scale == 1
+ local dx = (c_urx - a_urx - a_llx + c_llx)/2
+ local dd = (c_urx - c_llx)*italicfactor
+ if a_ury < 0 then
+ local right = rightcommand[dx-dd]
+ if trace_visualize then
+ t.commands = {
+ push, right, red, acc_t,
+ black, pop, chr_t,
+ }
+ else
+ t.commands = {
+ push, right, acc_t, pop,
+ chr_t,
+ }
+ end
+t.depth = a_ury
+ elseif c_ury > a_lly then -- messy test
+ local dy
+ if compose then
+ -- experimental: we could use sx but all that testing
+ -- takes time and code
+ dy = compose[i]
+ if dy then
+ dy = dy.dy
+ end
+ if not dy then
+ dy = compose[acc]
+ if dy then
+ dy = dy and dy.dy
+ end
+ end
+ if not dy then
+ dy = compose.dy
+ end
+ if not dy then
+ dy = - deltaxheight + extraxheight
+ elseif dy > -1.5 and dy < 1.5 then
+ -- we assume a fraction of (percentage)
+ dy = - dy * deltaxheight
+ else
+ -- we assume fontunits (value smaller than 2 make no sense)
+ dy = - dy * scale
+ end
+ else
+ dy = - deltaxheight + extraxheight
+ end
+t.height = a_ury-dy
+ local right = rightcommand[dx+dd]
+ local down = downcommand[dy]
+ if trace_visualize then
+ t.commands = {
+ push, right, down, green,
+ acc_t, black, pop, chr_t,
+ }
+ else
+ t.commands = {
+ push, right, down, acc_t,
+ pop, chr_t,
+ }
+ end
+ else
+ local right = rightcommand[dx+dd]
+ if trace_visualize then
+ t.commands = {
+ push, right, blue, acc_t,
+ black, pop, chr_t,
+ }
+ else
+ t.commands = {
+ push, right, acc_t, pop,
+ chr_t,
+ }
+ end
+t.height = a_ury
+ end
+ end
+ else
+ t.commands = {
+ chr_t, -- else index mess
+ }
+ end
+ else
+ if trace_define then
+ report("%C becomes simplified %C",i,chr)
+ end
+ t.commands = {
+ chr_t, -- else index mess
+ }
+ end
+ done = true
+ characters[i] = t
+ local d = { }
+ for k, v in next, descriptions[chr] do
+ d[k] = v
+ end
+ descriptions[i] = d
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+local specification = {
+ name = "compose",
+ description = "additional composed characters",
+ manipulators = {
+ base = composecharacters,
+ node = composecharacters,
+ }
+}
+
+registerotffeature(specification)
+registerafmfeature(specification)
+
+addotffeature {
+ name = "char-ligatures",
+ type = "ligature",
+ data = characters.splits.char,
+ order = { "char-ligatures" },
+ prepend = true,
+}
+
+addotffeature {
+ name = "compat-ligatures",
+ type = "ligature",
+ data = characters.splits.compat,
+ order = { "compat-ligatures" },
+ prepend = true,
+}
+
+registerotffeature {
+ name = 'char-ligatures',
+ description = 'unicode char specials to ligatures',
+}
+
+registerotffeature {
+ name = 'compat-ligatures',
+ description = 'unicode compat specials to ligatures',
+}
+
+do
+
+ -- This installs the builder into the regular virtual font builder,
+ -- which only makes sense as demo.
+
+ local vf = handlers.vf
+ local commands = vf.combiner.commands
+
+ vf.helpers.composecharacters = composecharacters
+
+ commands["compose.trace.enable"] = function()
+ trace_visualize = true
+ end
+
+ commands["compose.trace.disable"] = function()
+ trace_visualize = false
+ end
+
+ commands["compose.force.enable"] = function()
+ force_combining = true
+ end
+
+ commands["compose.force.disable"] = function()
+ force_combining = false
+ end
+
+ commands["compose.trace.set"] = function(g,v)
+ if v[2] == nil then
+ trace_visualize = true
+ else
+ trace_visualize = v[2]
+ end
+ end
+
+ commands["compose.apply"] = function(g,v)
+ composecharacters(g)
+ end
+
+end
diff --git a/tex/context/base/mkxl/font-fmp.lmt b/tex/context/base/mkxl/font-fmp.lmt
new file mode 100644
index 000000000..f35c96f49
--- /dev/null
+++ b/tex/context/base/mkxl/font-fmp.lmt
@@ -0,0 +1,123 @@
+if not modules then modules = { } end modules ['font-fmp'] = {
+ 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"
+}
+
+-- We only need to pick up the filename and optionally the enc file as we only use
+-- them for old school virtual math fonts. We might as well drop this completely.
+-- This used to be a backend module but the code is rather generic so we just put it
+-- here now.
+
+local find, match, splitlines = string.find, string.match, string.splitlines
+
+local implement = interfaces.implement
+
+local mappings = { }
+
+local function setline(n)
+ if trace_fonts then
+ report_fonts("mapline: %s",n)
+ end
+ local name, fullname, encfile, pfbfile = match(n,"(%S+)%s+(%S+).-<(.-%.enc).-<(.-%.pfb)")
+ if name then
+ mappings[name] = { fullname, encfile, pfbfile }
+ end
+end
+
+local function setfile(n)
+ local okay, data = resolvers.loadbinfile(n,"map")
+ if okay and data then
+ data = splitlines(data)
+ for i=1,#data do
+ local d = data[i]
+ if d ~= "" and not find(d,"^[#%%]") then
+ setline(d)
+ end
+ end
+ end
+end
+
+local function getentry(n)
+ local n = file.nameonly(n)
+ local m = mappings[n]
+ if m then
+ local encfile = m[2]
+ local encoding = fonts.encodings.load(encfile)
+ if not encoding then
+ return
+ end
+ local pfbfile = resolvers.findfile(m[3],"pfb")
+ if not pfbfile or pfbfile == "" then
+ return
+ end
+ return encoding, pfbfile, encfile
+ end
+end
+
+-- soon to be obsolete:
+
+local mappings = fonts.mappings or { }
+fonts.mappings = mappings
+
+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 mappings.loadfile(name)
+ name = file.addsuffix(name,"map")
+ if not loaded[name] then
+ if trace_mapfiles then
+ report_mapfiles("loading map file %a",name)
+ end
+ setfile(name)
+ loaded[name] = true
+ end
+end
+
+local loaded = { -- prevent double loading
+}
+
+function mappings.loadline(how,line)
+ if line then
+ how = how .. " " .. line
+ elseif how == "" then
+ how = "= " .. line
+ end
+ if not loaded[how] then
+ if trace_mapfiles then
+ report_mapfiles("processing map line %a",line)
+ end
+ setline(how)
+ loaded[how] = true
+ end
+end
+
+function mappings.reset()
+ lpdf.setmapfile("") -- tricky ... backend related
+end
+
+mappings.getentry = getentry
+
+implement {
+ name = "loadmapfile",
+ actions = mappings.loadfile,
+ arguments = "string"
+}
+
+implement {
+ name = "loadmapline",
+ actions = mappings.loadline,
+ arguments = "string"
+}
+
+implement {
+ name = "resetmapfiles",
+ actions = mappings.reset,
+ arguments = "string"
+}
diff --git a/tex/context/base/mkxl/font-lib.mklx b/tex/context/base/mkxl/font-lib.mklx
index 08c5acbdf..d187d3a05 100644
--- a/tex/context/base/mkxl/font-lib.mklx
+++ b/tex/context/base/mkxl/font-lib.mklx
@@ -17,9 +17,10 @@
\registerctxluafile{font-ini}{}
\registerctxluafile{font-log}{}
-\registerctxluafile{font-con}{}
-\registerctxluafile{font-cft}{}
+\registerctxluafile{font-con}{autosuffix}
+%registerctxluafile{font-cft}{}
\registerctxluafile{font-enc}{}
+\registerctxluafile{font-fmp}{autosuffix}
\registerctxluafile{font-agl}{} % if needed we can comment this and delay loading
\registerctxluafile{font-cid}{} % cid maps
\registerctxluafile{font-map}{optimize}
@@ -81,15 +82,15 @@
\registerctxluafile{font-lua}{}
\registerctxluafile{font-vir}{}
-\registerctxluafile{font-enh}{}
+\registerctxluafile{font-enh}{autosuffix}
\registerctxluafile{good-ini}{}
\registerctxluafile{good-gen}{}
\registerctxluafile{good-ctx}{}
\registerctxluafile{good-mth}{}
-\registerctxluafile{font-def}{}
-\registerctxluafile{font-ctx}{} % after def as it overloads
+\registerctxluafile{font-def}{autosuffix}
+\registerctxluafile{font-ctx}{autosuffix} % after def as it overloads
% extensions, order matters
@@ -116,7 +117,7 @@
\registerctxluafile{font-imp-tracing}{} % comes last!
-\registerctxluafile{font-fbk}{}
+\registerctxluafile{font-fbk}{autosuffix}
\registerctxluafile{font-aux}{}
@@ -151,22 +152,4 @@
\permanent \def\cleanfontname #1{\clf_cleanfontname{#1}}
\permanent\protected\def\setfontofid #1{\clf_setfontofid\numexpr#1\relax}
-% this is an ugly hack needed for postponed inclusion stuff ... don't try
-% to understand this ... these are kind of old mkiv solutions
-
-\permanent\protected\def\typethreefont#1{\setfontid#1\relax}
-\permanent\protected\def\typethreechar#1{\char#1\hskip-\fontcharwd\font#1\relax}
-\permanent\protected\def\typethreelast#1{\char#1\relax}
-\permanent\protected\def\typethreecode#1{\pdfliteral direct {#1}}
-
-% This might change ...
-
-\newtoks \typethreetoks % used at the lua end
-\mutable\let\typethreemacro\empty % used at the lua end
-
-\typethreetoks {%
- \setbox\zerocount\hpack{\typethreemacro}%
- \setbox\zerocount\hpack{\raise\dp\zerocount\box\zerocount}%
-}
-
\protect \endinput
diff --git a/tex/context/base/mkxl/font-ogr.lmt b/tex/context/base/mkxl/font-ogr.lmt
index e57e88ed6..df6449ded 100644
--- a/tex/context/base/mkxl/font-ogr.lmt
+++ b/tex/context/base/mkxl/font-ogr.lmt
@@ -9,12 +9,6 @@ if not modules then modules = { } end modules ['font-ogr'] = {
-- Here we deal with graphic variants and for now also color support ends up here
-- but that might change. It's lmtx only code.
-if not context then
- return
-elseif CONTEXTLMTXMODE == 0 then
- return
-end
-
local tostring, tonumber, next, type = tostring, tonumber, next, type
local round, max, mod, div = math.round, math.max, math.mod, math.div
local find = string.find
@@ -72,7 +66,6 @@ do
d_tfmdata.descriptions = d_descriptions
d_tfmdata.parentdata = t_tfmdata -- so we can access it if needed
d_properties.instance = - droppedin -- will become an extra element in the hash
- t_properties.virtualized = true
identifiers[droppedin] = d_tfmdata
local fonts = t_tfmdata.fonts or { }
t_tfmdata.fonts = fonts
@@ -358,18 +351,23 @@ end
-- This is also somewhat specific.
-local sharedpalettes do
+local color_direct = function() return false end
+local color_indirect = color_direct
+
+updaters.register("backend.update",function()
+ color_direct = lpdf.fonts.color_direct
+ color_indirect = lpdf.fonts.color_indirect
+end)
- sharedpalettes = { }
+local sharedpalettes = { } do
- local colors = attributes.list[attributes.private('color')] or { }
- local transparencies = attributes.list[attributes.private('transparency')] or { }
+ local register = attributes.colors.register
+
+ local colors = attributes.list[attributes.private('color')] or { }
+ local transparencies = attributes.list[attributes.private('transparency')] or { }
function otf.registerpalette(name,values)
sharedpalettes[name] = values
- local color = lpdf.color
- local transparency = lpdf.transparency
- local register = colors.register
for i=1,#values do
local v = values[i]
if v == "textcolor" then
@@ -387,13 +385,7 @@ local sharedpalettes do
c = colors[v]
t = transparencies[v]
end
- if c and t then
- values[i] = color(1,c) .. " " .. transparency(t)
- elseif c then
- values[i] = color(1,c)
- elseif t then
- values[i] = color(1,t)
- end
+ values[i] = color_indirect(c,t)
end
end
end
@@ -402,29 +394,11 @@ end
local initializeoverlay do
- -- we should use the proper interface instead but for now:
-
- local colors = attributes.colors
- local rgbtocmyk = colors.rgbtocmyk
-
- local f_cmyk = formatters["%.3N %.3f %.3N %.3N k"]
- local f_rgb = formatters["%.3N %.3f %.3N rg"]
- local f_gray = formatters["%.3N g"]
-
- local function convert(t,k)
+ local function autoconvert(t,k)
local v = { }
- local m = colors.model
for i=1,#k do
local p = k[i]
- local r, g, b = p[1]/255, p[2]/255, p[3]/255
- if r == g and g == b then
- p = f_gray(r)
- elseif m == "cmyk" then
- p = f_cmyk(rgbtocmyk(r,g,b))
- else
- p = f_rgb(r,g,b)
- end
- v[i] = p
+ v[i] = color_direct(p[1]/255, p[2]/255, p[3]/255)
end
t[k] = v
return v
@@ -438,7 +412,7 @@ local initializeoverlay do
--
local converted = resources.converted
if not converted then
- converted = setmetatableindex(convert)
+ converted = setmetatableindex(autoconvert)
resources.converted = converted
end
local colorvalues = sharedpalettes[value]
diff --git a/tex/context/base/mkxl/font-tra.mkxl b/tex/context/base/mkxl/font-tra.mkxl
index 72addc5e9..b24eab865 100644
--- a/tex/context/base/mkxl/font-tra.mkxl
+++ b/tex/context/base/mkxl/font-tra.mkxl
@@ -330,14 +330,14 @@
% new
-\permanent\protected\def\savefontdata[#1]% not yet in i-*.xml
- {\begingroup
- \getdummyparameters[#1]%
- \clf_savefont {
- filename {\dummyparameter\c!file}
- fontname {\dummyparameter\c!name}
- method {\dummyparameter\c!method}
- }%
- \endgroup}
+% \permanent\protected\def\savefontdata[#1]% not yet in i-*.xml
+% {\begingroup
+% \getdummyparameters[#1]%
+% \clf_savefont {
+% filename {\dummyparameter\c!file}
+% fontname {\dummyparameter\c!name}
+% method {\dummyparameter\c!method}
+% }%
+% \endgroup}
\protect \endinput
diff --git a/tex/context/base/mkxl/lpdf-col.lmt b/tex/context/base/mkxl/lpdf-col.lmt
index 5ebd4bd79..13f34fe46 100644
--- a/tex/context/base/mkxl/lpdf-col.lmt
+++ b/tex/context/base/mkxl/lpdf-col.lmt
@@ -737,47 +737,6 @@ do
local f_slant = formatters["q 1 0 %N 1 0 0 cm"]
- -- local fillcolors = {
- -- red = { "pdf", "page", "1 0 0 rg" },
- -- green = { "pdf", "page", "0 1 0 rg" },
- -- blue = { "pdf", "page", "0 0 1 rg" },
- -- gray = { "pdf", "page", ".5 g" },
- -- black = { "pdf", "page", "0 g" },
- -- palered = { "pdf", "page", "1 .75 .75 rg" },
- -- palegreen = { "pdf", "page", ".75 1 .75 rg" },
- -- paleblue = { "pdf", "page", ".75 .75 1 rg" },
- -- palegray = { "pdf", "page", ".75 g" },
- -- }
- --
- -- local strokecolors = {
- -- red = { "pdf", "page", "1 0 0 RG" },
- -- green = { "pdf", "page", "0 1 0 RG" },
- -- blue = { "pdf", "page", "0 0 1 RG" },
- -- gray = { "pdf", "page", ".5 G" },
- -- black = { "pdf", "page", "0 G" },
- -- palered = { "pdf", "page", "1 .75 .75 RG" },
- -- palegreen = { "pdf", "page", ".75 1 .75 RG" },
- -- paleblue = { "pdf", "page", ".75 .75 1 RG" },
- -- palegray = { "pdf", "page", ".75 G" },
- -- }
- --
- -- backends.pdf.tables.vfspecials = allocate { -- todo: distinguish between glyph and rule color
- --
- -- red = { "pdf", "page", "1 0 0 rg 1 0 0 RG" },
- -- green = { "pdf", "page", "0 1 0 rg 0 1 0 RG" },
- -- blue = { "pdf", "page", "0 0 1 rg 0 0 1 RG" },
- -- gray = { "pdf", "page", ".75 g .75 G" },
- -- black = { "pdf", "page", "0 g 0 G" },
- --
- -- -- rulecolors = fillcolors,
- -- -- fillcolors = fillcolors,
- -- -- strokecolors = strokecolors,
- --
- -- startslant = function(a) return { "pdf", "origin", f_slant(a) } end,
- -- stopslant = { "pdf", "origin", "Q" },
- --
- -- }
-
local slants = setmetatableindex(function(t,k)
local v = { "pdf", "origin", f_slant(a) }
t[k] = v
@@ -830,16 +789,147 @@ do
-- will experiment first (both engines). Virtual fonts will change
-- anyway.
- backends.pdf.tables.vfspecials = allocate { -- todo: distinguish between glyph and rule color
+ local vfspecials = backends.pdf.tables.vfspecials or allocate { }
+ backends.pdf.tables.vfspecials = vfspecials
- startcolor = startcolor,
- -- stopcolor = { "pdf", "page", "0 g 0 G Q" },
- stopcolor = { "pdf", "text", "Q" },
+ vfspecials.startcolor = startcolor
+ vfspecials.stopcolor = { "pdf", "text", "Q" }
- startslant = startslant,
- -- stopslant = { "pdf", "origin", "Q" },
- stopslant = { "pdf", "text", "Q" },
+ vfspecials.startslant = startslant
+ vfspecials.stopslant = { "pdf", "text", "Q" }
- }
+end
+
+-- new method:
+
+do
+
+ updaters.register("backend.update.lpdf",function()
+ pdfprint = lpdf.print
+ end)
+
+ -- Is this still used? It's a font property now.
+
+ local f_slant = formatters["q 1 0 %N 1 0 0 cm"]
+
+ local slants = setmetatableindex(function(t,k)
+ local v = f_slant(a)
+ t[k] = v
+ return k
+ end)
+
+ local function startslant(a)
+ return pdfprint("origin", slants[a])
+ end
+
+ local function stopslant()
+ pdfprint("text", "Q")
+ end
+
+ -- We inherit the outer transparency.
+
+ local pdfcolor = lpdf.color
+ local pdftransparency = lpdf.transparency
+
+ -- local c_cache = setmetatableindex(function(t,m)
+ -- local v = setmetatableindex(function(t,c)
+ -- local p = "q " .. pdfcolor(m,c)
+ -- t[c] = p
+ -- return p
+ -- end)
+ -- t[m] = v
+ -- return v
+ -- end)
+ --
+ -- local t_cache = setmetatableindex(function(t,transparency)
+ -- local p = pdftransparency(transparency)
+ -- local v = setmetatableindex(function(t,colormodel)
+ -- local v = setmetatableindex(function(t,color)
+ -- local v = "q " .. pdfcolor(colormodel,color) .. " " .. p
+ -- t[color] = v
+ -- return v
+ -- end)
+ -- t[colormodel] = v
+ -- return v
+ -- end)
+ -- t[transparency] = v
+ -- return v
+ -- end)
+
+ -- local function startcolor(color)
+ -- local m, c = colortoattributes(color)
+ -- local t = transparencytoattribute(color)
+ -- if t then
+ -- pdfprint("page", t_cache[t][m][c])
+ -- else
+ -- pdfprint("page", c_cache[m][c])
+ -- end
+ -- end
+
+ local function startcolor(color)
+ local m, c = colortoattributes(color)
+ local t = transparencytoattribute(color)
+ if t and t ~= unsetvalue then
+ pdfprint("page", "q " .. pdfcolor(m,c) .. " " .. pdftransparency(t))
+ else
+ pdfprint("page", "q " .. pdfcolor(m,c))
+ end
+ end
+
+ local function stopcolor()
+ pdfprint("text", "Q")
+ end
+
+ updaters.register("backend.update.lpdf",function()
+ fonts.vfcommands = {
+ startslant = startslant,
+ stopslant = stopslant,
+ startcolor = startcolor,
+ stopcolor = stopcolor,
+ }
+ end)
end
+
+-- These generate the in-stream color commands:
+
+do
+
+ local color = lpdf.color
+ local transparency = lpdf.transparency
+
+ local fonts = { }
+ lpdf.fonts = fonts
+
+ fonts.color_indirect = function(c,t)
+ if c and t then
+ return color(1,c) .. " " .. transparency(t)
+ elseif c then
+ return color(1,c)
+ elseif t then
+ return transparency(t)
+ else
+ return false
+ end
+ end
+
+ local colors = attributes.colors
+ local rgbtocmyk = colors.rgbtocmyk
+
+ local f_cmyk = formatters["%.3N %.3f %.3N %.3N k"]
+ local f_rgb = formatters["%.3N %.3f %.3N rg"]
+ local f_gray = formatters["%.3N g"]
+
+ fonts.color_direct = function(r,g,b)
+ local m = colors.model
+ if r == g and g == b then
+ return f_gray(r)
+ elseif m == "cmyk" then
+ return f_cmyk(rgbtocmyk(r,g,b))
+ else
+ return f_rgb(r,g,b)
+ end
+ end
+
+end
+
diff --git a/tex/context/base/mkxl/lpdf-emb.lmt b/tex/context/base/mkxl/lpdf-emb.lmt
index 994ae2e07..53bbfe5da 100644
--- a/tex/context/base/mkxl/lpdf-emb.lmt
+++ b/tex/context/base/mkxl/lpdf-emb.lmt
@@ -78,6 +78,7 @@ local readstring = utilities.files.readstring
local openfile = utilities.files.open
local closefile = utilities.files.close
+local getmapentry = fonts.mappings.getentry
-- needs checking: signed vs unsigned
@@ -307,63 +308,6 @@ end
end
--- Map file mess.
-
-local getmapentry do
-
- -- We only need to pick up the filename and optionally the enc file
- -- as we only use them for old school virtual math fonts. We might as
- -- we drop this completely.
-
- local find, match, splitlines = string.find, string.match, string.splitlines
-
-
- local mappings = { }
-
- lpdf.loadmapline = function(n)
- if trace_fonts then
- report_fonts("mapline: %s",n)
- end
- local name, fullname, encfile, pfbfile = match(n,"(%S+)%s+(%S+).-<(.-%.enc).-<(.-%.pfb)")
- if name then
- mappings[name] = { fullname, encfile, pfbfile }
- end
- end
-
- lpdf.loadmapfile = function(n)
- local okay, data = resolvers.loadbinfile(n,"map")
- if okay and data then
- data = splitlines(data)
- for i=1,#data do
- local d = data[i]
- if d ~= "" and not find(d,"^[#%%]") then
- loadmapline(d)
- end
- end
- end
- end
-
- getmapentry = function(n)
- local n = file.nameonly(n)
- local m = mappings[n]
- if m then
- local encfile = m[2]
- local encoding = fonts.encodings.load(encfile)
- if not encoding then
- return
- end
- local pfbfile = resolvers.findfile(m[3],"pfb")
- if not pfbfile or pfbfile == "" then
- return
- end
- return encoding, pfbfile, encfile
- end
- end
-
- lpdf.getmapentry = getmapentry
-
-end
-
-- The three writers: opentype, truetype and type1.
local mainwriters = { }
@@ -2241,7 +2185,7 @@ end
-- local done = false -- todo:
--- updaters.register("backend.update.pdf",function()
+-- updaters.register("backend.update",function()
-- if not done then
lpdf.registerdocumentfinalizer(lpdf.flushfonts,1,"wrapping up fonts")
-- done = true
diff --git a/tex/context/base/mkxl/lpdf-grp.lmt b/tex/context/base/mkxl/lpdf-grp.lmt
index 3b45123e3..6adbe8c3c 100644
--- a/tex/context/base/mkxl/lpdf-grp.lmt
+++ b/tex/context/base/mkxl/lpdf-grp.lmt
@@ -298,3 +298,5 @@ end
function lpdf.patternstream(n,width,height)
return f_pattern("Pt" .. n,width*basepoints,height*basepoints)
end
+
+backends.pdf.codeinjections.registerpattern = lpdf.registerpattern
diff --git a/tex/context/base/mkxl/lpdf-img.lmt b/tex/context/base/mkxl/lpdf-img.lmt
index 83d3dfae6..50034c360 100644
--- a/tex/context/base/mkxl/lpdf-img.lmt
+++ b/tex/context/base/mkxl/lpdf-img.lmt
@@ -1345,3 +1345,7 @@ end
-- end
-- return true
-- end
+
+backends.pdf.codeinjections.jpg = lpdf.injectors.jpg
+backends.pdf.codeinjections.jp2 = lpdf.injectors.jp2
+backends.pdf.codeinjections.png = lpdf.injectors.png
diff --git a/tex/context/base/mkxl/lpdf-lmt.lmt b/tex/context/base/mkxl/lpdf-lmt.lmt
index d1c5e59f7..c02a35fe1 100644
--- a/tex/context/base/mkxl/lpdf-lmt.lmt
+++ b/tex/context/base/mkxl/lpdf-lmt.lmt
@@ -176,7 +176,6 @@ local usedcharacters = setmetatableindex("table")
local pdfcharacters
local horizontalmode = true
------ widefontmode = true
local scalefactor = 1
local threshold = 655360
local thresfactor = 100
@@ -193,7 +192,6 @@ local function updatefontstate(font)
local designsize = fontparameters.designsize or size
pdfcharacters = usedcharacters[font]
horizontalmode = fontparameters.writingmode ~= "vertical"
- -- widefontmode = fontproperties.encodingbytes == 2
scalefactor = (designsize/size) * tjfactor
local fthreshold = fontproperties.threshold
threshold = (fthreshold and (size * fthreshold / 100)) or 655360
@@ -945,6 +943,7 @@ local flushrule, flushsimplerule, flushspecialrule, flushimage, flushgroup do
local f_b = formatters["%.6N w 0 %.6N %.6N %.6N re f"]
local f_x = formatters["[] 0 d 0 J %.6N w %.6N %.6N %.6N %.6N re S"]
+ local f_y = formatters["[] 0 d 0 J %.6N w %.6N %.6N %.6N %.6N re S %.6N 0 m %.6N 0 l S"]
-- Historically the index is an object which is kind of bad.
@@ -1361,7 +1360,7 @@ local flushrule, flushsimplerule, flushspecialrule, flushimage, flushgroup do
b = b + 1 ; buffer[b] = s_e
end
- flushspecialrule = function(pos_h,pos_v,pos_r,width,height,depth,line,outline)
+ flushspecialrule = function(pos_h,pos_v,pos_r,width,height,depth,line,outline,baseline)
pdf_goto_pagemode()
b = b + 1 ; buffer[b] = s_b
@@ -1375,7 +1374,14 @@ local flushrule, flushsimplerule, flushspecialrule, flushimage, flushgroup do
local rule
if outline then
- rule = f_x(line,half,-depth+half,width-line,total-line)
+ local d = -depth + half
+ local w = width - line
+ local t = total - line
+ if baseline then
+ rule = f_y(line,half,d,w,t,half,w)
+ else
+ rule = f_x(line,half,d,w,t)
+ end
else
rule = f_b(line,-depth,width,total)
end
@@ -2901,6 +2907,11 @@ do
-- --
lpdf.registerdocumentfinalizer(wrapup,nil,"wrapping up")
--
+ statistics.register("result saved in file", function()
+ local outputfilename = environment.outputfilename or environment.jobname or tex.jobname or "<unset>"
+ return string.format("%s.%s, compresslevel %s, objectcompresslevel %s",outputfilename,"pdf",lpdf.getcompression())
+ end)
+ --
end
converter = drivers.converters.lmtx
useddriver = driver
diff --git a/tex/context/base/mkxl/lpdf-vfc.lmt b/tex/context/base/mkxl/lpdf-vfc.lmt
index 65b863203..6030bb028 100644
--- a/tex/context/base/mkxl/lpdf-vfc.lmt
+++ b/tex/context/base/mkxl/lpdf-vfc.lmt
@@ -9,7 +9,9 @@ if not modules then modules = { } end modules ['lpdf-vfc'] = {
local setmetatableindex = table.setmetatableindex
local defaultline = 16384
-local vfspecials = backends.pdf.tables.vfspecials
+
+local vfspecials = backends.pdf.tables.vfspecials or utilities.storage.allocate { }
+backends.pdf.tables.vfspecials = vfspecials
vfspecials.backgrounds = setmetatableindex(function(t,h)
local v = setmetatableindex(function(t,d)
diff --git a/tex/context/base/mkxl/luat-cod.lmt b/tex/context/base/mkxl/luat-cod.lmt
index 91aa6592e..8268c9f64 100644
--- a/tex/context/base/mkxl/luat-cod.lmt
+++ b/tex/context/base/mkxl/luat-cod.lmt
@@ -75,7 +75,7 @@ function lua.registercode(filename,options)
end
end
if barename == filename then
- filename = filename .. (opts.autosuffix and CONTEXTLMTXMODE > 0 and ".lmt" or ".lua")
+ filename = filename .. (opts.autosuffix and ".lmt" or ".lua")
end
local code = environment.luafilechunk(filename,false,opts.optimize)
if code then
diff --git a/tex/context/base/mkxl/meta-fnt.lmt b/tex/context/base/mkxl/meta-fnt.lmt
new file mode 100644
index 000000000..627dbc746
--- /dev/null
+++ b/tex/context/base/mkxl/meta-fnt.lmt
@@ -0,0 +1,263 @@
+if not modules then modules = { } end modules ['meta-fnt'] = {
+ version = 1.001,
+ comment = "companion to meta-fnt.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local next = next
+local concat = table.concat
+local format = string.format
+local formatters = string.formatters
+local chardata = characters.data
+local fontdata = fonts.hashes.identifiers
+
+local vffonts = fonts.handlers.vf
+
+local mpfonts = fonts.mp or { }
+fonts.mp = mpfonts
+
+mpfonts.version = mpfonts.version or 1.20
+mpfonts.inline = true
+mpfonts.cache = containers.define("fonts", "mp", mpfonts.version, true)
+
+metapost.fonts = metapost.fonts or { }
+
+local function unicodetoactualtext(...)
+ unicodetoactualtext = backends.codeinjections.unicodetoactualtext
+ return unicodetoactualtext(...)
+end
+
+-- a few glocals
+
+local characters, descriptions = { }, { }
+local factor, code, slot, width, height, depth, total, variants, bbox, llx, lly, urx, ury = 100, { }, 0, 0, 0, 0, 0, 0, true, 0, 0, 0, 0
+
+local flusher = {
+ startfigure = function(_chr_,_llx_,_lly_,_urx_,_ury_)
+ code = { }
+ slot = _chr_
+ llx = _llx_
+ lly = _lly_
+ urx = _urx_
+ ury = _ury_
+ width = urx - llx
+ height = ury
+ depth = -lly
+ total = total + 1
+ inline = mpfonts.inline
+ end,
+ flushfigure = function(t)
+ for i=1,#t do
+ code[#code+1] = t[i]
+ end
+ end,
+ stopfigure = function()
+ local cd = chardata[n]
+ local code = unicodetoactualtext(slot,concat(code," ")) or ""
+ descriptions[slot] = {
+ -- unicode = slot,
+ name = cd and cd.adobename,
+ width = width * 100,
+ height = height * 100,
+ depth = depth * 100,
+ boundingbox = { llx, lly, urx, ury },
+ }
+ if inline then
+ characters[slot] = {
+ commands = {
+ { "pdf", "origin", code },
+ }
+ }
+ else
+ characters[slot] = {
+ commands = {
+ {
+ "image",
+ {
+ stream = code,
+ bbox = { 0, -depth * 65536, width * 65536, height * 65536 }
+ },
+ },
+ }
+ }
+ end
+ code = nil -- no need to keep that
+ end
+}
+
+local function process(mpxformat,name,instances,scalefactor)
+ local filename = resolvers.findfile(name)
+ local attributes = filename and lfs.isfile(filename) and lfs.attributes(filename)
+ if attributes then
+ statistics.starttiming(metapost.fonts)
+ scalefactor = scalefactor or 1
+ instances = instances or metapost.fonts.instances or 1 -- maybe store in liost too
+ local fontname = file.removesuffix(file.basename(name))
+ local modification = attributes.modification
+ local filesize = attributes.size
+ local hash = file.robustname(formatters["%s %05i %03i"](fontname,scalefactor*1000,instances))
+ local lists = containers.read(mpfonts.cache,hash)
+ if not lists or lists.modification ~= modification or lists.filesize ~= filesize or lists.instances ~= instances or lists.scalefactor ~= scalefactor then
+ statistics.starttiming(flusher)
+ local data = io.loaddata(filename)
+ metapost.reset(mpxformat)
+ metapost.setoutercolor(2) -- no outer color and no reset either
+ lists = { }
+ for i=1,instances do
+ characters = { }
+ descriptions = { }
+ metapost.process {
+ mpx = mpxformat,
+ flusher = flusher,
+ askedfig = "all",
+ -- incontext = false,
+ data = {
+ formatters["randomseed := %s ;"](i*10),
+ formatters["charscale := %s ;"](scalefactor),
+ data,
+ },
+ }
+ lists[i] = {
+ characters = characters,
+ descriptions = descriptions,
+ parameters = {
+ designsize = 655360,
+ slant = 0,
+ space = 333 * scalefactor,
+ space_stretch = 166.5 * scalefactor,
+ space_shrink = 111 * scalefactor,
+ x_height = 431 * scalefactor,
+ quad = 1000 * scalefactor,
+ extra_space = 0,
+ },
+ properties = {
+ name = formatters["%s-%03i"](hash,i),
+ spacer = "space",
+ }
+ }
+ end
+ lists.version = metapost.variables.fontversion or "1.000"
+ lists.modification = modification
+ lists.filesize = filesize
+ lists.instances = instances
+ lists.scalefactor = scalefactor
+ metapost.reset(mpxformat) -- saves memory
+ lists = containers.write(mpfonts.cache, hash, lists)
+ statistics.stoptiming(flusher)
+ end
+ variants = variants + #lists
+ statistics.stoptiming(metapost.fonts)
+ return lists
+ else
+ return { }
+ end
+end
+
+metapost.fonts.flusher = flusher
+metapost.fonts.instances = 1
+metapost.fonts.process = process
+
+local function build(g,v)
+ local size = g.specification.size
+ local data = process(v[2],v[3],v[4],size/655360,v[6])
+ local list = { }
+ local t = { }
+ for d=1,#data do
+ t = fonts.constructors.scale(data[d],-1000)
+ -- local id = font.nextid()
+ -- t.fonts = { { id = id } }
+ fontdata[id] = t
+ if v[5] then
+ vffonts.helpers.composecharacters(t)
+ end
+ list[d] = font.define(t)
+ end
+ for k, v in next, t do -- last t
+ g[k] = v -- kind of replace, when not present, make nil
+ end
+ g.variants = list
+end
+
+vffonts.combiner.commands.metapost = build
+vffonts.combiner.commands.metafont = build
+
+statistics.register("metapost font generation", function()
+ if total > 0 then
+ local time = statistics.elapsedtime(flusher)
+ if total > 0 then
+ return format("%i glyphs, %s seconds runtime, %.1f glyphs/second", total, time, total/tonumber(time))
+ else
+ return format("%i glyphs, %s seconds runtime", total, time)
+ end
+ end
+end)
+
+statistics.register("metapost font loading",function()
+ if variants > 0 then
+ local time = statistics.elapsedtime(metapost.fonts)
+ if variants > 0 then
+ return format("%s seconds, %i instances, %.3f instances/second", time, variants, variants/tonumber(time))
+ else
+ return format("%s seconds, %i instances", time, variants)
+ end
+ end
+end)
+
+-- fonts.definers.methods.install( "bidi", {
+-- {
+-- "metapost", -- method
+-- "metafun", -- format
+-- "fontoeps.mp", -- filename
+-- 1, -- instances
+-- false, -- compose
+-- },
+-- } )
+
+local report = logs.reporter("metapost","fonts")
+
+function metapost.fonts.define(specification)
+ local fontname = specification.fontname or ""
+ local filename = specification.filename or ""
+ local format = specification.format or "metafun"
+ if fontname == "" then
+ report("no fontname given")
+ return
+ end
+ if filename == "" then
+ report("no filename given for %a",fontname)
+ return
+ end
+ local fullname = resolvers.findfile(filename)
+ if fullname == "" then
+ report("unable to locate file %a",filename)
+ return
+ end
+ report("generating font %a using format %a and file %a",fontname,format,filename)
+ fonts.definers.methods.install(fontname, {
+ {
+ specification.engine or "metapost",
+ format,
+ filename,
+ specification.instances or 1,
+ specification.compose or false,
+ },
+ } )
+end
+
+interfaces.implement {
+ name = "definemetafont",
+ actions = metapost.fonts.define,
+ arguments = {
+ {
+ { "fontname" },
+ { "filename" },
+ }
+ }
+}
+
+-- metapost.fonts.define {
+-- fontname = "bidi",
+-- filename = "bidi-symbols.mp",
+-- }
diff --git a/tex/context/base/mkxl/meta-fnt.mkxl b/tex/context/base/mkxl/meta-fnt.mkxl
index 690571c79..9efee0ee5 100644
--- a/tex/context/base/mkxl/meta-fnt.mkxl
+++ b/tex/context/base/mkxl/meta-fnt.mkxl
@@ -13,7 +13,7 @@
\writestatus{loading}{MetaPost Graphics / Fonts}
-\registerctxluafile{meta-fnt}{}
+\registerctxluafile{meta-fnt}{autosuffix}
\unprotect
diff --git a/tex/context/base/mkxl/node-acc.lmt b/tex/context/base/mkxl/node-acc.lmt
new file mode 100644
index 000000000..2c3302a3e
--- /dev/null
+++ b/tex/context/base/mkxl/node-acc.lmt
@@ -0,0 +1,112 @@
+if not modules then modules = { } end modules ['node-acc'] = {
+ 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"
+}
+
+-- see mkiv lua module for some experimental unused code
+
+local nodes, node = nodes, node
+
+local tasks = nodes.tasks
+
+local nuts = nodes.nuts
+local tonut = nodes.tonut
+local tonode = nodes.tonode
+
+local getid = nuts.getid
+local getsubtype = nuts.getsubtype
+local getattr = nuts.getattr
+local getlist = nuts.getlist
+local getchar = nuts.getchar
+local getnext = nuts.getnext
+
+local setattr = nuts.setattr
+local setlink = nuts.setlink
+local setchar = nuts.setchar
+local setsubtype = nuts.setsubtype
+local getwidth = nuts.getwidth
+local setwidth = nuts.setwidth
+
+local nextglyph = nuts.traversers.glyph
+local nextnode = nuts.traversers.node
+
+local copy_node = nuts.copy
+local insert_after = nuts.insert_after
+
+local nodecodes = nodes.nodecodes
+local gluecodes = nodes.gluecodes
+
+local glue_code = nodecodes.glue
+local glyph_code = nodecodes.glyph
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+
+local userskip_code = gluecodes.user
+local spaceskip_code = gluecodes.spaceskip
+local xspaceskip_code = gluecodes.xspaceskip
+
+local a_characters = attributes.private("characters")
+
+local nofreplaced = 0
+
+-- todo: nbsp etc
+-- todo: collapse kerns (not needed, backend does this)
+-- todo: maybe cache as we now create many nodes
+-- todo: check for subtype related to spacing (13/14 but most seems to be user anyway)
+
+local trace = false trackers.register("backend.spaces", function(v) trace = v end)
+local slot = nil
+
+local function injectspaces(head)
+ -- This can become two fast loops or we just move this to the backend where we can
+ -- also check for spaces (it actually is rather old code that relates to tagging
+ -- and so, which was implemented rather early in the mkiv saga).
+ local p, p_id
+ local n = head
+ while n do
+ local id = getid(n)
+ if id == glue_code then
+ if p and getid(p) == glyph_code then
+ local s = getsubtype(n)
+ if s == spaceskip_code or s == xspaceskip_code then
+ local g = copy_node(p)
+ local a = getattr(n,a_characters)
+ setchar(g,slot)
+ setlink(p,g,n)
+ setwidth(n,getwidth(n) - getwidth(g))
+ if a then
+ setattr(g,a_characters,a)
+ end
+ setattr(n,a_characters,0)
+ nofreplaced = nofreplaced + 1
+ end
+ end
+ elseif id == hlist_code or id == vlist_code then
+ injectspaces(getlist(n),slot)
+ end
+ p_id = id
+ p = n
+ n = getnext(n)
+ end
+ return head
+end
+
+nodes.handlers.accessibility = function(head)
+ if trace then
+ if not slot then
+ slot = fonts.helpers.privateslot("visualspace")
+ end
+ else
+ slot = 32
+ end
+ return injectspaces(head,slot)
+end
+
+statistics.register("inserted spaces in output",function()
+ if nofreplaced > 0 then
+ return nofreplaced
+ end
+end)
diff --git a/tex/context/base/mkxl/node-ini.mkxl b/tex/context/base/mkxl/node-ini.mkxl
index 84f46e546..abdff7b5b 100644
--- a/tex/context/base/mkxl/node-ini.mkxl
+++ b/tex/context/base/mkxl/node-ini.mkxl
@@ -19,7 +19,7 @@
\registerctxluafile{node-cmp}{autosuffix}
\registerctxluafile{node-ini}{autosuffix}
-\registerctxluafile{node-met}{}
+\registerctxluafile{node-met}{autosuffix}
\registerctxluafile{node-nut}{autosuffix}
\registerctxluafile{node-res}{autosuffix}
%registerctxluafile{node-ppt}{} % experimental, not used so probably useless
@@ -34,7 +34,7 @@
\registerctxluafile{node-pro}{}
\registerctxluafile{node-ser}{autosuffix}
\registerctxluafile{node-ext}{}
-\registerctxluafile{node-acc}{} % experimental
+\registerctxluafile{node-acc}{autosuffix} % experimental
%registerctxluafile{node-prp}{} % makes no sense (yet)
\registerctxluafile{node-scn}{autosuffix}
\registerctxluafile{node-syn}{}
diff --git a/tex/context/base/mkxl/node-met.lmt b/tex/context/base/mkxl/node-met.lmt
new file mode 100644
index 000000000..a3f02d709
--- /dev/null
+++ b/tex/context/base/mkxl/node-met.lmt
@@ -0,0 +1,633 @@
+if not modules then modules = { } end modules ['node-MET'] = {
+ 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 is an experimental module. Don't use nuts for generic code, at least not till
+-- the regular code is proven stable. No support otherwise.
+
+-- luatex: todo: copylist should return h, t
+-- todo: see if using insert_before and insert_after makes sense here
+
+-- This file is a side effect of the \LUATEX\ speed optimization project of Luigi
+-- Scarso and me. As \CONTEXT\ spends over half its time in \LUA, we though that
+-- using \LUAJIT\ could improve performance. We've published some of our experiences
+-- elsewhere, but to summarize: \LUAJITTEX\ benefits a lot from the faster virtual
+-- machine, but when jit is turned of we loose some again. We experimented with
+-- ffi (without messing up the \CONTEXT\ code too much) but there we also lost more
+-- than we gained (mostly due to lack of compatible userdata support: it's all or
+-- nothing). This made us decide to look into the \LUA||\TEX\ interfacing and by
+-- profiling and careful looking at the (of course then still beta source code) we
+-- could come up with some improvements. The first showed up in 0.75 and we've more
+-- on the agenda for 0.80. Although some interfaces could be sped up significantly
+-- in practice we're only talking of 5||10\% on a \CONTEXT\ run and maybe more when
+-- complex and extensive node list manipulations happens (we're talking of hundreds
+-- of millions cross boundary calls then for documents of hundreds pages). One of the
+-- changes in the \CONTEXT\ code base is that we went from indexed access to nodes to
+-- function calls (in principle faster weren't it that the accessors need to do more
+-- checking which makes them slower) and from there to optimizing these calls as well
+-- as providing fast variants for well defined situations. At first optimizations were
+-- put in a separate \type {node.fast} table although some improvements could be
+-- ported to the main node functions. Because we got the feeling that more gain was
+-- possible (especially when using more complex fonts and \MKIV\ functionality) we
+-- eventually abandoned this approach and dropped the \type {fast} table in favour of
+-- another hack. In the process we had done lots of profiling and testing so we knew
+-- where time was wasted,
+--
+-- As lots of testing and experimenting was part of this project, I could not have
+-- done without stacks of new \CD s and \DVD s. This time Porcupine Tree, No-Man
+-- and Archive were came to rescue.
+--
+-- It all started with testing performance of:
+--
+-- node.getfield = metatable.__index
+-- node.setfield = metatable.__newindex
+
+local type, select = type, select
+local setmetatableindex = table.setmetatableindex
+
+-- First we get the metatable of a node:
+
+local metatable = nil
+
+do
+ local glyph = node.new("glyph",0)
+ metatable = getmetatable(glyph)
+ node.free(glyph)
+end
+
+-- statistics.tracefunction(node, "node", "getfield","setfield")
+-- statistics.tracefunction(node.direct,"node.direct","getfield","setfield")
+
+-- We start with some helpers and provide all relevant basic functions in the
+-- node namespace as well.
+
+nodes = nodes or { }
+local nodes = nodes
+
+local nodecodes = nodes.nodecodes
+
+nodes.tostring = node.tostring or tostring
+nodes.copy = node.copy
+nodes.copy_node = node.copy
+nodes.copy_list = node.copy_list
+nodes.delete = node.delete
+nodes.dimensions = node.dimensions
+nodes.rangedimensions = node.rangedimensions
+nodes.end_of_math = node.end_of_math
+nodes.flush = node.flush_node
+nodes.flush_node = node.flush_node
+nodes.flush_list = node.flush_list
+nodes.free = node.free
+nodes.insert_after = node.insert_after
+nodes.insert_before = node.insert_before
+nodes.hpack = node.hpack
+nodes.new = node.new
+nodes.tail = node.tail
+nodes.traverse = node.traverse
+nodes.traverse_id = node.traverse_id
+nodes.traverse_char = node.traverse_char
+nodes.traverse_glyph = node.traverse_glyph
+nodes.traverse_list = node.traverse_list
+nodes.slide = node.slide
+nodes.vpack = node.vpack
+nodes.fields = node.fields
+nodes.is_node = node.is_node
+nodes.setglue = node.setglue
+nodes.uses_font = node.uses_font
+
+nodes.first_glyph = node.first_glyph
+nodes.has_glyph = node.has_glyph or node.first_glyph
+
+nodes.current_attributes = node.current_attributes or node.current_attr
+nodes.has_field = node.has_field
+nodes.last_node = node.last_node
+nodes.usedlist = node.usedlist
+nodes.protrusion_skippable = node.protrusion_skippable
+nodes.check_discretionaries = node.check_discretionaries
+nodes.write = node.write
+nodes.flatten_discretionaries = node.flatten_discretionaries
+
+nodes.count = node.count
+nodes.length = node.length
+
+nodes.has_attribute = node.has_attribute
+nodes.set_attribute = node.set_attribute
+nodes.find_attribute = node.find_attribute
+nodes.unset_attribute = node.unset_attribute
+
+nodes.protect_glyph = node.protect_glyph
+nodes.protect_glyphs = node.protect_glyphs
+nodes.unprotect_glyph = node.unprotect_glyph
+nodes.unprotect_glyphs = node.unprotect_glyphs
+nodes.kerning = node.kerning
+nodes.ligaturing = node.ligaturing
+nodes.hyphenating = node.hyphenating
+nodes.mlist_to_hlist = node.mlist_to_hlist
+
+nodes.effective_glue = node.effective_glue
+nodes.getglue = node.getglue
+nodes.setglue = node.setglue
+nodes.is_zero_glue = node.is_zero_glue
+
+nodes.tonode = function(n) return n end
+nodes.tonut = function(n) return n end
+
+-- These are never used in \CONTEXT, only as a gimmick in node operators
+-- so we keep them around.
+--
+-- Fro nwo I keep them in \LMTX\ but they will go away!
+
+local n_getfield = node.getfield
+local n_getattr = node.get_attribute
+
+local n_setfield = node.setfield
+local n_setattr = n_setfield
+
+nodes.getfield = n_getfield
+nodes.setfield = n_setfield
+nodes.getattr = n_getattr
+nodes.setattr = n_setattr
+nodes.takeattr = nodes.unset_attribute
+
+local function n_getid (n) return n_getfield(n,"id") end
+local function n_getsubtype(n) return n_getfield(n,"subtype") end
+
+nodes.getid = n_getid
+nodes.getsubtype = n_getsubtype
+
+local function n_getchar(n) return n_getfield(n,"char") end
+local function n_setchar(n,c) return n_setfield(n,"char",c) end
+local function n_getfont(n) return n_getfield(n,"font") end
+local function n_setfont(n,f) return n_setfield(n,"font",f) end
+
+nodes.getchar = n_getchar
+nodes.setchar = n_setchar
+nodes.getfont = n_getfont
+nodes.setfont = n_setfont
+
+local function n_getlist (n) return n_getfield(n,"list") end
+local function n_setlist (n,l) return n_setfield(n,"list",l) end
+local function n_getleader(n) return n_getfield(n,"leader") end
+local function n_setleader(n,l) return n_setfield(n,"leader",l) end
+
+nodes.getlist = n_getlist
+nodes.setlist = n_setlist
+nodes.getleader = n_getleader
+nodes.setleader = n_setleader
+
+local function n_getnext(n) return n_getfield(n,"next") end
+local function n_setnext(n,nn) return n_setfield(n,"next",nn) end
+local function n_getprev(n) return n_getfield(n,"prev") end
+local function n_setprev(n,pp) return n_setfield(n,"prev",pp) end
+local function n_getboth(n) return n_getfield(n,"prev"), n_getfield(n,"next") end
+local function n_setboth(n,pp,nn) return n_setfield(n,"prev",pp), n_setfield(n,"next",nn) end
+
+nodes.getnext = n_getnext
+nodes.setnext = n_setnext
+nodes.getprev = n_getprev
+nodes.setprev = n_setprev
+nodes.getboth = n_getboth
+nodes.setboth = n_setboth
+
+local function n_setlink(...)
+ -- not that fast but not used often anyway
+ local h = nil
+ for i=1,select("#",...) do
+ local n = select(i,...)
+ if not n then
+ -- go on
+ elseif h then
+ n_setfield(h,"next",n)
+ n_setfield(n,"prev",h)
+ else
+ h = n
+ end
+ end
+ return h
+end
+
+nodes.setlink = n_setlink
+
+nodes.getbox = node.getbox or tex.getbox
+nodes.setbox = node.setbox or tex.setbox
+
+local n_flush_node = nodes.flush
+local n_copy_node = nodes.copy
+local n_copy_list = nodes.copy_list
+local n_find_tail = nodes.tail
+local n_insert_after = nodes.insert_after
+local n_insert_before = nodes.insert_before
+local n_slide = nodes.slide
+
+local n_remove_node = node.remove -- not yet nodes.remove
+
+local function remove(head,current,free_too)
+ local t = current
+ head, current = n_remove_node(head,current)
+ if not t then
+ -- forget about it
+ elseif free_too then
+ n_flush_node(t)
+ t = nil
+ else
+ n_setboth(t)
+ end
+ return head, current, t
+end
+
+nodes.remove = remove
+
+function nodes.delete(head,current)
+ return remove(head,current,true)
+end
+
+-- local h, c = nodes.replace(head,current,new)
+-- local c = nodes.replace(false,current,new)
+-- local c = nodes.replace(current,new)
+--
+-- todo: check for new.next and find tail
+
+function nodes.replace(head,current,new) -- no head returned if false
+ if not new then
+ head, current, new = false, head, current
+-- current, new = head, current
+ end
+ local prev = n_getprev(current)
+ local next = n_getnext(current)
+ if next then
+ n_setlink(new,next)
+ end
+ if prev then
+ n_setlink(prev,new)
+ end
+ if head then
+ if head == current then
+ head = new
+ end
+ n_flush_node(current)
+ return head, new
+ else
+ n_flush_node(current)
+ return new
+ end
+end
+
+-- nodes.countall : see node-nut.lua
+
+function nodes.append(head,current,...)
+ for i=1,select("#",...) do
+ head, current = n_insert_after(head,current,(select(i,...)))
+ end
+ return head, current
+end
+
+function nodes.prepend(head,current,...)
+ for i=1,select("#",...) do
+ head, current = n_insert_before(head,current,(select(i,...)))
+ end
+ return head, current
+end
+
+function nodes.linked(...)
+ local head, last
+ for i=1,select("#",...) do
+ local next = select(i,...)
+ if next then
+ if head then
+ n_setlink(last,next)
+ else
+ head = next
+ end
+ last = n_find_tail(next) -- we could skip the last one
+ end
+ end
+ return head
+end
+
+function nodes.concat(list) -- consider tail instead of slide
+ local head, tail
+ for i=1,#list do
+ local li = list[i]
+ if li then
+ if head then
+ n_setlink(tail,li)
+ else
+ head = li
+ end
+ tail = n_slide(li)
+ end
+ end
+ return head, tail
+end
+
+function nodes.reference(n)
+ return n and tonut(n) or "<none>"
+end
+
+-- Here starts an experiment with metatables. Of course this only works with nodes
+-- wrapped in userdata with a metatable.
+--
+-- Nodes are kind of special in the sense that you need to keep an eye on creation
+-- and destruction. This is quite natural if you consider that changing the content
+-- of a node would also change any copy (or alias). As there are too many pitfalls
+-- we don't have this kind of support built in \LUATEX, which means that macro
+-- packages are free to provide their own. One can even use local variants.
+--
+-- n1 .. n2 : append nodes, no copies
+-- n1 * 5 : append 4 copies of nodes
+-- 5 + n1 : strip first 5 nodes
+-- n1 - 5 : strip last 5 nodes
+-- n1 + n2 : inject n2 after first of n1
+-- n1 - n2 : inject n2 before last of n1
+-- n1^2 : two copies of nodes (keep orginal)
+-- - n1 : reverse nodes
+-- n1/f : apply function to nodes
+
+-- local s = nodes.typesetters.tonodes
+--
+-- local function w(lst)
+-- context.dontleavehmode()
+-- context(lst)
+-- context.par()
+-- end
+--
+-- local n1 = s("a")
+-- local n2 = s("b")
+-- local n3 = s("c")
+-- local n4 = s("d")
+-- local n5 = s("e")
+-- local n6 = s("f")
+-- local n7 = s("g")
+--
+-- local n0 = n1 .. (n2 * 10).. n3 .. (5 * n4) .. n5 .. ( 5 * n6 ) .. n7 / function(n) n.char = string.byte("!") return n end
+--
+-- w(#n0)
+--
+-- w(n0)
+--
+-- local n1 = s("a") * 10
+-- local n2 = s("b") * 10
+--
+-- local n0 = ((5 + n1) .. (n2 - 5) )
+-- local n0 = - n0
+--
+-- local n0 = nil .. n0^3 .. nil
+--
+-- w(n0)
+--
+-- w ( s("a") + s("b") ) w ( s("a") + 4*s("b") ) w ( 4*s("a") + s("b") ) w ( 4*s("a") + 4*s("b") )
+-- w ( s("a") - s("b") ) w ( s("a") - 4*s("b") ) w ( 4*s("a") - s("b") ) w ( 4*s("a") - 4*s("b") )
+
+local n_remove_node = nodes.remove
+
+metatable.__concat = function(n1,n2) -- todo: accept nut on one end
+ if not n1 then
+ return n2
+ elseif not n2 then
+ return n1
+ elseif n1 == n2 then
+ -- or abort
+ return n2 -- or n2 * 2
+ else
+ local tail = n_find_tail(n1)
+ n_setlink(tail,n2)
+ return n1
+ end
+end
+
+metatable.__mul = function(n,multiplier)
+ if type(multiplier) ~= "number" then
+ n, multiplier = multiplier, n
+ end
+ if multiplier <= 1 then
+ return n
+ elseif n_getnext(n) then
+ local head
+ for i=2,multiplier do
+ local h = n_copy_list(n)
+ if head then
+ local t = n_find_tail(h)
+ n_setlink(t,head)
+ end
+ head = h
+ end
+ local t = n_find_tail(n)
+ n_setlink(t,head)
+ else
+ local head
+ for i=2,multiplier do
+ local c = n_copy_node(n)
+ if head then
+ n_setlink(c,head)
+ end
+ head = c
+ end
+ n_setlink(n,head)
+ end
+ return n
+end
+
+metatable.__sub = function(first,second)
+ if type(second) == "number" then
+ local tail = n_find_tail(first)
+ for i=1,second do
+ local prev = n_getprev(tail)
+ n_flush_node(tail) -- can become flushlist/flushnode
+ if prev then
+ tail = prev
+ else
+ return nil
+ end
+ end
+ if tail then
+ n_setnext(tail)
+ return first
+ else
+ return nil
+ end
+ else
+ -- aaaaa - bbb => aaaabbba
+ local firsttail = n_find_tail(first)
+ local prev = n_getprev(firsttail)
+ if prev then
+ local secondtail = n_find_tail(second)
+ n_setlink(secondtail,firsttail)
+ n_setlink(prev,second)
+ return first
+ else
+ local secondtail = n_find_tail(second)
+ n_setlink(secondtail,first)
+ return second
+ end
+ end
+end
+
+metatable.__add = function(first,second)
+ if type(first) == "number" then
+ local head = second
+ for i=1,first do
+ local second = n_getnext(head)
+ n_flush_node(head) -- can become flushlist/flushnode
+ if second then
+ head = second
+ else
+ return nil
+ end
+ end
+ if head then
+ n_setprev(head)
+ return head
+ else
+ return nil
+ end
+ else
+ -- aaaaa + bbb => abbbaaaa
+ local next = n_getnext(first)
+ if next then
+ local secondtail = n_find_tail(second)
+ n_setlink(first,second)
+ n_setlink(secondtail,next)
+ else
+ n_setlink(first,second)
+ end
+ return first
+ end
+end
+
+metatable.__len = function(current)
+ local length = 0
+ while current do
+ current = n_getnext(current)
+ length = length + 1
+ end
+ return length
+end
+
+metatable.__div = function(list,action)
+ return action(list) or list -- always a value
+end
+
+metatable.__pow = function(n,multiplier)
+ local tail = n
+ local head = nil
+ if n_getnext(n) then
+ if multiplier == 1 then
+ head = n_copy_list(n)
+ else
+ for i=1,multiplier do
+ local h = n_copy_list(n)
+ if head then
+ local t = n_find_tail(h)
+ n_setlink(t,head)
+ end
+ head = h
+ end
+ end
+ else
+ if multiplier == 1 then
+ head = n_copy_node(n)
+ else
+ for i=2,multiplier do
+ local c = n_copy_node(n)
+ if head then
+ n_setlink(head,c)
+ end
+ head = c
+ end
+ end
+ end
+ -- todo: tracing
+ return head
+end
+
+metatable.__unm = function(head)
+ local last = head
+ local first = head
+ local current = n_getnext(head)
+ while current do
+ local next = n_getnext(current)
+ n_setlink(current,first)
+ first = current
+ current = next
+ end
+ n_setprev(first)
+ n_setnext(last)
+ return first
+end
+
+-- see node-nut.lua for more info on going nuts
+
+-- if not gonuts then
+--
+-- local nuts = { }
+-- nodes.nuts = nuts
+--
+-- local function dummy(f) return f end
+--
+-- nodes.vianuts = dummy
+-- nodes.vianodes = dummy
+--
+-- for k, v in next, nodes do
+-- if type(v) == "function" then
+-- nuts[k] = v
+-- end
+-- end
+--
+-- end
+
+-- also handy
+
+local tonode = nodes.tonode
+local whatsit_code = nodecodes.whatsit
+local getfields = node.fields
+local sort = table.sort
+local whatsitkeys = { }
+local keys = { whatsit = whatsitkeys }
+local messyhack = table.tohash { -- temporary solution
+ nodecodes.attributelist,
+ nodecodes.attribute,
+ nodecodes.action, -- hm
+}
+
+setmetatableindex(keys,function(t,k)
+ local v = (k == "attributelist" or k == nodecodes.attributelist) and { } or getfields(k)
+ if messyhack[k] then
+ for i=1,#v do
+ if v[i] == "subtype" then
+ remove(v,i)
+ break
+ end
+ end
+ end
+ if v[ 0] then v[#v+1] = "next" v[ 0] = nil end
+ if v[-1] then v[#v+1] = "prev" v[-1] = nil end
+ sort(v)
+ t[k] = v
+ return v
+end)
+
+setmetatableindex(whatsitkeys,function(t,k)
+ local v = getfields(whatsit_code,k)
+ if v[ 0] then v[#v+1] = "next" v[ 0] = nil end
+ if v[-1] then v[#v+1] = "prev" v[-1] = nil end
+ sort(v)
+ t[k] = v
+ return v
+end)
+
+local function nodefields(n)
+ n = tonode(n)
+ local id = n.id
+ if id == whatsit_code then
+ return whatsitkeys[n.subtype]
+ else
+ return keys[id]
+ end
+end
+
+nodes.keys = keys -- [id][subtype]
+nodes.fields = nodefields -- (n)
diff --git a/tex/context/base/mkxl/node-ref.lmt b/tex/context/base/mkxl/node-ref.lmt
index 06c9981a9..c02a37dd1 100644
--- a/tex/context/base/mkxl/node-ref.lmt
+++ b/tex/context/base/mkxl/node-ref.lmt
@@ -55,7 +55,6 @@ local nodepool = nuts.pool
local tonode = nuts.tonode
local tonut = nuts.tonut
-local getfield = nuts.getfield
local setlink = nuts.setlink
local setnext = nuts.setnext
local setprev = nuts.setprev
diff --git a/tex/context/base/mkxl/node-res.lmt b/tex/context/base/mkxl/node-res.lmt
index c57e5cfd0..7a37b1b9e 100644
--- a/tex/context/base/mkxl/node-res.lmt
+++ b/tex/context/base/mkxl/node-res.lmt
@@ -578,28 +578,6 @@ end)
lua.registerfinalizer(cleanup, "cleanup reserved nodes")
--- experiment
-
-do
-
- local glyph = tonode(glyph)
- local traverse_id = nodes.traverse_id
-
- local traversers = table.setmetatableindex(function(t,k)
- local v = traverse_id(type(k) == "number" and k or nodecodes[k],glyph)
- t[k] = v
- return v
- end)
-
- traversers.node = nodes.traverse (glyph)
- traversers.char = nodes.traverse_char (glyph)
- if nuts.traverse_glyph then traversers.glyph = nodes.traverse_glyph (glyph) end
- if nuts.traverse_list then traversers.list = nodes.traverse_list (glyph) end
-
- nodes.traversers = traversers
-
-end
-
do
local glyph = glyph
@@ -611,11 +589,13 @@ do
return v
end)
- traversers.node = nuts.traverse (glyph)
- traversers.char = nuts.traverse_char (glyph)
- if nuts.traverse_glyph then traversers.glyph = nuts.traverse_glyph (glyph) end
- if nuts.traverse_list then traversers.list = nuts.traverse_list (glyph) end
- if nuts.traverse_content then traversers.content = nuts.traverse_content(glyph) end
+ -- these are special:
+
+ traversers.node = nuts.traverse (glyph)
+ traversers.char = nuts.traverse_char (glyph)
+ traversers.glyph = nuts.traverse_glyph (glyph)
+ traversers.list = nuts.traverse_list (glyph)
+ traversers.content = nuts.traverse_content(glyph)
nuts.traversers = traversers
diff --git a/tex/context/base/mkxl/node-rul.lmt b/tex/context/base/mkxl/node-rul.lmt
index 99c5d98a4..a95c5272d 100644
--- a/tex/context/base/mkxl/node-rul.lmt
+++ b/tex/context/base/mkxl/node-rul.lmt
@@ -51,9 +51,6 @@ local getwidth = nuts.getwidth
local setwidth = nuts.setwidth
local setoffsets = nuts.setoffsets
local setfield = nuts.setfield
-
------ getfield = nuts.getfield
------ getdata = nuts.getdata
local getruledata = nuts.getruledata
local isglyph = nuts.isglyph
diff --git a/tex/context/base/mkxl/node-ser.lmt b/tex/context/base/mkxl/node-ser.lmt
index 0d7f3ccad..8fcbb31e4 100644
--- a/tex/context/base/mkxl/node-ser.lmt
+++ b/tex/context/base/mkxl/node-ser.lmt
@@ -18,7 +18,6 @@ local node = node
local getfields = node.fields
-local traverse = nodes.traverse
local is_node = nodes.is_node
local nodecodes = nodes.nodecodes
diff --git a/tex/context/base/mkxl/node-tra.lmt b/tex/context/base/mkxl/node-tra.lmt
index 7b401361f..f985652c5 100644
--- a/tex/context/base/mkxl/node-tra.lmt
+++ b/tex/context/base/mkxl/node-tra.lmt
@@ -181,18 +181,8 @@ end
nodes.tosequence = tosequence
nuts .tosequence = tosequence
-if CONTEXTLMTXMODE > 0 then
-
- function nodes.report(t)
- report_nodes("output %a, %s nodes",tex.getoutputactive(),count_nodes(t))
- end
-
-else
-
- function nodes.report(t)
- report_nodes("output %a, %s nodes",status.output_active,count_nodes(t))
- end
-
+function nodes.report(t)
+ report_nodes("output %a, %s nodes",tex.getoutputactive(),count_nodes(t))
end
function nodes.packlist(head)
diff --git a/tex/context/base/mkxl/strc-tag.lmt b/tex/context/base/mkxl/strc-tag.lmt
index 11049abe0..96510f24e 100644
--- a/tex/context/base/mkxl/strc-tag.lmt
+++ b/tex/context/base/mkxl/strc-tag.lmt
@@ -263,11 +263,11 @@ function tags.locatedtag(tag)
return false -- handy as bogus index
end
-function structures.atlocation(str)
+function structures.atlocation(str) -- not used
local specification = taglist[texgetattribute(a_tagged)]
if specification then
+ local list = specification.taglist
if list then
- local taglist = specification.taglist
local pattern = patterns[str]
for i=#list,1,-1 do
if find(list[i],pattern) then
diff --git a/tex/context/base/mkxl/supp-box.lmt b/tex/context/base/mkxl/supp-box.lmt
index 53ad3ae31..b4b0d44cc 100644
--- a/tex/context/base/mkxl/supp-box.lmt
+++ b/tex/context/base/mkxl/supp-box.lmt
@@ -39,7 +39,6 @@ local nuts = nodes.nuts
local tonut = nuts.tonut
local tonode = nuts.tonode
------ getfield = nuts.getfield
local getnext = nuts.getnext
local getprev = nuts.getprev
local getboth = nuts.getboth
@@ -56,7 +55,6 @@ local getdepth = nuts.getdepth
local getwhd = nuts.getwhd
local takebox = nuts.takebox
------ setfield = nuts.setfield
local setlink = nuts.setlink
local setboth = nuts.setboth
local setnext = nuts.setnext
@@ -153,19 +151,6 @@ implement {
end
}
--- local function hyphenatedhack(head,pre)
--- pre = tonut(pre)
--- for n in nextdisc, tonut(head) do
--- local hyphen = getfield(n,"pre")
--- if hyphen then
--- flush_list(hyphen)
--- end
--- setfield(n,"pre",copy_list(pre))
--- end
--- end
---
--- commands.hyphenatedhack = hyphenatedhack
-
local function checkedlist(list)
if type(list) == "number" then
return getlist(getbox(tonut(list)))
diff --git a/tex/context/base/mkxl/syst-aux.mkxl b/tex/context/base/mkxl/syst-aux.mkxl
index 7a60e63bb..7b1b889c3 100644
--- a/tex/context/base/mkxl/syst-aux.mkxl
+++ b/tex/context/base/mkxl/syst-aux.mkxl
@@ -5216,8 +5216,8 @@
%D \stripspaces\from\one\to\two
%D \stoptyping
%D
-%D Both the old string \type{\one} and the new one \type{\two}
-%D are expanded. This command is a special case of:
+%D Both the old string \type {\one} and the new one \type {\two} are expanded. This
+%D command is a special case of:
%D
%D \starttyping
%D \stripcharacter\char\from\one\to\two
@@ -5745,9 +5745,8 @@
%D \macros
%D {twodigitrounding}
%D
-%D When using \type {\special}s or \type {\pdfliteral}s, it sometimes makes sense to
-%D limit the precission. The next macro rounds a real number to two digits. It takes
-%D one argument and only works in \ETEX.
+%D The next macro rounds a real number to two digits. They are probably no longer needed
+%D but we keep them around for a while.
\permanent\def\integerrounding #1{\clf_rounded\zerocount\numexpr#1\relax}
\permanent\def\onedigitrounding #1{\clf_rounded\plusone \numexpr#1\relax}
diff --git a/tex/context/base/mkxl/toks-scn.lmt b/tex/context/base/mkxl/toks-scn.lmt
index 93e0af09a..73eedbba7 100644
--- a/tex/context/base/mkxl/toks-scn.lmt
+++ b/tex/context/base/mkxl/toks-scn.lmt
@@ -22,7 +22,7 @@ local tokenbits = tokens.bits
local scanstring = scanners.string
local scanargument = scanners.argument
-local scandelimited = scanners.delimited -- lmtx
+local scandelimited = scanners.delimited
local scanverbatim = scanners.verbatim
local scantokenlist = scanners.tokenlist
local scantoks = scanners.toks
@@ -176,8 +176,6 @@ function scanners.whd()
end
end
--- begin lmtx
-
local l = utf.byte("[")
local r = utf.byte("]")
@@ -209,8 +207,6 @@ scanners.optional = scanoptional
scanners.bracketedasis = scanbracketedasis
scanners.argumentasis = scanargumentasis
--- end lmtx
-
local shortcuts = {
tokens = tokens,
bits = tokenbits,
@@ -247,21 +243,19 @@ local shortcuts = {
scanclose = scanclose,
scanlist = scanlist,
scancsname = scancsname,
- todimen = todimen,
- tonumber = tonumber,
- tostring = tostring,
- toboolean = toboolean,
- inspect = inspect,
- report = report_scan,
- -- lmtx
scandelimited = scandelimited, -- not directly useable
scanbracketed = scanbracketed,
scanoptional = scanoptional,
scanbracketedasis = scanbracketedasis,
scanargumentasis = scanargumentasis,
- --
scanintegerargument = scanintegerargument,
scandimenargument = scandimenargument,
+ todimen = todimen,
+ tonumber = tonumber,
+ tostring = tostring,
+ toboolean = toboolean,
+ inspect = inspect,
+ report = report_scan,
}
tokens.shortcuts = shortcuts
@@ -299,10 +293,8 @@ local f_elseif = formatters[" elseif scankeywordcs('%s') then data['%s'] = sc
----- f_if_x = formatters[ " if not data['%s'] and scankeywordcs('%s') then data['%s'] = scan%s()"]
----- f_elseif_x = formatters[" elseif not data['%s'] and scankeywordcs('%s') then data['%s'] = scan%s()"]
--- if CONTEXTLMTXMODE > 0 then
--- f_if = formatters[" local key = scanletters() if key == '' then break elseif key == '%s' then data['%s'] = scan%s()"]
--- f_elseif = formatters[" elseif key == '%s' then data['%s'] = scan%s()"]
--- end
+----- f_if = formatters[" local key = scanletters() if key == '' then break elseif key == '%s' then data['%s'] = scan%s()"]
+----- f_elseif = formatters[" elseif key == '%s' then data['%s'] = scan%s()"]
local f_local = formatters["local scan%s = scanners.%s"]
local f_scan = formatters["scan%s()"]
@@ -314,10 +306,8 @@ local f_scan_c = formatters["%s(scan%s())"]
-- see above
--- if CONTEXTLMTXMODE > 0 then
--- f_if_c = formatters[" local key = scanletters() if key == '' then break elseif key == '%s' then data['%s'] = %s(scan%s())"]
--- f_elseif_c = formatters[" elseif k == '%s' then data['%s'] = %s(scan%s())"]
--- end
+----- f_if_c = formatters[" local key = scanletters() if key == '' then break elseif key == '%s' then data['%s'] = %s(scan%s())"]
+----- f_elseif_c = formatters[" elseif k == '%s' then data['%s'] = %s(scan%s())"]
local f_any = formatters[" else local key = scanword(true) if key then data[key] = scan%s() else break end end"]
local f_any_c = formatters[" else local key = scanword(true) if key then data[key] = %s(scan%s()) else break end end"]
diff --git a/tex/context/base/mkxl/typo-chr.lmt b/tex/context/base/mkxl/typo-chr.lmt
new file mode 100644
index 000000000..bb11f54a6
--- /dev/null
+++ b/tex/context/base/mkxl/typo-chr.lmt
@@ -0,0 +1,291 @@
+if not modules then modules = { } end modules ['typo-chr'] = {
+ version = 1.001,
+ comment = "companion to typo-bld.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- A historic intermediate version can be found in the mkiv variant. The code
+-- can probably be improved but performance etc is not really an issue here.
+
+local insert, remove = table.insert, table.remove
+
+local context = context
+local ctx_doifelse = commands.doifelse
+
+local nodecodes = nodes.nodecodes
+local boundarycodes = nodes.boundarycodes
+local subtypes = nodes.subtypes
+
+local glyph_code = nodecodes.glyph
+local par_code = nodecodes.par
+local boundary_code = nodecodes.boundary
+
+local wordboundary_code = boundarycodes.word
+
+local texgetnest = tex.getnest -- to be used
+local texsetcount = tex.setcount
+
+local flush_node = nodes.flush_node
+local flush_list = nodes.flush_list
+
+local settexattribute = tex.setattribute
+local punctuation = characters.is_punctuation
+
+local variables = interfaces.variables
+local v_all = variables.all
+local v_reset = variables.reset
+
+local stack = { }
+
+local a_marked = attributes.numbers['marked']
+local lastmarked = 0
+local marked = {
+ [v_all] = 1,
+ [""] = 1,
+ [v_reset] = attributes.unsetvalue,
+}
+
+local function pickup()
+ local list = texgetnest()
+ if list then
+ local tail = list.tail
+ if tail and tail.id == glyph_code and punctuation[tail.char] then
+ local prev = tail.prev
+ list.tail = prev
+ if prev then
+ prev.next = nil
+ end
+ list.tail = prev
+ tail.prev = nil
+ return tail
+ end
+ end
+end
+
+local actions = {
+ remove = function(specification)
+ local n = pickup()
+ if n then
+ flush_node(n)
+ end
+ end,
+ push = function(specification)
+ local n = pickup()
+ if n then
+ insert(stack,n or false)
+ end
+ end,
+ pop = function(specification)
+ local n = remove(stack)
+ if n then
+ context(n)
+ end
+ end,
+}
+
+local function pickuppunctuation(specification)
+ local action = actions[specification.action or "remove"]
+ if action then
+ action(specification)
+ end
+end
+
+-- I played with nested marked content but it makes no sense and gives
+-- complex code. Also, it's never needed so why bother.
+
+local function pickup(head,tail,str)
+ local attr = marked[str]
+ local last = tail
+ if last[a_marked] == attr then
+ local first = last
+ while true do
+ local prev = first.prev
+ if prev and prev[a_marked] == attr then
+ if prev.id == par_code then -- and start_of_par(prev)
+ break
+ else
+ first = prev
+ end
+ else
+ break
+ end
+ end
+ return first, last
+ end
+end
+
+local function found(str)
+ local list = texgetnest()
+ if list then
+ local tail = list.tail
+ return tail and tail[a_marked] == marked[str]
+ end
+end
+
+local actions = {
+ remove = function(specification)
+ local list = texgetnest()
+ if list then
+ local head = list.head
+ local tail = list.tail
+ local first, last = pickup(head,tail,specification.mark)
+ if first then
+ if first == head then
+ list.head = nil
+ list.tail = nil
+ else
+ local prev = first.prev
+ list.tail = prev
+ prev.next = nil
+ end
+ flush_list(first)
+ end
+ end
+ end,
+}
+
+local function pickupmarkedcontent(specification)
+ local action = actions[specification.action or "remove"]
+ if action then
+ action(specification)
+ end
+end
+
+local function markcontent(str)
+ local currentmarked = marked[str or v_all]
+ if not currentmarked then
+ lastmarked = lastmarked + 1
+ currentmarked = lastmarked
+ marked[str] = currentmarked
+ end
+ settexattribute(a_marked,currentmarked)
+end
+
+interfaces.implement {
+ name = "pickuppunctuation",
+ actions = pickuppunctuation,
+ arguments = {
+ {
+ { "action" }
+ }
+ }
+}
+
+interfaces.implement {
+ name = "pickupmarkedcontent",
+ actions = pickupmarkedcontent,
+ arguments = {
+ {
+ { "action" },
+ { "mark" }
+ }
+ }
+}
+
+interfaces.implement {
+ name = "markcontent",
+ actions = markcontent,
+ arguments = "string",
+}
+
+interfaces.implement {
+ name = "doifelsemarkedcontent",
+ actions = function(str) ctx_doifelse(found(str)) end,
+ arguments = "string",
+}
+
+-- We just put these here.
+
+interfaces.implement {
+ name = "lastnodeidstring",
+ public = true,
+ actions = function()
+ local list = texgetnest() -- "top"
+ local okay = false
+ if list then
+ local tail = list.tail
+ if tail then
+ okay = nodecodes[tail.id]
+ end
+ end
+ context(okay or "")
+ end,
+}
+
+-- local t_lastnodeid = token.create("c_syst_last_node_id")
+--
+-- interfaces.implement {
+-- name = "lastnodeid",
+-- public = true,
+-- actions = function()
+-- ...
+-- tex.setcount("c_syst_last_node_id",okay)
+-- context.sprint(t_lastnodeid)
+-- end,
+-- }
+
+-- not needed in lmtx ...
+
+interfaces.implement {
+ name = "lastnodeid",
+ actions = function()
+ local list = texgetnest() -- "top"
+ local okay = -1
+ if list then
+ local tail = list.tail
+ if tail then
+ okay = tail.id
+ end
+ end
+ texsetcount("c_syst_last_node_id",okay)
+ end,
+}
+
+interfaces.implement {
+ name = "lastnodesubtypestring",
+ public = true,
+ actions = function()
+ local list = texgetnest() -- "top"
+ local okay = false
+ if list then
+ local tail = list.tail
+ if head then
+ okay = subtypes[tail.id][tail.subtype]
+ end
+ end
+ context(okay or "")
+ end,
+}
+
+local function lastnodeequals(id,subtype)
+ local list = texgetnest() -- "top"
+ local okay = false
+ if list then
+ local tail = list.tail
+ if tail then
+ local i = tail.id
+ okay = i == id or i == nodecodes[id]
+ if subtype then
+ local s = tail.subtype
+ okay = s == subtype or s == subtypes[i][subtype]
+ end
+ end
+ end
+ ctx_doifelse(okay)
+end
+
+interfaces.implement {
+ name = "lastnodeequals",
+ arguments = "2 strings",
+ actions = lastnodeequals,
+}
+
+interfaces.implement {
+ name = "atwordboundary",
+ actions = function()
+ lastnodeequals(boundary_code,wordboundary_code)
+ end,
+}
+
diff --git a/tex/context/base/mkxl/typo-chr.mkxl b/tex/context/base/mkxl/typo-chr.mkxl
index c4fbeba17..1c459cfcf 100644
--- a/tex/context/base/mkxl/typo-chr.mkxl
+++ b/tex/context/base/mkxl/typo-chr.mkxl
@@ -33,7 +33,7 @@
%D for instance when combining bit and pieces where keeping a state is complex compared
%D to cleaning up unwanted stuff.
-\registerctxluafile{typo-chr}{}
+\registerctxluafile{typo-chr}{autosuffix}
\definesystemattribute[marked][public]
diff --git a/tex/context/base/mkxl/typo-drp.lmt b/tex/context/base/mkxl/typo-drp.lmt
index 8741376b6..2c53cc111 100644
--- a/tex/context/base/mkxl/typo-drp.lmt
+++ b/tex/context/base/mkxl/typo-drp.lmt
@@ -310,13 +310,8 @@ actions[v_default] = function(head,setting)
if trace_initials then
report_initials("setting hangafter to %i and hangindent to %p",hangafter,hangindent)
end
- if CONTEXTLMTXMODE > 0 then
- texset("hangafter",hangafter,true)
- texset("hangindent",hangindent,true)
- else
- texset("hangafter",hangafter)
- texset("hangindent",hangindent)
- end
+ texset("hangafter",hangafter,true)
+ texset("hangindent",hangindent,true)
end
if indent then
insert_after(first,first,new_kern(-parindent))
diff --git a/tex/context/base/mkxl/typo-ovl.lmt b/tex/context/base/mkxl/typo-ovl.lmt
new file mode 100644
index 000000000..5834f9ff0
--- /dev/null
+++ b/tex/context/base/mkxl/typo-ovl.lmt
@@ -0,0 +1,184 @@
+if not modules then modules = { } end modules ['typo-ovl'] = {
+ version = 1.001,
+ comment = "companion to typo-ovl.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is dubious code. If you needed it your source is probably bad. We only used
+-- in when we had to mark bad content but when cleaning up some project code I decided
+-- that it is easier to maintain in the distribution then in a project style. After all,
+-- we have hardly any private code. For convenience I hooked it into the existing
+-- replacement module (as it used the same code anyway). I did some cleanup.
+
+local next, type = next, type
+
+local context = context
+
+local nuts = nodes.nuts
+local tonut = nodes.tonut
+local tonode = nodes.tonode
+
+local nodecodes = nodes.nodecodes
+local glyph_code = nodecodes.glyph
+local disc_code = nodecodes.disc
+
+local getnext = nuts.getnext
+local getid = nuts.getid
+local getdisc = nuts.getdisc
+local getattr = nuts.getattr
+local setattr = nuts.setattr
+local getattrlist = nuts.getattrlist
+local setattrlist = nuts.setattrlist
+local getdata = nuts.getdata
+local setfont = nuts.setfont
+
+local nextnode = nuts.traversers.node
+
+local unsetvalue = attributes.unsetvalue
+local prvattribute = attributes.private
+
+local texgetbox = tex.getbox
+local currentfont = font.current
+
+local a_overloads = attributes.private("overloads")
+local n_overloads = 0
+local t_overloads = { }
+
+local overloaded = { }
+
+local function markasoverload(a)
+ local n = prvattribute(a)
+ if n then
+ overloaded[n] = a
+ end
+end
+
+attributes.markasoverload = markasoverload
+
+markasoverload("color")
+markasoverload("colormodel")
+markasoverload("transparency")
+markasoverload("case")
+markasoverload("negative")
+markasoverload("effect")
+markasoverload("ruled")
+markasoverload("shifted")
+markasoverload("kernchars")
+markasoverload("kern")
+markasoverload("noligature")
+markasoverload("viewerlayer")
+
+local function tooverloads(n)
+ local current = tonut(n)
+ local a = getattrlist(current)
+ local s = { }
+ while a do
+ local index, value = getdata(a)
+ local o = overloaded[n]
+ if o and value ~= unsetvalue then
+ -- It is actually very unlikely that we have unsetvalue.
+ s[index] = value
+ end
+ a = getnext(a)
+ end
+ return s
+end
+
+attributes.tooverloads = tooverloads
+
+function attributes.applyoverloads(specification,start,stop)
+ local start = tonut(start)
+ local processor = specification.processor
+ local overloads = specification.processor or getattr(start,a_overloads)
+ if overloads and overloads ~= unsetvalue then
+ overloads = t_overloads[overloads]
+ if not overloads then
+ return
+ end
+ else
+ return
+ end
+
+ local last = stop and tonut(stop)
+ local oldlist = nil
+ local newlist = nil
+ local newfont = overloads.font
+
+ local function apply(current)
+ local a = getattrlist(current)
+ if a == oldlist then
+ setattrlist(current,newlist)
+ else
+ oldlist = getattrlist(current)
+ for k, v in next, overloads do
+ if type(v) == "number" then
+ setattr(current,k,v)
+ else
+ -- can be: ["font"] = number
+ end
+ end
+ newlist = current -- getattrlist(current)
+ end
+ if newfont then
+ setfont(current,newfont)
+ end
+ end
+
+ for current, id in nextnode, start do
+ if id == glyph_code then
+ apply(current)
+ elseif id == disc_code then
+ apply(current)
+ if pre then
+ while pre do
+ if getid(pre) == glyph_code then
+ apply()
+ end
+ pre = getnext(pre)
+ end
+ end
+ if post then
+ while post do
+ if getid(post) == glyph_code then
+ apply()
+ end
+ post = getnext(post)
+ end
+ end
+ if replace then
+ while replace do
+ if getid(replace) == glyph_code then
+ apply()
+ end
+ replace = getnext(replace)
+ end
+ end
+ end
+ if current == last then
+ break
+ end
+ end
+end
+
+-- we assume the same highlight so we're global
+
+interfaces.implement {
+ name = "overloadsattribute",
+ arguments = { "string", "integer", "integer" },
+ actions = function(name,font,box)
+ local samplebox = texgetbox(box)
+ local sample = samplebox and samplebox.list
+ local overloads = sample and tooverloads(sample)
+ if overloads then
+ overloads.font = font > 0 and font or false
+ n_overloads = n_overloads + 1
+ t_overloads[n_overloads] = overloads
+ t_overloads[name] = overloads
+ context(n_overloads)
+ else
+ context(unsetvalue)
+ end
+ end
+}
diff --git a/tex/context/base/mkxl/typo-ovl.mkxl b/tex/context/base/mkxl/typo-ovl.mkxl
index 8ad4cce26..b3ba2e783 100644
--- a/tex/context/base/mkxl/typo-ovl.mkxl
+++ b/tex/context/base/mkxl/typo-ovl.mkxl
@@ -22,7 +22,7 @@
%D replaced as otherwise normal attributes make more sense. Using this otherwise
%D is weird but one never knows what users come up with.
-\registerctxluafile{typo-ovl}{}
+\registerctxluafile{typo-ovl}{autosuffix}
\definesystemattribute[overloads][public,global]
diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua
index 7fb03eb1e..d54dc082a 100644
--- a/tex/generic/context/luatex/luatex-fonts-merged.lua
+++ b/tex/generic/context/luatex/luatex-fonts-merged.lua
@@ -1,6 +1,6 @@
-- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua
-- parent file : c:/data/develop/context/sources/luatex-fonts.lua
--- merge date : 2020-12-06 18:12
+-- merge date : 2020-12-08 11:06
do -- begin closure to overcome local limits and interference
@@ -21141,7 +21141,6 @@ local function copytotfm(data,cache_id)
parameters.units=units
parameters.vheight=metadata.defaultvheight
properties.space=spacer
- properties.encodingbytes=2
properties.format=data.format or formats.otf
properties.filename=filename
properties.fontname=fontname
@@ -21149,6 +21148,9 @@ local function copytotfm(data,cache_id)
properties.psname=psname
properties.name=filename or fullname
properties.subfont=subfont
+if not CONTEXTLMTXMODE or CONTEXTLMTXMODE==0 then
+ properties.encodingbytes=2
+end
properties.private=properties.private or data.private or privateoffset
return {
characters=characters,
@@ -34711,7 +34713,6 @@ local function copytotfm(data)
parameters.descender=abs(metadata.descender or 0)
parameters.units=1000
properties.spacer=spacer
- properties.encodingbytes=2
properties.format=fonts.formats[filename] or "type1"
properties.filename=filename
properties.fontname=fontname
@@ -34719,6 +34720,9 @@ local function copytotfm(data)
properties.psname=fullname
properties.name=filename or fullname or fontname
properties.private=properties.private or data.private or privateoffset
+if not CONTEXTLMTXMODE or CONTEXTLMTXMODE==0 then
+ properties.encodingbytes=2
+end
if next(characters) then
return {
characters=characters,