summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilipp Gesang <phg@phi-gamma.net>2016-04-19 08:28:21 +0200
committerPhilipp Gesang <phg@phi-gamma.net>2016-04-19 08:28:21 +0200
commitc35210aaaf992793bd111a0bd90e28630c7d2101 (patch)
treeb0706956c6986a8b83d2d62abeeb8a13396605eb
parenta119b257eb5ad1a9ce9a9015d082f28fd2a3527e (diff)
parentf73ce8ad37e9dab36d7aebf03198586691737afe (diff)
downloadluaotfload-c35210aaaf992793bd111a0bd90e28630c7d2101.tar.gz
Merge pull request #333 from phi-gamma/master
latest 2.7 progress
-rw-r--r--doc/luaotfload-main.tex31
-rwxr-xr-xscripts/mkstatus2
-rw-r--r--src/fontloader/misc/fontloader-font-otr.lua4
-rw-r--r--src/fontloader/runtime/fontloader-reference.lua6
-rw-r--r--src/fontloader/runtime/fontloader-tl2014.lua13936
-rw-r--r--src/luaotfload-configuration.lua1
-rw-r--r--src/luaotfload-database.lua3
-rw-r--r--src/luaotfload-features.lua2
-rw-r--r--src/luaotfload-init.lua3
-rw-r--r--src/luaotfload-main.lua27
10 files changed, 40 insertions, 13975 deletions
diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex
index 7099f3d..fdca3c0 100644
--- a/doc/luaotfload-main.tex
+++ b/doc/luaotfload-main.tex
@@ -807,6 +807,36 @@ Currently (2014) there are three of them:
\endsection
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\beginsection {Combining fonts}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+Version 2.7 and later support combining characters from multiple fonts into a
+single virtualized one. This requires that the affected fonts be loaded in
+advance as well as a special \emphasis{request syntax}. Furthermore, this
+allows to define \emphasis{fallback fonts} to supplement fonts that may lack
+certain required glyphs.
+
+For example, the \identifier{Latin Modern} family of fonts does, as indicated
+in the name, not provide Cyrillic glyphs. If Latin script dominates in the copy
+with interspersed Cyrillic, a fallback can be created from a similiar looking
+font like \identifier{Computer Modern Unicode}, taking advantage of the fact
+that it too derives from Knuth’s original \identifier{Computer Modern} series:
+
+\beginlisting
+ \input luaotfload.sty
+ \font \lm = file:lmroman10-regular.otf:mode=base
+ \font \cmu = file:cmunrm.otf:mode=base
+ \font \lmu = "combo: 1->\fontid\lm; 2->\fontid\cmu,fallback"
+ \lmu Eh bien, mon prince. Gênes et Lueques ne sont plus que des
+ apanages, des поместья, de la famille Buonaparte.
+ \bye
+\endlisting
+
+TODO
+
+\endsection
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\beginsection {Font names database}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1779,3 +1809,4 @@ Distributions probably package it as \fileent
\endinput
+% vim:ft=tex:tw=79:et:sw=2
diff --git a/scripts/mkstatus b/scripts/mkstatus
index 5e3be9a..f9ff7eb 100755
--- a/scripts/mkstatus
+++ b/scripts/mkstatus
@@ -5,7 +5,6 @@
-- DESCRIPTION: writes the repository state
-- REQUIREMENTS: luatex, the lualibs package
-- AUTHOR: Philipp Gesang (Phg), <phg42.2a@gmail.com>
--- VERSION: 1.0
-- CREATED: 2013-07-07 14:01:12+0200
-----------------------------------------------------------------------
--
@@ -80,7 +79,6 @@ local names = {
--- fontloader runtimes
{ rtdir, "fontloader-basics-gen.lua", },
{ rtdir, "fontloader-reference.lua", },
- { rtdir, "fontloader-tl2014.lua", },
--- fontloader constituents
{ miscdir, "fontloader-basics-nod.lua", },
diff --git a/src/fontloader/misc/fontloader-font-otr.lua b/src/fontloader/misc/fontloader-font-otr.lua
index 7226907..3542d18 100644
--- a/src/fontloader/misc/fontloader-font-otr.lua
+++ b/src/fontloader/misc/fontloader-font-otr.lua
@@ -2020,7 +2020,7 @@ local function loadfontdata(specification)
ttcversion = ttcversion,
nofsubfonts = nofsubfonts,
}
- for i=1,fontdata.nofsubfonts do
+ for i=1,nofsubfonts do
subfonts[i] = readdata(f,offsets[i],specification)
end
end
@@ -2167,7 +2167,7 @@ function readers.getinfo(filename,specification) -- string, nil|number|table
info[i] = getinfo(fontdata,i,platformnames,rawfamilynames)
end
return info
- elseif subfont > 1 and subfont <= #subfonts then
+ elseif subfont >= 1 and subfont <= #subfonts then
return getinfo(fontdata,subfont,platformnames,rawfamilynames)
else
return {
diff --git a/src/fontloader/runtime/fontloader-reference.lua b/src/fontloader/runtime/fontloader-reference.lua
index 1595733..e784738 100644
--- a/src/fontloader/runtime/fontloader-reference.lua
+++ b/src/fontloader/runtime/fontloader-reference.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 : 04/18/16 18:53:40
+-- merge date : 04/18/16 22:12:36
do -- begin closure to overcome local limits and interference
@@ -9536,7 +9536,7 @@ local function loadfontdata(specification)
ttcversion=ttcversion,
nofsubfonts=nofsubfonts,
}
- for i=1,fontdata.nofsubfonts do
+ for i=1,nofsubfonts do
subfonts[i]=readdata(f,offsets[i],specification)
end
end
@@ -9667,7 +9667,7 @@ function readers.getinfo(filename,specification)
info[i]=getinfo(fontdata,i,platformnames,rawfamilynames)
end
return info
- elseif subfont>1 and subfont<=#subfonts then
+ elseif subfont>=1 and subfont<=#subfonts then
return getinfo(fontdata,subfont,platformnames,rawfamilynames)
else
return {
diff --git a/src/fontloader/runtime/fontloader-tl2014.lua b/src/fontloader/runtime/fontloader-tl2014.lua
deleted file mode 100644
index 12b68a5..0000000
--- a/src/fontloader/runtime/fontloader-tl2014.lua
+++ /dev/null
@@ -1,13936 +0,0 @@
--- merged file : luatex-fonts-merged.lua
--- parent file : luatex-fonts.lua
--- merge date : 07/29/14 00:30:11
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['l-lua']={
- version=1.001,
- comment="companion to luat-lib.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local major,minor=string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$")
-_MAJORVERSION=tonumber(major) or 5
-_MINORVERSION=tonumber(minor) or 1
-_LUAVERSION=_MAJORVERSION+_MINORVERSION/10
-if not lpeg then
- lpeg=require("lpeg")
-end
-if loadstring then
- local loadnormal=load
- function load(first,...)
- if type(first)=="string" then
- return loadstring(first,...)
- else
- return loadnormal(first,...)
- end
- end
-else
- loadstring=load
-end
-if not ipairs then
- local function iterate(a,i)
- i=i+1
- local v=a[i]
- if v~=nil then
- return i,v
- end
- end
- function ipairs(a)
- return iterate,a,0
- end
-end
-if not pairs then
- function pairs(t)
- return next,t
- end
-end
-if not table.unpack then
- table.unpack=_G.unpack
-elseif not unpack then
- _G.unpack=table.unpack
-end
-if not package.loaders then
- package.loaders=package.searchers
-end
-local print,select,tostring=print,select,tostring
-local inspectors={}
-function setinspector(inspector)
- inspectors[#inspectors+1]=inspector
-end
-function inspect(...)
- for s=1,select("#",...) do
- local value=select(s,...)
- local done=false
- for i=1,#inspectors do
- done=inspectors[i](value)
- if done then
- break
- end
- end
- if not done then
- print(tostring(value))
- end
- end
-end
-local dummy=function() end
-function optionalrequire(...)
- local ok,result=xpcall(require,dummy,...)
- if ok then
- return result
- end
-end
-if lua then
- lua.mask=load([[τεχ = 1]]) and "utf" or "ascii"
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['l-lpeg']={
- version=1.001,
- comment="companion to luat-lib.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-lpeg=require("lpeg")
-if not lpeg.print then function lpeg.print(...) print(lpeg.pcode(...)) end end
-local type,next,tostring=type,next,tostring
-local byte,char,gmatch,format=string.byte,string.char,string.gmatch,string.format
-local floor=math.floor
-local P,R,S,V,Ct,C,Cs,Cc,Cp,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.V,lpeg.Ct,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Cp,lpeg.Cmt
-local lpegtype,lpegmatch,lpegprint=lpeg.type,lpeg.match,lpeg.print
-if setinspector then
- setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end)
-end
-lpeg.patterns=lpeg.patterns or {}
-local patterns=lpeg.patterns
-local anything=P(1)
-local endofstring=P(-1)
-local alwaysmatched=P(true)
-patterns.anything=anything
-patterns.endofstring=endofstring
-patterns.beginofstring=alwaysmatched
-patterns.alwaysmatched=alwaysmatched
-local sign=S('+-')
-local zero=P('0')
-local digit=R('09')
-local octdigit=R("07")
-local lowercase=R("az")
-local uppercase=R("AZ")
-local underscore=P("_")
-local hexdigit=digit+lowercase+uppercase
-local cr,lf,crlf=P("\r"),P("\n"),P("\r\n")
-local newline=P("\r")*(P("\n")+P(true))+P("\n")
-local escaped=P("\\")*anything
-local squote=P("'")
-local dquote=P('"')
-local space=P(" ")
-local period=P(".")
-local comma=P(",")
-local utfbom_32_be=P('\000\000\254\255')
-local utfbom_32_le=P('\255\254\000\000')
-local utfbom_16_be=P('\254\255')
-local utfbom_16_le=P('\255\254')
-local utfbom_8=P('\239\187\191')
-local utfbom=utfbom_32_be+utfbom_32_le+utfbom_16_be+utfbom_16_le+utfbom_8
-local utftype=utfbom_32_be*Cc("utf-32-be")+utfbom_32_le*Cc("utf-32-le")+utfbom_16_be*Cc("utf-16-be")+utfbom_16_le*Cc("utf-16-le")+utfbom_8*Cc("utf-8")+alwaysmatched*Cc("utf-8")
-local utfstricttype=utfbom_32_be*Cc("utf-32-be")+utfbom_32_le*Cc("utf-32-le")+utfbom_16_be*Cc("utf-16-be")+utfbom_16_le*Cc("utf-16-le")+utfbom_8*Cc("utf-8")
-local utfoffset=utfbom_32_be*Cc(4)+utfbom_32_le*Cc(4)+utfbom_16_be*Cc(2)+utfbom_16_le*Cc(2)+utfbom_8*Cc(3)+Cc(0)
-local utf8next=R("\128\191")
-patterns.utfbom_32_be=utfbom_32_be
-patterns.utfbom_32_le=utfbom_32_le
-patterns.utfbom_16_be=utfbom_16_be
-patterns.utfbom_16_le=utfbom_16_le
-patterns.utfbom_8=utfbom_8
-patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n")
-patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000")
-patterns.utf8one=R("\000\127")
-patterns.utf8two=R("\194\223")*utf8next
-patterns.utf8three=R("\224\239")*utf8next*utf8next
-patterns.utf8four=R("\240\244")*utf8next*utf8next*utf8next
-patterns.utfbom=utfbom
-patterns.utftype=utftype
-patterns.utfstricttype=utfstricttype
-patterns.utfoffset=utfoffset
-local utf8char=patterns.utf8one+patterns.utf8two+patterns.utf8three+patterns.utf8four
-local validutf8char=utf8char^0*endofstring*Cc(true)+Cc(false)
-local utf8character=P(1)*R("\128\191")^0
-patterns.utf8=utf8char
-patterns.utf8char=utf8char
-patterns.utf8character=utf8character
-patterns.validutf8=validutf8char
-patterns.validutf8char=validutf8char
-local eol=S("\n\r")
-local spacer=S(" \t\f\v")
-local whitespace=eol+spacer
-local nonspacer=1-spacer
-local nonwhitespace=1-whitespace
-patterns.eol=eol
-patterns.spacer=spacer
-patterns.whitespace=whitespace
-patterns.nonspacer=nonspacer
-patterns.nonwhitespace=nonwhitespace
-local stripper=spacer^0*C((spacer^0*nonspacer^1)^0)
-local fullstripper=whitespace^0*C((whitespace^0*nonwhitespace^1)^0)
-local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0))
-patterns.stripper=stripper
-patterns.fullstripper=fullstripper
-patterns.collapser=collapser
-patterns.lowercase=lowercase
-patterns.uppercase=uppercase
-patterns.letter=patterns.lowercase+patterns.uppercase
-patterns.space=space
-patterns.tab=P("\t")
-patterns.spaceortab=patterns.space+patterns.tab
-patterns.newline=newline
-patterns.emptyline=newline^1
-patterns.equal=P("=")
-patterns.comma=comma
-patterns.commaspacer=comma*spacer^0
-patterns.period=period
-patterns.colon=P(":")
-patterns.semicolon=P(";")
-patterns.underscore=underscore
-patterns.escaped=escaped
-patterns.squote=squote
-patterns.dquote=dquote
-patterns.nosquote=(escaped+(1-squote))^0
-patterns.nodquote=(escaped+(1-dquote))^0
-patterns.unsingle=(squote/"")*patterns.nosquote*(squote/"")
-patterns.undouble=(dquote/"")*patterns.nodquote*(dquote/"")
-patterns.unquoted=patterns.undouble+patterns.unsingle
-patterns.unspacer=((patterns.spacer^1)/"")^0
-patterns.singlequoted=squote*patterns.nosquote*squote
-patterns.doublequoted=dquote*patterns.nodquote*dquote
-patterns.quoted=patterns.doublequoted+patterns.singlequoted
-patterns.digit=digit
-patterns.octdigit=octdigit
-patterns.hexdigit=hexdigit
-patterns.sign=sign
-patterns.cardinal=digit^1
-patterns.integer=sign^-1*digit^1
-patterns.unsigned=digit^0*period*digit^1
-patterns.float=sign^-1*patterns.unsigned
-patterns.cunsigned=digit^0*comma*digit^1
-patterns.cpunsigned=digit^0*(period+comma)*digit^1
-patterns.cfloat=sign^-1*patterns.cunsigned
-patterns.cpfloat=sign^-1*patterns.cpunsigned
-patterns.number=patterns.float+patterns.integer
-patterns.cnumber=patterns.cfloat+patterns.integer
-patterns.cpnumber=patterns.cpfloat+patterns.integer
-patterns.oct=zero*octdigit^1
-patterns.octal=patterns.oct
-patterns.HEX=zero*P("X")*(digit+uppercase)^1
-patterns.hex=zero*P("x")*(digit+lowercase)^1
-patterns.hexadecimal=zero*S("xX")*hexdigit^1
-patterns.hexafloat=sign^-1*zero*S("xX")*(hexdigit^0*period*hexdigit^1+hexdigit^1*period*hexdigit^0+hexdigit^1)*(S("pP")*sign^-1*hexdigit^1)^-1
-patterns.decafloat=sign^-1*(digit^0*period*digit^1+digit^1*period*digit^0+digit^1)*S("eE")*sign^-1*digit^1
-patterns.propername=(uppercase+lowercase+underscore)*(uppercase+lowercase+underscore+digit)^0*endofstring
-patterns.somecontent=(anything-newline-space)^1
-patterns.beginline=#(1-newline)
-patterns.longtostring=Cs(whitespace^0/""*((patterns.quoted+nonwhitespace^1+whitespace^1/""*(P(-1)+Cc(" ")))^0))
-local function anywhere(pattern)
- return P { P(pattern)+1*V(1) }
-end
-lpeg.anywhere=anywhere
-function lpeg.instringchecker(p)
- p=anywhere(p)
- return function(str)
- return lpegmatch(p,str) and true or false
- end
-end
-function lpeg.splitter(pattern,action)
- return (((1-P(pattern))^1)/action+1)^0
-end
-function lpeg.tsplitter(pattern,action)
- return Ct((((1-P(pattern))^1)/action+1)^0)
-end
-local splitters_s,splitters_m,splitters_t={},{},{}
-local function splitat(separator,single)
- local splitter=(single and splitters_s[separator]) or splitters_m[separator]
- if not splitter then
- separator=P(separator)
- local other=C((1-separator)^0)
- if single then
- local any=anything
- splitter=other*(separator*C(any^0)+"")
- splitters_s[separator]=splitter
- else
- splitter=other*(separator*other)^0
- splitters_m[separator]=splitter
- end
- end
- return splitter
-end
-local function tsplitat(separator)
- local splitter=splitters_t[separator]
- if not splitter then
- splitter=Ct(splitat(separator))
- splitters_t[separator]=splitter
- end
- return splitter
-end
-lpeg.splitat=splitat
-lpeg.tsplitat=tsplitat
-function string.splitup(str,separator)
- if not separator then
- separator=","
- end
- return lpegmatch(splitters_m[separator] or splitat(separator),str)
-end
-local cache={}
-function lpeg.split(separator,str)
- local c=cache[separator]
- if not c then
- c=tsplitat(separator)
- cache[separator]=c
- end
- return lpegmatch(c,str)
-end
-function string.split(str,separator)
- if separator then
- local c=cache[separator]
- if not c then
- c=tsplitat(separator)
- cache[separator]=c
- end
- return lpegmatch(c,str)
- else
- return { str }
- end
-end
-local spacing=patterns.spacer^0*newline
-local empty=spacing*Cc("")
-local nonempty=Cs((1-spacing)^1)*spacing^-1
-local content=(empty+nonempty)^1
-patterns.textline=content
-local linesplitter=tsplitat(newline)
-patterns.linesplitter=linesplitter
-function string.splitlines(str)
- return lpegmatch(linesplitter,str)
-end
-local cache={}
-function lpeg.checkedsplit(separator,str)
- local c=cache[separator]
- if not c then
- separator=P(separator)
- local other=C((1-separator)^1)
- c=Ct(separator^0*other*(separator^1*other)^0)
- cache[separator]=c
- end
- return lpegmatch(c,str)
-end
-function string.checkedsplit(str,separator)
- local c=cache[separator]
- if not c then
- separator=P(separator)
- local other=C((1-separator)^1)
- c=Ct(separator^0*other*(separator^1*other)^0)
- cache[separator]=c
- end
- return lpegmatch(c,str)
-end
-local function f2(s) local c1,c2=byte(s,1,2) return c1*64+c2-12416 end
-local function f3(s) local c1,c2,c3=byte(s,1,3) return (c1*64+c2)*64+c3-925824 end
-local function f4(s) local c1,c2,c3,c4=byte(s,1,4) return ((c1*64+c2)*64+c3)*64+c4-63447168 end
-local utf8byte=patterns.utf8one/byte+patterns.utf8two/f2+patterns.utf8three/f3+patterns.utf8four/f4
-patterns.utf8byte=utf8byte
-local cache={}
-function lpeg.stripper(str)
- if type(str)=="string" then
- local s=cache[str]
- if not s then
- s=Cs(((S(str)^1)/""+1)^0)
- cache[str]=s
- end
- return s
- else
- return Cs(((str^1)/""+1)^0)
- end
-end
-local cache={}
-function lpeg.keeper(str)
- if type(str)=="string" then
- local s=cache[str]
- if not s then
- s=Cs((((1-S(str))^1)/""+1)^0)
- cache[str]=s
- end
- return s
- else
- return Cs((((1-str)^1)/""+1)^0)
- end
-end
-function lpeg.frontstripper(str)
- return (P(str)+P(true))*Cs(anything^0)
-end
-function lpeg.endstripper(str)
- return Cs((1-P(str)*endofstring)^0)
-end
-function lpeg.replacer(one,two,makefunction,isutf)
- local pattern
- local u=isutf and utf8char or 1
- if type(one)=="table" then
- local no=#one
- local p=P(false)
- if no==0 then
- for k,v in next,one do
- p=p+P(k)/v
- end
- pattern=Cs((p+u)^0)
- elseif no==1 then
- local o=one[1]
- one,two=P(o[1]),o[2]
- pattern=Cs((one/two+u)^0)
- else
- for i=1,no do
- local o=one[i]
- p=p+P(o[1])/o[2]
- end
- pattern=Cs((p+u)^0)
- end
- else
- pattern=Cs((P(one)/(two or "")+u)^0)
- end
- if makefunction then
- return function(str)
- return lpegmatch(pattern,str)
- end
- else
- return pattern
- end
-end
-function lpeg.finder(lst,makefunction,isutf)
- local pattern
- if type(lst)=="table" then
- pattern=P(false)
- if #lst==0 then
- for k,v in next,lst do
- pattern=pattern+P(k)
- end
- else
- for i=1,#lst do
- pattern=pattern+P(lst[i])
- end
- end
- else
- pattern=P(lst)
- end
- if isutf then
- pattern=((utf8char or 1)-pattern)^0*pattern
- else
- pattern=(1-pattern)^0*pattern
- end
- if makefunction then
- return function(str)
- return lpegmatch(pattern,str)
- end
- else
- return pattern
- end
-end
-local splitters_f,splitters_s={},{}
-function lpeg.firstofsplit(separator)
- local splitter=splitters_f[separator]
- if not splitter then
- local pattern=P(separator)
- splitter=C((1-pattern)^0)
- splitters_f[separator]=splitter
- end
- return splitter
-end
-function lpeg.secondofsplit(separator)
- local splitter=splitters_s[separator]
- if not splitter then
- local pattern=P(separator)
- splitter=(1-pattern)^0*pattern*C(anything^0)
- splitters_s[separator]=splitter
- end
- return splitter
-end
-local splitters_s,splitters_p={},{}
-function lpeg.beforesuffix(separator)
- local splitter=splitters_s[separator]
- if not splitter then
- local pattern=P(separator)
- splitter=C((1-pattern)^0)*pattern*endofstring
- splitters_s[separator]=splitter
- end
- return splitter
-end
-function lpeg.afterprefix(separator)
- local splitter=splitters_p[separator]
- if not splitter then
- local pattern=P(separator)
- splitter=pattern*C(anything^0)
- splitters_p[separator]=splitter
- end
- return splitter
-end
-function lpeg.balancer(left,right)
- left,right=P(left),P(right)
- return P { left*((1-left-right)+V(1))^0*right }
-end
-local nany=utf8char/""
-function lpeg.counter(pattern)
- pattern=Cs((P(pattern)/" "+nany)^0)
- return function(str)
- return #lpegmatch(pattern,str)
- end
-end
-utf=utf or (unicode and unicode.utf8) or {}
-local utfcharacters=utf and utf.characters or string.utfcharacters
-local utfgmatch=utf and utf.gmatch
-local utfchar=utf and utf.char
-lpeg.UP=lpeg.P
-if utfcharacters then
- function lpeg.US(str)
- local p=P(false)
- for uc in utfcharacters(str) do
- p=p+P(uc)
- end
- return p
- end
-elseif utfgmatch then
- function lpeg.US(str)
- local p=P(false)
- for uc in utfgmatch(str,".") do
- p=p+P(uc)
- end
- return p
- end
-else
- function lpeg.US(str)
- local p=P(false)
- local f=function(uc)
- p=p+P(uc)
- end
- lpegmatch((utf8char/f)^0,str)
- return p
- end
-end
-local range=utf8byte*utf8byte+Cc(false)
-function lpeg.UR(str,more)
- local first,last
- if type(str)=="number" then
- first=str
- last=more or first
- else
- first,last=lpegmatch(range,str)
- if not last then
- return P(str)
- end
- end
- if first==last then
- return P(str)
- elseif utfchar and (last-first<8) then
- local p=P(false)
- for i=first,last do
- p=p+P(utfchar(i))
- end
- return p
- else
- local f=function(b)
- return b>=first and b<=last
- end
- return utf8byte/f
- end
-end
-function lpeg.is_lpeg(p)
- return p and lpegtype(p)=="pattern"
-end
-function lpeg.oneof(list,...)
- if type(list)~="table" then
- list={ list,... }
- end
- local p=P(list[1])
- for l=2,#list do
- p=p+P(list[l])
- end
- return p
-end
-local sort=table.sort
-local function copyindexed(old)
- local new={}
- for i=1,#old do
- new[i]=old
- end
- return new
-end
-local function sortedkeys(tab)
- local keys,s={},0
- for key,_ in next,tab do
- s=s+1
- keys[s]=key
- end
- sort(keys)
- return keys
-end
-function lpeg.append(list,pp,delayed,checked)
- local p=pp
- if #list>0 then
- local keys=copyindexed(list)
- sort(keys)
- for i=#keys,1,-1 do
- local k=keys[i]
- if p then
- p=P(k)+p
- else
- p=P(k)
- end
- end
- elseif delayed then
- local keys=sortedkeys(list)
- if p then
- for i=1,#keys,1 do
- local k=keys[i]
- local v=list[k]
- p=P(k)/list+p
- end
- else
- for i=1,#keys do
- local k=keys[i]
- local v=list[k]
- if p then
- p=P(k)+p
- else
- p=P(k)
- end
- end
- if p then
- p=p/list
- end
- end
- elseif checked then
- local keys=sortedkeys(list)
- for i=1,#keys do
- local k=keys[i]
- local v=list[k]
- if p then
- if k==v then
- p=P(k)+p
- else
- p=P(k)/v+p
- end
- else
- if k==v then
- p=P(k)
- else
- p=P(k)/v
- end
- end
- end
- else
- local keys=sortedkeys(list)
- for i=1,#keys do
- local k=keys[i]
- local v=list[k]
- if p then
- p=P(k)/v+p
- else
- p=P(k)/v
- end
- end
- end
- return p
-end
-local function make(t,hash)
- local p=P(false)
- local keys=sortedkeys(t)
- for i=1,#keys do
- local k=keys[i]
- local v=t[k]
- local h=hash[v]
- if h then
- if next(v) then
- p=p+P(k)*(make(v,hash)+P(true))
- else
- p=p+P(k)*P(true)
- end
- else
- if next(v) then
- p=p+P(k)*make(v,hash)
- else
- p=p+P(k)
- end
- end
- end
- return p
-end
-function lpeg.utfchartabletopattern(list)
- local tree={}
- local hash={}
- local n=#list
- if n==0 then
- for s in next,list do
- local t=tree
- for c in gmatch(s,".") do
- local tc=t[c]
- if not tc then
- tc={}
- t[c]=tc
- end
- t=tc
- end
- hash[t]=s
- end
- else
- for i=1,n do
- local t=tree
- local s=list[i]
- for c in gmatch(s,".") do
- local tc=t[c]
- if not tc then
- tc={}
- t[c]=tc
- end
- t=tc
- end
- hash[t]=s
- end
- end
- return make(tree,hash)
-end
-patterns.containseol=lpeg.finder(eol)
-local function nextstep(n,step,result)
- local m=n%step
- local d=floor(n/step)
- if d>0 then
- local v=V(tostring(step))
- local s=result.start
- for i=1,d do
- if s then
- s=v*s
- else
- s=v
- end
- end
- result.start=s
- end
- if step>1 and result.start then
- local v=V(tostring(step/2))
- result[tostring(step)]=v*v
- end
- if step>0 then
- return nextstep(m,step/2,result)
- else
- return result
- end
-end
-function lpeg.times(pattern,n)
- return P(nextstep(n,2^16,{ "start",["1"]=pattern }))
-end
-local trailingzeros=zero^0*-digit
-local case_1=period*trailingzeros/""
-local case_2=period*(digit-trailingzeros)^1*(trailingzeros/"")
-local number=digit^1*(case_1+case_2)
-local stripper=Cs((number+1)^0)
-lpeg.patterns.stripzeros=stripper
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['l-functions']={
- version=1.001,
- comment="companion to luat-lib.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-functions=functions or {}
-function functions.dummy() end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['l-string']={
- version=1.001,
- comment="companion to luat-lib.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local string=string
-local sub,gmatch,format,char,byte,rep,lower=string.sub,string.gmatch,string.format,string.char,string.byte,string.rep,string.lower
-local lpegmatch,patterns=lpeg.match,lpeg.patterns
-local P,S,C,Ct,Cc,Cs=lpeg.P,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cc,lpeg.Cs
-local unquoted=patterns.squote*C(patterns.nosquote)*patterns.squote+patterns.dquote*C(patterns.nodquote)*patterns.dquote
-function string.unquoted(str)
- return lpegmatch(unquoted,str) or str
-end
-function string.quoted(str)
- return format("%q",str)
-end
-function string.count(str,pattern)
- local n=0
- for _ in gmatch(str,pattern) do
- n=n+1
- end
- return n
-end
-function string.limit(str,n,sentinel)
- if #str>n then
- sentinel=sentinel or "..."
- return sub(str,1,(n-#sentinel))..sentinel
- else
- return str
- end
-end
-local stripper=patterns.stripper
-local fullstripper=patterns.fullstripper
-local collapser=patterns.collapser
-local longtostring=patterns.longtostring
-function string.strip(str)
- return lpegmatch(stripper,str) or ""
-end
-function string.fullstrip(str)
- return lpegmatch(fullstripper,str) or ""
-end
-function string.collapsespaces(str)
- return lpegmatch(collapser,str) or ""
-end
-function string.longtostring(str)
- return lpegmatch(longtostring,str) or ""
-end
-local pattern=P(" ")^0*P(-1)
-function string.is_empty(str)
- if str=="" then
- return true
- else
- return lpegmatch(pattern,str) and true or false
- end
-end
-local anything=patterns.anything
-local allescapes=Cc("%")*S(".-+%?()[]*")
-local someescapes=Cc("%")*S(".-+%()[]")
-local matchescapes=Cc(".")*S("*?")
-local pattern_a=Cs ((allescapes+anything )^0 )
-local pattern_b=Cs ((someescapes+matchescapes+anything )^0 )
-local pattern_c=Cs (Cc("^")*(someescapes+matchescapes+anything )^0*Cc("$") )
-function string.escapedpattern(str,simple)
- return lpegmatch(simple and pattern_b or pattern_a,str)
-end
-function string.topattern(str,lowercase,strict)
- if str=="" or type(str)~="string" then
- return ".*"
- elseif strict then
- str=lpegmatch(pattern_c,str)
- else
- str=lpegmatch(pattern_b,str)
- end
- if lowercase then
- return lower(str)
- else
- return str
- end
-end
-function string.valid(str,default)
- return (type(str)=="string" and str~="" and str) or default or nil
-end
-string.itself=function(s) return s end
-local pattern=Ct(C(1)^0)
-function string.totable(str)
- return lpegmatch(pattern,str)
-end
-local replacer=lpeg.replacer("@","%%")
-function string.tformat(fmt,...)
- return format(lpegmatch(replacer,fmt),...)
-end
-string.quote=string.quoted
-string.unquote=string.unquoted
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['l-table']={
- version=1.001,
- comment="companion to luat-lib.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local type,next,tostring,tonumber,ipairs,select=type,next,tostring,tonumber,ipairs,select
-local table,string=table,string
-local concat,sort,insert,remove=table.concat,table.sort,table.insert,table.remove
-local format,lower,dump=string.format,string.lower,string.dump
-local getmetatable,setmetatable=getmetatable,setmetatable
-local getinfo=debug.getinfo
-local lpegmatch,patterns=lpeg.match,lpeg.patterns
-local floor=math.floor
-local stripper=patterns.stripper
-function table.strip(tab)
- local lst,l={},0
- for i=1,#tab do
- local s=lpegmatch(stripper,tab[i]) or ""
- if s=="" then
- else
- l=l+1
- lst[l]=s
- end
- end
- return lst
-end
-function table.keys(t)
- if t then
- local keys,k={},0
- for key,_ in next,t do
- k=k+1
- keys[k]=key
- end
- return keys
- else
- return {}
- end
-end
-local function compare(a,b)
- local ta,tb=type(a),type(b)
- if ta==tb then
- return a<b
- else
- return tostring(a)<tostring(b)
- end
-end
-local function sortedkeys(tab)
- if tab then
- local srt,category,s={},0,0
- for key,_ in next,tab do
- s=s+1
- srt[s]=key
- if category==3 then
- else
- local tkey=type(key)
- if tkey=="string" then
- category=(category==2 and 3) or 1
- elseif tkey=="number" then
- category=(category==1 and 3) or 2
- else
- category=3
- end
- end
- end
- if category==0 or category==3 then
- sort(srt,compare)
- else
- sort(srt)
- end
- return srt
- else
- return {}
- end
-end
-local function sortedhashonly(tab)
- if tab then
- local srt,s={},0
- for key,_ in next,tab do
- if type(key)=="string" then
- s=s+1
- srt[s]=key
- end
- end
- sort(srt)
- return srt
- else
- return {}
- end
-end
-local function sortedindexonly(tab)
- if tab then
- local srt,s={},0
- for key,_ in next,tab do
- if type(key)=="number" then
- s=s+1
- srt[s]=key
- end
- end
- sort(srt)
- return srt
- else
- return {}
- end
-end
-local function sortedhashkeys(tab,cmp)
- if tab then
- local srt,s={},0
- for key,_ in next,tab do
- if key then
- s=s+1
- srt[s]=key
- end
- end
- sort(srt,cmp)
- return srt
- else
- return {}
- end
-end
-function table.allkeys(t)
- local keys={}
- for k,v in next,t do
- for k,v in next,v do
- keys[k]=true
- end
- end
- return sortedkeys(keys)
-end
-table.sortedkeys=sortedkeys
-table.sortedhashonly=sortedhashonly
-table.sortedindexonly=sortedindexonly
-table.sortedhashkeys=sortedhashkeys
-local function nothing() end
-local function sortedhash(t,cmp)
- if t then
- local s
- if cmp then
- s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end)
- else
- s=sortedkeys(t)
- end
- local n=0
- local m=#s
- local function kv()
- if n<m then
- n=n+1
- local k=s[n]
- return k,t[k]
- end
- end
- return kv
- else
- return nothing
- end
-end
-table.sortedhash=sortedhash
-table.sortedpairs=sortedhash
-function table.append(t,list)
- local n=#t
- for i=1,#list do
- n=n+1
- t[n]=list[i]
- end
- return t
-end
-function table.prepend(t,list)
- local nl=#list
- local nt=nl+#t
- for i=#t,1,-1 do
- t[nt]=t[i]
- nt=nt-1
- end
- for i=1,#list do
- t[i]=list[i]
- end
- return t
-end
-function table.merge(t,...)
- t=t or {}
- for i=1,select("#",...) do
- for k,v in next,(select(i,...)) do
- t[k]=v
- end
- end
- return t
-end
-function table.merged(...)
- local t={}
- for i=1,select("#",...) do
- for k,v in next,(select(i,...)) do
- t[k]=v
- end
- end
- return t
-end
-function table.imerge(t,...)
- local nt=#t
- for i=1,select("#",...) do
- local nst=select(i,...)
- for j=1,#nst do
- nt=nt+1
- t[nt]=nst[j]
- end
- end
- return t
-end
-function table.imerged(...)
- local tmp,ntmp={},0
- for i=1,select("#",...) do
- local nst=select(i,...)
- for j=1,#nst do
- ntmp=ntmp+1
- tmp[ntmp]=nst[j]
- end
- end
- return tmp
-end
-local function fastcopy(old,metatabletoo)
- if old then
- local new={}
- for k,v in next,old do
- if type(v)=="table" then
- new[k]=fastcopy(v,metatabletoo)
- else
- new[k]=v
- end
- end
- if metatabletoo then
- local mt=getmetatable(old)
- if mt then
- setmetatable(new,mt)
- end
- end
- return new
- else
- return {}
- end
-end
-local function copy(t,tables)
- tables=tables or {}
- local tcopy={}
- if not tables[t] then
- tables[t]=tcopy
- end
- for i,v in next,t do
- if type(i)=="table" then
- if tables[i] then
- i=tables[i]
- else
- i=copy(i,tables)
- end
- end
- if type(v)~="table" then
- tcopy[i]=v
- elseif tables[v] then
- tcopy[i]=tables[v]
- else
- tcopy[i]=copy(v,tables)
- end
- end
- local mt=getmetatable(t)
- if mt then
- setmetatable(tcopy,mt)
- end
- return tcopy
-end
-table.fastcopy=fastcopy
-table.copy=copy
-function table.derive(parent)
- local child={}
- if parent then
- setmetatable(child,{ __index=parent })
- end
- return child
-end
-function table.tohash(t,value)
- local h={}
- if t then
- if value==nil then value=true end
- for _,v in next,t do
- h[v]=value
- end
- end
- return h
-end
-function table.fromhash(t)
- local hsh,h={},0
- for k,v in next,t do
- if v then
- h=h+1
- hsh[h]=k
- end
- end
- return hsh
-end
-local noquotes,hexify,handle,reduce,compact,inline,functions
-local reserved=table.tohash {
- 'and','break','do','else','elseif','end','false','for','function','if',
- 'in','local','nil','not','or','repeat','return','then','true','until','while',
- 'NaN','goto',
-}
-local function simple_table(t)
- if #t>0 then
- local n=0
- for _,v in next,t do
- n=n+1
- end
- if n==#t then
- local tt,nt={},0
- for i=1,#t do
- local v=t[i]
- local tv=type(v)
- if tv=="number" then
- nt=nt+1
- if hexify then
- tt[nt]=format("0x%X",v)
- else
- tt[nt]=tostring(v)
- end
- elseif tv=="string" then
- nt=nt+1
- tt[nt]=format("%q",v)
- elseif tv=="boolean" then
- nt=nt+1
- tt[nt]=v and "true" or "false"
- else
- tt=nil
- break
- end
- end
- return tt
- end
- end
- return nil
-end
-local propername=patterns.propername
-local function dummy() end
-local function do_serialize(root,name,depth,level,indexed)
- if level>0 then
- depth=depth.." "
- if indexed then
- handle(format("%s{",depth))
- else
- local tn=type(name)
- if tn=="number" then
- if hexify then
- handle(format("%s[0x%X]={",depth,name))
- else
- handle(format("%s[%s]={",depth,name))
- end
- elseif tn=="string" then
- if noquotes and not reserved[name] and lpegmatch(propername,name) then
- handle(format("%s%s={",depth,name))
- else
- handle(format("%s[%q]={",depth,name))
- end
- elseif tn=="boolean" then
- handle(format("%s[%s]={",depth,name and "true" or "false"))
- else
- handle(format("%s{",depth))
- end
- end
- end
- if root and next(root) then
- local first,last=nil,0
- if compact then
- last=#root
- for k=1,last do
- if root[k]==nil then
- last=k-1
- break
- end
- end
- if last>0 then
- first=1
- end
- end
- local sk=sortedkeys(root)
- for i=1,#sk do
- local k=sk[i]
- local v=root[k]
- local tv,tk=type(v),type(k)
- if compact and first and tk=="number" and k>=first and k<=last then
- if tv=="number" then
- if hexify then
- handle(format("%s 0x%X,",depth,v))
- else
- handle(format("%s %s,",depth,v))
- end
- elseif tv=="string" then
- if reduce and tonumber(v) then
- handle(format("%s %s,",depth,v))
- else
- handle(format("%s %q,",depth,v))
- end
- elseif tv=="table" then
- if not next(v) then
- handle(format("%s {},",depth))
- elseif inline then
- local st=simple_table(v)
- if st then
- handle(format("%s { %s },",depth,concat(st,", ")))
- else
- do_serialize(v,k,depth,level+1,true)
- end
- else
- do_serialize(v,k,depth,level+1,true)
- end
- elseif tv=="boolean" then
- handle(format("%s %s,",depth,v and "true" or "false"))
- elseif tv=="function" then
- if functions then
- handle(format('%s load(%q),',depth,dump(v)))
- else
- handle(format('%s "function",',depth))
- end
- else
- handle(format("%s %q,",depth,tostring(v)))
- end
- elseif k=="__p__" then
- if false then
- handle(format("%s __p__=nil,",depth))
- end
- elseif tv=="number" then
- if tk=="number" then
- if hexify then
- handle(format("%s [0x%X]=0x%X,",depth,k,v))
- else
- handle(format("%s [%s]=%s,",depth,k,v))
- end
- elseif tk=="boolean" then
- if hexify then
- handle(format("%s [%s]=0x%X,",depth,k and "true" or "false",v))
- else
- handle(format("%s [%s]=%s,",depth,k and "true" or "false",v))
- end
- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
- if hexify then
- handle(format("%s %s=0x%X,",depth,k,v))
- else
- handle(format("%s %s=%s,",depth,k,v))
- end
- else
- if hexify then
- handle(format("%s [%q]=0x%X,",depth,k,v))
- else
- handle(format("%s [%q]=%s,",depth,k,v))
- end
- end
- elseif tv=="string" then
- if reduce and tonumber(v) then
- if tk=="number" then
- if hexify then
- handle(format("%s [0x%X]=%s,",depth,k,v))
- else
- handle(format("%s [%s]=%s,",depth,k,v))
- end
- elseif tk=="boolean" then
- handle(format("%s [%s]=%s,",depth,k and "true" or "false",v))
- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
- handle(format("%s %s=%s,",depth,k,v))
- else
- handle(format("%s [%q]=%s,",depth,k,v))
- end
- else
- if tk=="number" then
- if hexify then
- handle(format("%s [0x%X]=%q,",depth,k,v))
- else
- handle(format("%s [%s]=%q,",depth,k,v))
- end
- elseif tk=="boolean" then
- handle(format("%s [%s]=%q,",depth,k and "true" or "false",v))
- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
- handle(format("%s %s=%q,",depth,k,v))
- else
- handle(format("%s [%q]=%q,",depth,k,v))
- end
- end
- elseif tv=="table" then
- if not next(v) then
- if tk=="number" then
- if hexify then
- handle(format("%s [0x%X]={},",depth,k))
- else
- handle(format("%s [%s]={},",depth,k))
- end
- elseif tk=="boolean" then
- handle(format("%s [%s]={},",depth,k and "true" or "false"))
- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
- handle(format("%s %s={},",depth,k))
- else
- handle(format("%s [%q]={},",depth,k))
- end
- elseif inline then
- local st=simple_table(v)
- if st then
- if tk=="number" then
- if hexify then
- handle(format("%s [0x%X]={ %s },",depth,k,concat(st,", ")))
- else
- handle(format("%s [%s]={ %s },",depth,k,concat(st,", ")))
- end
- elseif tk=="boolean" then
- handle(format("%s [%s]={ %s },",depth,k and "true" or "false",concat(st,", ")))
- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
- handle(format("%s %s={ %s },",depth,k,concat(st,", ")))
- else
- handle(format("%s [%q]={ %s },",depth,k,concat(st,", ")))
- end
- else
- do_serialize(v,k,depth,level+1)
- end
- else
- do_serialize(v,k,depth,level+1)
- end
- elseif tv=="boolean" then
- if tk=="number" then
- if hexify then
- handle(format("%s [0x%X]=%s,",depth,k,v and "true" or "false"))
- else
- handle(format("%s [%s]=%s,",depth,k,v and "true" or "false"))
- end
- elseif tk=="boolean" then
- handle(format("%s [%s]=%s,",depth,tostring(k),v and "true" or "false"))
- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
- handle(format("%s %s=%s,",depth,k,v and "true" or "false"))
- else
- handle(format("%s [%q]=%s,",depth,k,v and "true" or "false"))
- end
- elseif tv=="function" then
- if functions then
- local f=getinfo(v).what=="C" and dump(dummy) or dump(v)
- if tk=="number" then
- if hexify then
- handle(format("%s [0x%X]=load(%q),",depth,k,f))
- else
- handle(format("%s [%s]=load(%q),",depth,k,f))
- end
- elseif tk=="boolean" then
- handle(format("%s [%s]=load(%q),",depth,k and "true" or "false",f))
- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
- handle(format("%s %s=load(%q),",depth,k,f))
- else
- handle(format("%s [%q]=load(%q),",depth,k,f))
- end
- end
- else
- if tk=="number" then
- if hexify then
- handle(format("%s [0x%X]=%q,",depth,k,tostring(v)))
- else
- handle(format("%s [%s]=%q,",depth,k,tostring(v)))
- end
- elseif tk=="boolean" then
- handle(format("%s [%s]=%q,",depth,k and "true" or "false",tostring(v)))
- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
- handle(format("%s %s=%q,",depth,k,tostring(v)))
- else
- handle(format("%s [%q]=%q,",depth,k,tostring(v)))
- end
- end
- end
- end
- if level>0 then
- handle(format("%s},",depth))
- end
-end
-local function serialize(_handle,root,name,specification)
- local tname=type(name)
- if type(specification)=="table" then
- noquotes=specification.noquotes
- hexify=specification.hexify
- handle=_handle or specification.handle or print
- reduce=specification.reduce or false
- functions=specification.functions
- compact=specification.compact
- inline=specification.inline and compact
- if functions==nil then
- functions=true
- end
- if compact==nil then
- compact=true
- end
- if inline==nil then
- inline=compact
- end
- else
- noquotes=false
- hexify=false
- handle=_handle or print
- reduce=false
- compact=true
- inline=true
- functions=true
- end
- if tname=="string" then
- if name=="return" then
- handle("return {")
- else
- handle(name.."={")
- end
- elseif tname=="number" then
- if hexify then
- handle(format("[0x%X]={",name))
- else
- handle("["..name.."]={")
- end
- elseif tname=="boolean" then
- if name then
- handle("return {")
- else
- handle("{")
- end
- else
- handle("t={")
- end
- if root then
- if getmetatable(root) then
- local dummy=root._w_h_a_t_e_v_e_r_
- root._w_h_a_t_e_v_e_r_=nil
- end
- if next(root) then
- do_serialize(root,name,"",0)
- end
- end
- handle("}")
-end
-function table.serialize(root,name,specification)
- local t,n={},0
- local function flush(s)
- n=n+1
- t[n]=s
- end
- serialize(flush,root,name,specification)
- return concat(t,"\n")
-end
-table.tohandle=serialize
-local maxtab=2*1024
-function table.tofile(filename,root,name,specification)
- local f=io.open(filename,'w')
- if f then
- if maxtab>1 then
- local t,n={},0
- local function flush(s)
- n=n+1
- t[n]=s
- if n>maxtab then
- f:write(concat(t,"\n"),"\n")
- t,n={},0
- end
- end
- serialize(flush,root,name,specification)
- f:write(concat(t,"\n"),"\n")
- else
- local function flush(s)
- f:write(s,"\n")
- end
- serialize(flush,root,name,specification)
- end
- f:close()
- io.flush()
- end
-end
-local function flattened(t,f,depth)
- if f==nil then
- f={}
- depth=0xFFFF
- elseif tonumber(f) then
- depth=f
- f={}
- elseif not depth then
- depth=0xFFFF
- end
- for k,v in next,t do
- if type(k)~="number" then
- if depth>0 and type(v)=="table" then
- flattened(v,f,depth-1)
- else
- f[#f+1]=v
- end
- end
- end
- for k=1,#t do
- local v=t[k]
- if depth>0 and type(v)=="table" then
- flattened(v,f,depth-1)
- else
- f[#f+1]=v
- end
- end
- return f
-end
-table.flattened=flattened
-local function unnest(t,f)
- if not f then
- f={}
- end
- for i=1,#t do
- local v=t[i]
- if type(v)=="table" then
- if type(v[1])=="table" then
- unnest(v,f)
- else
- f[#f+1]=v
- end
- else
- f[#f+1]=v
- end
- end
- return f
-end
-function table.unnest(t)
- return unnest(t)
-end
-local function are_equal(a,b,n,m)
- if a and b and #a==#b then
- n=n or 1
- m=m or #a
- for i=n,m do
- local ai,bi=a[i],b[i]
- if ai==bi then
- elseif type(ai)=="table" and type(bi)=="table" then
- if not are_equal(ai,bi) then
- return false
- end
- else
- return false
- end
- end
- return true
- else
- return false
- end
-end
-local function identical(a,b)
- for ka,va in next,a do
- local vb=b[ka]
- if va==vb then
- elseif type(va)=="table" and type(vb)=="table" then
- if not identical(va,vb) then
- return false
- end
- else
- return false
- end
- end
- return true
-end
-table.identical=identical
-table.are_equal=are_equal
-local function sparse(old,nest,keeptables)
- local new={}
- for k,v in next,old do
- if not (v=="" or v==false) then
- if nest and type(v)=="table" then
- v=sparse(v,nest)
- if keeptables or next(v) then
- new[k]=v
- end
- else
- new[k]=v
- end
- end
- end
- return new
-end
-table.sparse=sparse
-function table.compact(t)
- return sparse(t,true,true)
-end
-function table.contains(t,v)
- if t then
- for i=1,#t do
- if t[i]==v then
- return i
- end
- end
- end
- return false
-end
-function table.count(t)
- local n=0
- for k,v in next,t do
- n=n+1
- end
- return n
-end
-function table.swapped(t,s)
- local n={}
- if s then
- for k,v in next,s do
- n[k]=v
- end
- end
- for k,v in next,t do
- n[v]=k
- end
- return n
-end
-function table.mirrored(t)
- local n={}
- for k,v in next,t do
- n[v]=k
- n[k]=v
- end
- return n
-end
-function table.reversed(t)
- if t then
- local tt,tn={},#t
- if tn>0 then
- local ttn=0
- for i=tn,1,-1 do
- ttn=ttn+1
- tt[ttn]=t[i]
- end
- end
- return tt
- end
-end
-function table.reverse(t)
- if t then
- local n=#t
- for i=1,floor(n/2) do
- local j=n-i+1
- t[i],t[j]=t[j],t[i]
- end
- return t
- end
-end
-function table.sequenced(t,sep,simple)
- if not t then
- return ""
- end
- local n=#t
- local s={}
- if n>0 then
- for i=1,n do
- s[i]=tostring(t[i])
- end
- else
- n=0
- for k,v in sortedhash(t) do
- if simple then
- if v==true then
- n=n+1
- s[n]=k
- elseif v and v~="" then
- n=n+1
- s[n]=k.."="..tostring(v)
- end
- else
- n=n+1
- s[n]=k.."="..tostring(v)
- end
- end
- end
- return concat(s,sep or " | ")
-end
-function table.print(t,...)
- if type(t)~="table" then
- print(tostring(t))
- else
- serialize(print,t,...)
- end
-end
-if setinspector then
- setinspector(function(v) if type(v)=="table" then serialize(print,v,"table") return true end end)
-end
-function table.sub(t,i,j)
- return { unpack(t,i,j) }
-end
-function table.is_empty(t)
- return not t or not next(t)
-end
-function table.has_one_entry(t)
- return t and not next(t,next(t))
-end
-function table.loweredkeys(t)
- local l={}
- for k,v in next,t do
- l[lower(k)]=v
- end
- return l
-end
-function table.unique(old)
- local hash={}
- local new={}
- local n=0
- for i=1,#old do
- local oi=old[i]
- if not hash[oi] then
- n=n+1
- new[n]=oi
- hash[oi]=true
- end
- end
- return new
-end
-function table.sorted(t,...)
- sort(t,...)
- return t
-end
-function table.values(t,s)
- if t then
- local values,keys,v={},{},0
- for key,value in next,t do
- if not keys[value] then
- v=v+1
- values[v]=value
- keys[k]=key
- end
- end
- if s then
- sort(values)
- end
- return values
- else
- return {}
- end
-end
-function table.filtered(t,pattern,sort,cmp)
- if t and type(pattern)=="string" then
- if sort then
- local s
- if cmp then
- s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end)
- else
- s=sortedkeys(t)
- end
- local n=0
- local m=#s
- local function kv(s)
- while n<m do
- n=n+1
- local k=s[n]
- if find(k,pattern) then
- return k,t[k]
- end
- end
- end
- return kv,s
- else
- local n=next(t)
- local function iterator()
- while n do
- local k=n
- n=next(t,k)
- if find(k,pattern) then
- return k,t[k]
- end
- end
- end
- return iterator,t
- end
- else
- return nothing
- end
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['l-io']={
- version=1.001,
- comment="companion to luat-lib.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local io=io
-local byte,find,gsub,format=string.byte,string.find,string.gsub,string.format
-local concat=table.concat
-local floor=math.floor
-local type=type
-if string.find(os.getenv("PATH"),";",1,true) then
- io.fileseparator,io.pathseparator="\\",";"
-else
- io.fileseparator,io.pathseparator="/",":"
-end
-local function readall(f)
- return f:read("*all")
-end
-local function readall(f)
- local size=f:seek("end")
- if size==0 then
- return ""
- elseif size<1024*1024 then
- f:seek("set",0)
- return f:read('*all')
- else
- local done=f:seek("set",0)
- local step
- if size<1024*1024 then
- step=1024*1024
- elseif size>16*1024*1024 then
- step=16*1024*1024
- else
- step=floor(size/(1024*1024))*1024*1024/8
- end
- local data={}
- while true do
- local r=f:read(step)
- if not r then
- return concat(data)
- else
- data[#data+1]=r
- end
- end
- end
-end
-io.readall=readall
-function io.loaddata(filename,textmode)
- local f=io.open(filename,(textmode and 'r') or 'rb')
- if f then
- local data=readall(f)
- f:close()
- if #data>0 then
- return data
- end
- end
-end
-function io.savedata(filename,data,joiner)
- local f=io.open(filename,"wb")
- if f then
- if type(data)=="table" then
- f:write(concat(data,joiner or ""))
- elseif type(data)=="function" then
- data(f)
- else
- f:write(data or "")
- end
- f:close()
- io.flush()
- return true
- else
- return false
- end
-end
-function io.loadlines(filename,n)
- local f=io.open(filename,'r')
- if not f then
- elseif n then
- local lines={}
- for i=1,n do
- local line=f:read("*lines")
- if line then
- lines[#lines+1]=line
- else
- break
- end
- end
- f:close()
- lines=concat(lines,"\n")
- if #lines>0 then
- return lines
- end
- else
- local line=f:read("*line") or ""
- f:close()
- if #line>0 then
- return line
- end
- end
-end
-function io.loadchunk(filename,n)
- local f=io.open(filename,'rb')
- if f then
- local data=f:read(n or 1024)
- f:close()
- if #data>0 then
- return data
- end
- end
-end
-function io.exists(filename)
- local f=io.open(filename)
- if f==nil then
- return false
- else
- f:close()
- return true
- end
-end
-function io.size(filename)
- local f=io.open(filename)
- if f==nil then
- return 0
- else
- local s=f:seek("end")
- f:close()
- return s
- end
-end
-function io.noflines(f)
- if type(f)=="string" then
- local f=io.open(filename)
- if f then
- local n=f and io.noflines(f) or 0
- f:close()
- return n
- else
- return 0
- end
- else
- local n=0
- for _ in f:lines() do
- n=n+1
- end
- f:seek('set',0)
- return n
- end
-end
-local nextchar={
- [ 4]=function(f)
- return f:read(1,1,1,1)
- end,
- [ 2]=function(f)
- return f:read(1,1)
- end,
- [ 1]=function(f)
- return f:read(1)
- end,
- [-2]=function(f)
- local a,b=f:read(1,1)
- return b,a
- end,
- [-4]=function(f)
- local a,b,c,d=f:read(1,1,1,1)
- return d,c,b,a
- end
-}
-function io.characters(f,n)
- if f then
- return nextchar[n or 1],f
- end
-end
-local nextbyte={
- [4]=function(f)
- local a,b,c,d=f:read(1,1,1,1)
- if d then
- return byte(a),byte(b),byte(c),byte(d)
- end
- end,
- [3]=function(f)
- local a,b,c=f:read(1,1,1)
- if b then
- return byte(a),byte(b),byte(c)
- end
- end,
- [2]=function(f)
- local a,b=f:read(1,1)
- if b then
- return byte(a),byte(b)
- end
- end,
- [1]=function (f)
- local a=f:read(1)
- if a then
- return byte(a)
- end
- end,
- [-2]=function (f)
- local a,b=f:read(1,1)
- if b then
- return byte(b),byte(a)
- end
- end,
- [-3]=function(f)
- local a,b,c=f:read(1,1,1)
- if b then
- return byte(c),byte(b),byte(a)
- end
- end,
- [-4]=function(f)
- local a,b,c,d=f:read(1,1,1,1)
- if d then
- return byte(d),byte(c),byte(b),byte(a)
- end
- end
-}
-function io.bytes(f,n)
- if f then
- return nextbyte[n or 1],f
- else
- return nil,nil
- end
-end
-function io.ask(question,default,options)
- while true do
- io.write(question)
- if options then
- io.write(format(" [%s]",concat(options,"|")))
- end
- if default then
- io.write(format(" [%s]",default))
- end
- io.write(format(" "))
- io.flush()
- local answer=io.read()
- answer=gsub(answer,"^%s*(.*)%s*$","%1")
- if answer=="" and default then
- return default
- elseif not options then
- return answer
- else
- for k=1,#options do
- if options[k]==answer then
- return answer
- end
- end
- local pattern="^"..answer
- for k=1,#options do
- local v=options[k]
- if find(v,pattern) then
- return v
- end
- end
- end
- end
-end
-local function readnumber(f,n,m)
- if m then
- f:seek("set",n)
- n=m
- end
- if n==1 then
- return byte(f:read(1))
- elseif n==2 then
- local a,b=byte(f:read(2),1,2)
- return 256*a+b
- elseif n==3 then
- local a,b,c=byte(f:read(3),1,3)
- return 256*256*a+256*b+c
- elseif n==4 then
- local a,b,c,d=byte(f:read(4),1,4)
- return 256*256*256*a+256*256*b+256*c+d
- elseif n==8 then
- local a,b=readnumber(f,4),readnumber(f,4)
- return 256*a+b
- elseif n==12 then
- local a,b,c=readnumber(f,4),readnumber(f,4),readnumber(f,4)
- return 256*256*a+256*b+c
- elseif n==-2 then
- local b,a=byte(f:read(2),1,2)
- return 256*a+b
- elseif n==-3 then
- local c,b,a=byte(f:read(3),1,3)
- return 256*256*a+256*b+c
- elseif n==-4 then
- local d,c,b,a=byte(f:read(4),1,4)
- return 256*256*256*a+256*256*b+256*c+d
- elseif n==-8 then
- local h,g,f,e,d,c,b,a=byte(f:read(8),1,8)
- return 256*256*256*256*256*256*256*a+256*256*256*256*256*256*b+256*256*256*256*256*c+256*256*256*256*d+256*256*256*e+256*256*f+256*g+h
- else
- return 0
- end
-end
-io.readnumber=readnumber
-function io.readstring(f,n,m)
- if m then
- f:seek("set",n)
- n=m
- end
- local str=gsub(f:read(n),"\000","")
- return str
-end
-if not io.i_limiter then function io.i_limiter() end end
-if not io.o_limiter then function io.o_limiter() end end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['l-file']={
- version=1.001,
- comment="companion to luat-lib.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-file=file or {}
-local file=file
-if not lfs then
- lfs=optionalrequire("lfs")
-end
-if not lfs then
- lfs={
- getcurrentdir=function()
- return "."
- end,
- attributes=function()
- return nil
- end,
- isfile=function(name)
- local f=io.open(name,'rb')
- if f then
- f:close()
- return true
- end
- end,
- isdir=function(name)
- print("you need to load lfs")
- return false
- end
- }
-elseif not lfs.isfile then
- local attributes=lfs.attributes
- function lfs.isdir(name)
- return attributes(name,"mode")=="directory"
- end
- function lfs.isfile(name)
- return attributes(name,"mode")=="file"
- end
-end
-local insert,concat=table.insert,table.concat
-local match,find,gmatch=string.match,string.find,string.gmatch
-local lpegmatch=lpeg.match
-local getcurrentdir,attributes=lfs.currentdir,lfs.attributes
-local checkedsplit=string.checkedsplit
-local P,R,S,C,Cs,Cp,Cc,Ct=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Cp,lpeg.Cc,lpeg.Ct
-local colon=P(":")
-local period=P(".")
-local periods=P("..")
-local fwslash=P("/")
-local bwslash=P("\\")
-local slashes=S("\\/")
-local noperiod=1-period
-local noslashes=1-slashes
-local name=noperiod^1
-local suffix=period/""*(1-period-slashes)^1*-1
-local pattern=C((1-(slashes^1*noslashes^1*-1))^1)*P(1)
-local function pathpart(name,default)
- return name and lpegmatch(pattern,name) or default or ""
-end
-local pattern=(noslashes^0*slashes)^1*C(noslashes^1)*-1
-local function basename(name)
- return name and lpegmatch(pattern,name) or name
-end
-local pattern=(noslashes^0*slashes^1)^0*Cs((1-suffix)^1)*suffix^0
-local function nameonly(name)
- return name and lpegmatch(pattern,name) or name
-end
-local pattern=(noslashes^0*slashes)^0*(noperiod^1*period)^1*C(noperiod^1)*-1
-local function suffixonly(name)
- return name and lpegmatch(pattern,name) or ""
-end
-local pattern=(noslashes^0*slashes)^0*noperiod^1*((period*C(noperiod^1))^1)*-1+Cc("")
-local function suffixesonly(name)
- if name then
- return lpegmatch(pattern,name)
- else
- return ""
- end
-end
-file.pathpart=pathpart
-file.basename=basename
-file.nameonly=nameonly
-file.suffixonly=suffixonly
-file.suffix=suffixonly
-file.suffixesonly=suffixesonly
-file.suffixes=suffixesonly
-file.dirname=pathpart
-file.extname=suffixonly
-local drive=C(R("az","AZ"))*colon
-local path=C((noslashes^0*slashes)^0)
-local suffix=period*C(P(1-period)^0*P(-1))
-local base=C((1-suffix)^0)
-local rest=C(P(1)^0)
-drive=drive+Cc("")
-path=path+Cc("")
-base=base+Cc("")
-suffix=suffix+Cc("")
-local pattern_a=drive*path*base*suffix
-local pattern_b=path*base*suffix
-local pattern_c=C(drive*path)*C(base*suffix)
-local pattern_d=path*rest
-function file.splitname(str,splitdrive)
- if not str then
- elseif splitdrive then
- return lpegmatch(pattern_a,str)
- else
- return lpegmatch(pattern_b,str)
- end
-end
-function file.splitbase(str)
- if str then
- return lpegmatch(pattern_d,str)
- else
- return "",str
- end
-end
-function file.nametotable(str,splitdrive)
- if str then
- local path,drive,subpath,name,base,suffix=lpegmatch(pattern_c,str)
- if splitdrive then
- return {
- path=path,
- drive=drive,
- subpath=subpath,
- name=name,
- base=base,
- suffix=suffix,
- }
- else
- return {
- path=path,
- name=name,
- base=base,
- suffix=suffix,
- }
- end
- end
-end
-local pattern=Cs(((period*(1-period-slashes)^1*-1)/""+1)^1)
-function file.removesuffix(name)
- return name and lpegmatch(pattern,name)
-end
-local suffix=period/""*(1-period-slashes)^1*-1
-local pattern=Cs((noslashes^0*slashes^1)^0*((1-suffix)^1))*Cs(suffix)
-function file.addsuffix(filename,suffix,criterium)
- if not filename or not suffix or suffix=="" then
- return filename
- elseif criterium==true then
- return filename.."."..suffix
- elseif not criterium then
- local n,s=lpegmatch(pattern,filename)
- if not s or s=="" then
- return filename.."."..suffix
- else
- return filename
- end
- else
- local n,s=lpegmatch(pattern,filename)
- if s and s~="" then
- local t=type(criterium)
- if t=="table" then
- for i=1,#criterium do
- if s==criterium[i] then
- return filename
- end
- end
- elseif t=="string" then
- if s==criterium then
- return filename
- end
- end
- end
- return (n or filename).."."..suffix
- end
-end
-local suffix=period*(1-period-slashes)^1*-1
-local pattern=Cs((1-suffix)^0)
-function file.replacesuffix(name,suffix)
- if name and suffix and suffix~="" then
- return lpegmatch(pattern,name).."."..suffix
- else
- return name
- end
-end
-local reslasher=lpeg.replacer(P("\\"),"/")
-function file.reslash(str)
- return str and lpegmatch(reslasher,str)
-end
-function file.is_writable(name)
- if not name then
- elseif lfs.isdir(name) then
- name=name.."/m_t_x_t_e_s_t.tmp"
- local f=io.open(name,"wb")
- if f then
- f:close()
- os.remove(name)
- return true
- end
- elseif lfs.isfile(name) then
- local f=io.open(name,"ab")
- if f then
- f:close()
- return true
- end
- else
- local f=io.open(name,"ab")
- if f then
- f:close()
- os.remove(name)
- return true
- end
- end
- return false
-end
-local readable=P("r")*Cc(true)
-function file.is_readable(name)
- if name then
- local a=attributes(name)
- return a and lpegmatch(readable,a.permissions) or false
- else
- return false
- end
-end
-file.isreadable=file.is_readable
-file.iswritable=file.is_writable
-function file.size(name)
- if name then
- local a=attributes(name)
- return a and a.size or 0
- else
- return 0
- end
-end
-function file.splitpath(str,separator)
- return str and checkedsplit(lpegmatch(reslasher,str),separator or io.pathseparator)
-end
-function file.joinpath(tab,separator)
- return tab and concat(tab,separator or io.pathseparator)
-end
-local someslash=S("\\/")
-local stripper=Cs(P(fwslash)^0/""*reslasher)
-local isnetwork=someslash*someslash*(1-someslash)+(1-fwslash-colon)^1*colon
-local isroot=fwslash^1*-1
-local hasroot=fwslash^1
-local reslasher=lpeg.replacer(S("\\/"),"/")
-local deslasher=lpeg.replacer(S("\\/")^1,"/")
-function file.join(one,two,three,...)
- if not two then
- return one=="" and one or lpegmatch(stripper,one)
- end
- if one=="" then
- return lpegmatch(stripper,three and concat({ two,three,... },"/") or two)
- end
- if lpegmatch(isnetwork,one) then
- local one=lpegmatch(reslasher,one)
- local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two)
- if lpegmatch(hasroot,two) then
- return one..two
- else
- return one.."/"..two
- end
- elseif lpegmatch(isroot,one) then
- local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two)
- if lpegmatch(hasroot,two) then
- return two
- else
- return "/"..two
- end
- else
- return lpegmatch(deslasher,concat({ one,two,three,... },"/"))
- end
-end
-local drivespec=R("az","AZ")^1*colon
-local anchors=fwslash+drivespec
-local untouched=periods+(1-period)^1*P(-1)
-local mswindrive=Cs(drivespec*(bwslash/"/"+fwslash)^0)
-local mswinuncpath=(bwslash+fwslash)*(bwslash+fwslash)*Cc("//")
-local splitstarter=(mswindrive+mswinuncpath+Cc(false))*Ct(lpeg.splitat(S("/\\")^1))
-local absolute=fwslash
-function file.collapsepath(str,anchor)
- if not str then
- return
- end
- if anchor==true and not lpegmatch(anchors,str) then
- str=getcurrentdir().."/"..str
- end
- if str=="" or str=="." then
- return "."
- elseif lpegmatch(untouched,str) then
- return lpegmatch(reslasher,str)
- end
- local starter,oldelements=lpegmatch(splitstarter,str)
- local newelements={}
- local i=#oldelements
- while i>0 do
- local element=oldelements[i]
- if element=='.' then
- elseif element=='..' then
- local n=i-1
- while n>0 do
- local element=oldelements[n]
- if element~='..' and element~='.' then
- oldelements[n]='.'
- break
- else
- n=n-1
- end
- end
- if n<1 then
- insert(newelements,1,'..')
- end
- elseif element~="" then
- insert(newelements,1,element)
- end
- i=i-1
- end
- if #newelements==0 then
- return starter or "."
- elseif starter then
- return starter..concat(newelements,'/')
- elseif lpegmatch(absolute,str) then
- return "/"..concat(newelements,'/')
- else
- newelements=concat(newelements,'/')
- if anchor=="." and find(str,"^%./") then
- return "./"..newelements
- else
- return newelements
- end
- end
-end
-local tricky=S("/\\")*P(-1)
-local attributes=lfs.attributes
-function lfs.isdir(name)
- if lpegmatch(tricky,name) then
- return attributes(name,"mode")=="directory"
- else
- return attributes(name.."/.","mode")=="directory"
- end
-end
-function lfs.isfile(name)
- return attributes(name,"mode")=="file"
-end
-local validchars=R("az","09","AZ","--","..")
-local pattern_a=lpeg.replacer(1-validchars)
-local pattern_a=Cs((validchars+P(1)/"-")^1)
-local whatever=P("-")^0/""
-local pattern_b=Cs(whatever*(1-whatever*-1)^1)
-function file.robustname(str,strict)
- if str then
- str=lpegmatch(pattern_a,str) or str
- if strict then
- return lpegmatch(pattern_b,str) or str
- else
- return str
- end
- end
-end
-file.readdata=io.loaddata
-file.savedata=io.savedata
-function file.copy(oldname,newname)
- if oldname and newname then
- local data=io.loaddata(oldname)
- if data and data~="" then
- file.savedata(newname,data)
- end
- end
-end
-local letter=R("az","AZ")+S("_-+")
-local separator=P("://")
-local qualified=period^0*fwslash+letter*colon+letter^1*separator+letter^1*fwslash
-local rootbased=fwslash+letter*colon
-lpeg.patterns.qualified=qualified
-lpeg.patterns.rootbased=rootbased
-function file.is_qualified_path(filename)
- return filename and lpegmatch(qualified,filename)~=nil
-end
-function file.is_rootbased_path(filename)
- return filename and lpegmatch(rootbased,filename)~=nil
-end
-function file.strip(name,dir)
- if name then
- local b,a=match(name,"^(.-)"..dir.."(.*)$")
- return a~="" and a or name
- end
-end
-function lfs.mkdirs(path)
- local full=""
- for sub in gmatch(path,"(/*[^\\/]+)") do
- full=full..sub
- lfs.mkdir(full)
- end
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['l-boolean']={
- version=1.001,
- comment="companion to luat-lib.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local type,tonumber=type,tonumber
-boolean=boolean or {}
-local boolean=boolean
-function boolean.tonumber(b)
- if b then return 1 else return 0 end
-end
-function toboolean(str,tolerant)
- if str==nil then
- return false
- elseif str==false then
- return false
- elseif str==true then
- return true
- elseif str=="true" then
- return true
- elseif str=="false" then
- return false
- elseif not tolerant then
- return false
- elseif str==0 then
- return false
- elseif (tonumber(str) or 0)>0 then
- return true
- else
- return str=="yes" or str=="on" or str=="t"
- end
-end
-string.toboolean=toboolean
-function string.booleanstring(str)
- if str=="0" then
- return false
- elseif str=="1" then
- return true
- elseif str=="" then
- return false
- elseif str=="false" then
- return false
- elseif str=="true" then
- return true
- elseif (tonumber(str) or 0)>0 then
- return true
- else
- return str=="yes" or str=="on" or str=="t"
- end
-end
-function string.is_boolean(str,default)
- if type(str)=="string" then
- if str=="true" or str=="yes" or str=="on" or str=="t" or str=="1" then
- return true
- elseif str=="false" or str=="no" or str=="off" or str=="f" or str=="0" then
- return false
- end
- end
- return default
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['l-math']={
- version=1.001,
- comment="companion to luat-lib.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local floor,sin,cos,tan=math.floor,math.sin,math.cos,math.tan
-if not math.round then
- function math.round(x) return floor(x+0.5) end
-end
-if not math.div then
- function math.div(n,m) return floor(n/m) end
-end
-if not math.mod then
- function math.mod(n,m) return n%m end
-end
-local pipi=2*math.pi/360
-if not math.sind then
- function math.sind(d) return sin(d*pipi) end
- function math.cosd(d) return cos(d*pipi) end
- function math.tand(d) return tan(d*pipi) end
-end
-if not math.odd then
- function math.odd (n) return n%2~=0 end
- function math.even(n) return n%2==0 end
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['util-str']={
- version=1.001,
- comment="companion to luat-lib.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-utilities=utilities or {}
-utilities.strings=utilities.strings or {}
-local strings=utilities.strings
-local format,gsub,rep,sub=string.format,string.gsub,string.rep,string.sub
-local load,dump=load,string.dump
-local tonumber,type,tostring=tonumber,type,tostring
-local unpack,concat=table.unpack,table.concat
-local P,V,C,S,R,Ct,Cs,Cp,Carg,Cc=lpeg.P,lpeg.V,lpeg.C,lpeg.S,lpeg.R,lpeg.Ct,lpeg.Cs,lpeg.Cp,lpeg.Carg,lpeg.Cc
-local patterns,lpegmatch=lpeg.patterns,lpeg.match
-local utfchar,utfbyte=utf.char,utf.byte
-local loadstripped=nil
-if _LUAVERSION<5.2 then
- loadstripped=function(str,shortcuts)
- return load(str)
- end
-else
- loadstripped=function(str,shortcuts)
- if shortcuts then
- return load(dump(load(str),true),nil,nil,shortcuts)
- else
- return load(dump(load(str),true))
- end
- end
-end
-if not number then number={} end
-local stripper=patterns.stripzeros
-local function points(n)
- n=tonumber(n)
- return (not n or n==0) and "0pt" or lpegmatch(stripper,format("%.5fpt",n/65536))
-end
-local function basepoints(n)
- n=tonumber(n)
- return (not n or n==0) and "0bp" or lpegmatch(stripper,format("%.5fbp",n*(7200/7227)/65536))
-end
-number.points=points
-number.basepoints=basepoints
-local rubish=patterns.spaceortab^0*patterns.newline
-local anyrubish=patterns.spaceortab+patterns.newline
-local anything=patterns.anything
-local stripped=(patterns.spaceortab^1/"")*patterns.newline
-local leading=rubish^0/""
-local trailing=(anyrubish^1*patterns.endofstring)/""
-local redundant=rubish^3/"\n"
-local pattern=Cs(leading*(trailing+redundant+stripped+anything)^0)
-function strings.collapsecrlf(str)
- return lpegmatch(pattern,str)
-end
-local repeaters={}
-function strings.newrepeater(str,offset)
- offset=offset or 0
- local s=repeaters[str]
- if not s then
- s={}
- repeaters[str]=s
- end
- local t=s[offset]
- if t then
- return t
- end
- t={}
- setmetatable(t,{ __index=function(t,k)
- if not k then
- return ""
- end
- local n=k+offset
- local s=n>0 and rep(str,n) or ""
- t[k]=s
- return s
- end })
- s[offset]=t
- return t
-end
-local extra,tab,start=0,0,4,0
-local nspaces=strings.newrepeater(" ")
-string.nspaces=nspaces
-local pattern=Carg(1)/function(t)
- extra,tab,start=0,t or 7,1
- end*Cs((
- Cp()*patterns.tab/function(position)
- local current=(position-start+1)+extra
- local spaces=tab-(current-1)%tab
- if spaces>0 then
- extra=extra+spaces-1
- return nspaces[spaces]
- else
- return ""
- end
- end+patterns.newline*Cp()/function(position)
- extra,start=0,position
- end+patterns.anything
- )^1)
-function strings.tabtospace(str,tab)
- return lpegmatch(pattern,str,1,tab or 7)
-end
-local newline=patterns.newline
-local endofstring=patterns.endofstring
-local whitespace=patterns.whitespace
-local spacer=patterns.spacer
-local space=spacer^0
-local nospace=space/""
-local endofline=nospace*newline
-local stripend=(whitespace^1*endofstring)/""
-local normalline=(nospace*((1-space*(newline+endofstring))^1)*nospace)
-local stripempty=endofline^1/""
-local normalempty=endofline^1
-local singleempty=endofline*(endofline^0/"")
-local doubleempty=endofline*endofline^-1*(endofline^0/"")
-local stripstart=stripempty^0
-local p_prune_normal=Cs (stripstart*(stripend+normalline+normalempty )^0 )
-local p_prune_collapse=Cs (stripstart*(stripend+normalline+doubleempty )^0 )
-local p_prune_noempty=Cs (stripstart*(stripend+normalline+singleempty )^0 )
-local p_retain_normal=Cs ((normalline+normalempty )^0 )
-local p_retain_collapse=Cs ((normalline+doubleempty )^0 )
-local p_retain_noempty=Cs ((normalline+singleempty )^0 )
-local striplinepatterns={
- ["prune"]=p_prune_normal,
- ["prune and collapse"]=p_prune_collapse,
- ["prune and no empty"]=p_prune_noempty,
- ["retain"]=p_retain_normal,
- ["retain and collapse"]=p_retain_collapse,
- ["retain and no empty"]=p_retain_noempty,
- ["collapse"]=patterns.collapser,
-}
-strings.striplinepatterns=striplinepatterns
-function strings.striplines(str,how)
- return str and lpegmatch(how and striplinepatterns[how] or p_prune_collapse,str) or str
-end
-strings.striplong=strings.striplines
-function strings.nice(str)
- str=gsub(str,"[:%-+_]+"," ")
- return str
-end
-local n=0
-local sequenced=table.sequenced
-function string.autodouble(s,sep)
- if s==nil then
- return '""'
- end
- local t=type(s)
- if t=="number" then
- return tostring(s)
- end
- if t=="table" then
- return ('"'..sequenced(s,sep or ",")..'"')
- end
- return ('"'..tostring(s)..'"')
-end
-function string.autosingle(s,sep)
- if s==nil then
- return "''"
- end
- local t=type(s)
- if t=="number" then
- return tostring(s)
- end
- if t=="table" then
- return ("'"..sequenced(s,sep or ",").."'")
- end
- return ("'"..tostring(s).."'")
-end
-local tracedchars={}
-string.tracedchars=tracedchars
-strings.tracers=tracedchars
-function string.tracedchar(b)
- if type(b)=="number" then
- return tracedchars[b] or (utfchar(b).." (U+"..format('%05X',b)..")")
- else
- local c=utfbyte(b)
- return tracedchars[c] or (b.." (U+"..format('%05X',c)..")")
- end
-end
-function number.signed(i)
- if i>0 then
- return "+",i
- else
- return "-",-i
- end
-end
-local zero=P("0")^1/""
-local plus=P("+")/""
-local minus=P("-")
-local separator=S(".")
-local digit=R("09")
-local trailing=zero^1*#S("eE")
-local exponent=(S("eE")*(plus+Cs((minus*zero^0*P(-1))/"")+minus)*zero^0*(P(-1)*Cc("0")+P(1)^1))
-local pattern_a=Cs(minus^0*digit^1*(separator/""*trailing+separator*(trailing+digit)^0)*exponent)
-local pattern_b=Cs((exponent+P(1))^0)
-function number.sparseexponent(f,n)
- if not n then
- n=f
- f="%e"
- end
- local tn=type(n)
- if tn=="string" then
- local m=tonumber(n)
- if m then
- return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,m))
- end
- elseif tn=="number" then
- return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,n))
- end
- return tostring(n)
-end
-local template=[[
-%s
-%s
-return function(%s) return %s end
-]]
-local preamble,environment="",{}
-if _LUAVERSION<5.2 then
- preamble=[[
-local lpeg=lpeg
-local type=type
-local tostring=tostring
-local tonumber=tonumber
-local format=string.format
-local concat=table.concat
-local signed=number.signed
-local points=number.points
-local basepoints= number.basepoints
-local utfchar=utf.char
-local utfbyte=utf.byte
-local lpegmatch=lpeg.match
-local nspaces=string.nspaces
-local tracedchar=string.tracedchar
-local autosingle=string.autosingle
-local autodouble=string.autodouble
-local sequenced=table.sequenced
-local formattednumber=number.formatted
-local sparseexponent=number.sparseexponent
- ]]
-else
- environment={
- global=global or _G,
- lpeg=lpeg,
- type=type,
- tostring=tostring,
- tonumber=tonumber,
- format=string.format,
- concat=table.concat,
- signed=number.signed,
- points=number.points,
- basepoints=number.basepoints,
- utfchar=utf.char,
- utfbyte=utf.byte,
- lpegmatch=lpeg.match,
- nspaces=string.nspaces,
- tracedchar=string.tracedchar,
- autosingle=string.autosingle,
- autodouble=string.autodouble,
- sequenced=table.sequenced,
- formattednumber=number.formatted,
- sparseexponent=number.sparseexponent,
- }
-end
-local arguments={ "a1" }
-setmetatable(arguments,{ __index=function(t,k)
- local v=t[k-1]..",a"..k
- t[k]=v
- return v
- end
-})
-local prefix_any=C((S("+- .")+R("09"))^0)
-local prefix_tab=P("{")*C((1-P("}"))^0)*P("}")+C((1-R("az","AZ","09","%%"))^0)
-local format_s=function(f)
- n=n+1
- if f and f~="" then
- return format("format('%%%ss',a%s)",f,n)
- else
- return format("(a%s or '')",n)
- end
-end
-local format_S=function(f)
- n=n+1
- if f and f~="" then
- return format("format('%%%ss',tostring(a%s))",f,n)
- else
- return format("tostring(a%s)",n)
- end
-end
-local format_q=function()
- n=n+1
- return format("(a%s and format('%%q',a%s) or '')",n,n)
-end
-local format_Q=function()
- n=n+1
- return format("format('%%q',tostring(a%s))",n)
-end
-local format_i=function(f)
- n=n+1
- if f and f~="" then
- return format("format('%%%si',a%s)",f,n)
- else
- return format("format('%%i',a%s)",n)
- end
-end
-local format_d=format_i
-local format_I=function(f)
- n=n+1
- return format("format('%%s%%%si',signed(a%s))",f,n)
-end
-local format_f=function(f)
- n=n+1
- return format("format('%%%sf',a%s)",f,n)
-end
-local format_F=function()
- n=n+1
- if not f or f=="" then
- return format("(((a%s > -0.0000000005 and a%s < 0.0000000005) and '0') or format((a%s %% 1 == 0) and '%%i' or '%%.9f',a%s))",n,n,n,n)
- else
- return format("format((a%s %% 1 == 0) and '%%i' or '%%%sf',a%s)",n,f,n)
- end
-end
-local format_g=function(f)
- n=n+1
- return format("format('%%%sg',a%s)",f,n)
-end
-local format_G=function(f)
- n=n+1
- return format("format('%%%sG',a%s)",f,n)
-end
-local format_e=function(f)
- n=n+1
- return format("format('%%%se',a%s)",f,n)
-end
-local format_E=function(f)
- n=n+1
- return format("format('%%%sE',a%s)",f,n)
-end
-local format_j=function(f)
- n=n+1
- return format("sparseexponent('%%%se',a%s)",f,n)
-end
-local format_J=function(f)
- n=n+1
- return format("sparseexponent('%%%sE',a%s)",f,n)
-end
-local format_x=function(f)
- n=n+1
- return format("format('%%%sx',a%s)",f,n)
-end
-local format_X=function(f)
- n=n+1
- return format("format('%%%sX',a%s)",f,n)
-end
-local format_o=function(f)
- n=n+1
- return format("format('%%%so',a%s)",f,n)
-end
-local format_c=function()
- n=n+1
- return format("utfchar(a%s)",n)
-end
-local format_C=function()
- n=n+1
- return format("tracedchar(a%s)",n)
-end
-local format_r=function(f)
- n=n+1
- return format("format('%%%s.0f',a%s)",f,n)
-end
-local format_h=function(f)
- n=n+1
- if f=="-" then
- f=sub(f,2)
- return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
- else
- return format("format('0x%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
- end
-end
-local format_H=function(f)
- n=n+1
- if f=="-" then
- f=sub(f,2)
- return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
- else
- return format("format('0x%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
- end
-end
-local format_u=function(f)
- n=n+1
- if f=="-" then
- f=sub(f,2)
- return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
- else
- return format("format('u+%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
- end
-end
-local format_U=function(f)
- n=n+1
- if f=="-" then
- f=sub(f,2)
- return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
- else
- return format("format('U+%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
- end
-end
-local format_p=function()
- n=n+1
- return format("points(a%s)",n)
-end
-local format_b=function()
- n=n+1
- return format("basepoints(a%s)",n)
-end
-local format_t=function(f)
- n=n+1
- if f and f~="" then
- return format("concat(a%s,%q)",n,f)
- else
- return format("concat(a%s)",n)
- end
-end
-local format_T=function(f)
- n=n+1
- if f and f~="" then
- return format("sequenced(a%s,%q)",n,f)
- else
- return format("sequenced(a%s)",n)
- end
-end
-local format_l=function()
- n=n+1
- return format("(a%s and 'true' or 'false')",n)
-end
-local format_L=function()
- n=n+1
- return format("(a%s and 'TRUE' or 'FALSE')",n)
-end
-local format_N=function()
- n=n+1
- return format("tostring(tonumber(a%s) or a%s)",n,n)
-end
-local format_a=function(f)
- n=n+1
- if f and f~="" then
- return format("autosingle(a%s,%q)",n,f)
- else
- return format("autosingle(a%s)",n)
- end
-end
-local format_A=function(f)
- n=n+1
- if f and f~="" then
- return format("autodouble(a%s,%q)",n,f)
- else
- return format("autodouble(a%s)",n)
- end
-end
-local format_w=function(f)
- n=n+1
- f=tonumber(f)
- if f then
- return format("nspaces[%s+a%s]",f,n)
- else
- return format("nspaces[a%s]",n)
- end
-end
-local format_W=function(f)
- return format("nspaces[%s]",tonumber(f) or 0)
-end
-local digit=patterns.digit
-local period=patterns.period
-local three=digit*digit*digit
-local splitter=Cs (
- (((1-(three^1*period))^1+C(three))*(Carg(1)*three)^1+C((1-period)^1))*(P(1)/""*Carg(2))*C(2)
-)
-patterns.formattednumber=splitter
-function number.formatted(n,sep1,sep2)
- local s=type(s)=="string" and n or format("%0.2f",n)
- if sep1==true then
- return lpegmatch(splitter,s,1,".",",")
- elseif sep1=="." then
- return lpegmatch(splitter,s,1,sep1,sep2 or ",")
- elseif sep1=="," then
- return lpegmatch(splitter,s,1,sep1,sep2 or ".")
- else
- return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".")
- end
-end
-local format_m=function(f)
- n=n+1
- if not f or f=="" then
- f=","
- end
- return format([[formattednumber(a%s,%q,".")]],n,f)
-end
-local format_M=function(f)
- n=n+1
- if not f or f=="" then
- f="."
- end
- return format([[formattednumber(a%s,%q,",")]],n,f)
-end
-local format_z=function(f)
- n=n+(tonumber(f) or 1)
- return "''"
-end
-local format_rest=function(s)
- return format("%q",s)
-end
-local format_extension=function(extensions,f,name)
- local extension=extensions[name] or "tostring(%s)"
- local f=tonumber(f) or 1
- if f==0 then
- return extension
- elseif f==1 then
- n=n+1
- local a="a"..n
- return format(extension,a,a)
- elseif f<0 then
- local a="a"..(n+f+1)
- return format(extension,a,a)
- else
- local t={}
- for i=1,f do
- n=n+1
- t[#t+1]="a"..n
- end
- return format(extension,unpack(t))
- end
-end
-local builder=Cs { "start",
- start=(
- (
- P("%")/""*(
- V("!")
-+V("s")+V("q")+V("i")+V("d")+V("f")+V("F")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o")
-+V("c")+V("C")+V("S")
-+V("Q")
-+V("N")
-+V("r")+V("h")+V("H")+V("u")+V("U")+V("p")+V("b")+V("t")+V("T")+V("l")+V("L")+V("I")+V("w")
-+V("W")
-+V("a")
-+V("A")
-+V("j")+V("J")
-+V("m")+V("M")
-+V("z")
-+V("*")
- )+V("*")
- )*(P(-1)+Carg(1))
- )^0,
- ["s"]=(prefix_any*P("s"))/format_s,
- ["q"]=(prefix_any*P("q"))/format_q,
- ["i"]=(prefix_any*P("i"))/format_i,
- ["d"]=(prefix_any*P("d"))/format_d,
- ["f"]=(prefix_any*P("f"))/format_f,
- ["F"]=(prefix_any*P("F"))/format_F,
- ["g"]=(prefix_any*P("g"))/format_g,
- ["G"]=(prefix_any*P("G"))/format_G,
- ["e"]=(prefix_any*P("e"))/format_e,
- ["E"]=(prefix_any*P("E"))/format_E,
- ["x"]=(prefix_any*P("x"))/format_x,
- ["X"]=(prefix_any*P("X"))/format_X,
- ["o"]=(prefix_any*P("o"))/format_o,
- ["S"]=(prefix_any*P("S"))/format_S,
- ["Q"]=(prefix_any*P("Q"))/format_S,
- ["N"]=(prefix_any*P("N"))/format_N,
- ["c"]=(prefix_any*P("c"))/format_c,
- ["C"]=(prefix_any*P("C"))/format_C,
- ["r"]=(prefix_any*P("r"))/format_r,
- ["h"]=(prefix_any*P("h"))/format_h,
- ["H"]=(prefix_any*P("H"))/format_H,
- ["u"]=(prefix_any*P("u"))/format_u,
- ["U"]=(prefix_any*P("U"))/format_U,
- ["p"]=(prefix_any*P("p"))/format_p,
- ["b"]=(prefix_any*P("b"))/format_b,
- ["t"]=(prefix_tab*P("t"))/format_t,
- ["T"]=(prefix_tab*P("T"))/format_T,
- ["l"]=(prefix_any*P("l"))/format_l,
- ["L"]=(prefix_any*P("L"))/format_L,
- ["I"]=(prefix_any*P("I"))/format_I,
- ["w"]=(prefix_any*P("w"))/format_w,
- ["W"]=(prefix_any*P("W"))/format_W,
- ["j"]=(prefix_any*P("j"))/format_j,
- ["J"]=(prefix_any*P("J"))/format_J,
- ["m"]=(prefix_tab*P("m"))/format_m,
- ["M"]=(prefix_tab*P("M"))/format_M,
- ["z"]=(prefix_any*P("z"))/format_z,
- ["a"]=(prefix_any*P("a"))/format_a,
- ["A"]=(prefix_any*P("A"))/format_A,
- ["*"]=Cs(((1-P("%"))^1+P("%%")/"%%")^1)/format_rest,
- ["!"]=Carg(2)*prefix_any*P("!")*C((1-P("!"))^1)*P("!")/format_extension,
-}
-local direct=Cs (
- P("%")*(S("+- .")+R("09"))^0*S("sqidfgGeExXo")*P(-1)/[[local format = string.format return function(str) return format("%0",str) end]]
-)
-local function make(t,str)
- local f
- local p
- local p=lpegmatch(direct,str)
- if p then
- f=loadstripped(p)()
- else
- n=0
- p=lpegmatch(builder,str,1,t._connector_,t._extensions_)
- if n>0 then
- p=format(template,preamble,t._preamble_,arguments[n],p)
- f=loadstripped(p,t._environment_)()
- else
- f=function() return str end
- end
- end
- t[str]=f
- return f
-end
-local function use(t,fmt,...)
- return t[fmt](...)
-end
-strings.formatters={}
-if _LUAVERSION<5.2 then
- function strings.formatters.new(noconcat)
- local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_=preamble,_environment_={} }
- setmetatable(t,{ __index=make,__call=use })
- return t
- end
-else
- function strings.formatters.new(noconcat)
- local e={}
- for k,v in next,environment do
- e[k]=v
- end
- local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_="",_environment_=e }
- setmetatable(t,{ __index=make,__call=use })
- return t
- end
-end
-local formatters=strings.formatters.new()
-string.formatters=formatters
-string.formatter=function(str,...) return formatters[str](...) end
-local function add(t,name,template,preamble)
- if type(t)=="table" and t._type_=="formatter" then
- t._extensions_[name]=template or "%s"
- if type(preamble)=="string" then
- t._preamble_=preamble.."\n"..t._preamble_
- elseif type(preamble)=="table" then
- for k,v in next,preamble do
- t._environment_[k]=v
- end
- end
- end
-end
-strings.formatters.add=add
-patterns.xmlescape=Cs((P("<")/"&lt;"+P(">")/"&gt;"+P("&")/"&amp;"+P('"')/"&quot;"+P(1))^0)
-patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+P(1))^0)
-patterns.luaescape=Cs(((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0)
-patterns.luaquoted=Cs(Cc('"')*((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0*Cc('"'))
-if _LUAVERSION<5.2 then
- add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],"local xmlescape = lpeg.patterns.xmlescape")
- add(formatters,"tex",[[lpegmatch(texescape,%s)]],"local texescape = lpeg.patterns.texescape")
- add(formatters,"lua",[[lpegmatch(luaescape,%s)]],"local luaescape = lpeg.patterns.luaescape")
-else
- add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],{ xmlescape=lpeg.patterns.xmlescape })
- add(formatters,"tex",[[lpegmatch(texescape,%s)]],{ texescape=lpeg.patterns.texescape })
- add(formatters,"lua",[[lpegmatch(luaescape,%s)]],{ luaescape=lpeg.patterns.luaescape })
-end
-local dquote=patterns.dquote
-local equote=patterns.escaped+dquote/'\\"'+1
-local space=patterns.space
-local cquote=Cc('"')
-local pattern=Cs(dquote*(equote-P(-2))^0*dquote)
-+Cs(cquote*(equote-space)^0*space*equote^0*cquote)
-function string.optionalquoted(str)
- return lpegmatch(pattern,str) or str
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['luat-basics-gen']={
- version=1.100,
- comment="companion to luatex-*.tex",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-if context then
- texio.write_nl("fatal error: this module is not for context")
- os.exit()
-end
-local dummyfunction=function()
-end
-local dummyreporter=function(c)
- return function(...)
- (texio.reporter or texio.write_nl)(c.." : "..string.formatters(...))
- end
-end
-statistics={
- register=dummyfunction,
- starttiming=dummyfunction,
- stoptiming=dummyfunction,
- elapsedtime=nil,
-}
-directives={
- register=dummyfunction,
- enable=dummyfunction,
- disable=dummyfunction,
-}
-trackers={
- register=dummyfunction,
- enable=dummyfunction,
- disable=dummyfunction,
-}
-experiments={
- register=dummyfunction,
- enable=dummyfunction,
- disable=dummyfunction,
-}
-storage={
- register=dummyfunction,
- shared={},
-}
-logs={
- new=dummyreporter,
- reporter=dummyreporter,
- messenger=dummyreporter,
- report=dummyfunction,
-}
-callbacks={
- register=function(n,f) return callback.register(n,f) end,
-}
-utilities={
- storage={
- allocate=function(t) return t or {} end,
- mark=function(t) return t or {} end,
- },
-}
-characters=characters or {
- data={}
-}
-texconfig.kpse_init=true
-resolvers=resolvers or {}
-local remapper={
- otf="opentype fonts",
- ttf="truetype fonts",
- ttc="truetype fonts",
- dfont="truetype fonts",
- cid="cid maps",
- cidmap="cid maps",
- fea="font feature files",
- pfa="type1 fonts",
- pfb="type1 fonts",
- afm="afm",
-}
-function resolvers.findfile(name,fileformat)
- name=string.gsub(name,"\\","/")
- if not fileformat or fileformat=="" then
- fileformat=file.suffix(name)
- if fileformat=="" then
- fileformat="tex"
- end
- end
- fileformat=string.lower(fileformat)
- fileformat=remapper[fileformat] or fileformat
- local found=kpse.find_file(name,fileformat)
- if not found or found=="" then
- found=kpse.find_file(name,"other text files")
- end
- return found
-end
-resolvers.findbinfile=resolvers.findfile
-function resolvers.loadbinfile(filename,filetype)
- local data=io.loaddata(filename)
- return true,data,#data
-end
-function resolvers.resolve(s)
- return s
-end
-function resolvers.unresolve(s)
- return s
-end
-caches={}
-local writable=nil
-local readables={}
-local usingjit=jit
-if not caches.namespace or caches.namespace=="" or caches.namespace=="context" then
- caches.namespace='generic'
-end
-do
- local cachepaths=kpse.expand_var('$TEXMFCACHE') or ""
- if cachepaths=="" or cachepaths=="$TEXMFCACHE" then
- cachepaths=kpse.expand_var('$TEXMFVAR') or ""
- end
- if cachepaths=="" or cachepaths=="$TEXMFVAR" then
- cachepaths=kpse.expand_var('$VARTEXMF') or ""
- end
- if cachepaths=="" then
- local fallbacks={ "TMPDIR","TEMPDIR","TMP","TEMP","HOME","HOMEPATH" }
- for i=1,#fallbacks do
- cachepaths=os.getenv(fallbacks[i]) or ""
- if cachepath~="" and lfs.isdir(cachepath) then
- break
- end
- end
- end
- if cachepaths=="" then
- cachepaths="."
- end
- cachepaths=string.split(cachepaths,os.type=="windows" and ";" or ":")
- for i=1,#cachepaths do
- local cachepath=cachepaths[i]
- if not lfs.isdir(cachepath) then
- lfs.mkdirs(cachepath)
- if lfs.isdir(cachepath) then
- texio.write(string.format("(created cache path: %s)",cachepath))
- end
- end
- if file.is_writable(cachepath) then
- writable=file.join(cachepath,"luatex-cache")
- lfs.mkdir(writable)
- writable=file.join(writable,caches.namespace)
- lfs.mkdir(writable)
- break
- end
- end
- for i=1,#cachepaths do
- if file.is_readable(cachepaths[i]) then
- readables[#readables+1]=file.join(cachepaths[i],"luatex-cache",caches.namespace)
- end
- end
- if not writable then
- texio.write_nl("quiting: fix your writable cache path")
- os.exit()
- elseif #readables==0 then
- texio.write_nl("quiting: fix your readable cache path")
- os.exit()
- elseif #readables==1 and readables[1]==writable then
- texio.write(string.format("(using cache: %s)",writable))
- else
- texio.write(string.format("(using write cache: %s)",writable))
- texio.write(string.format("(using read cache: %s)",table.concat(readables," ")))
- end
-end
-function caches.getwritablepath(category,subcategory)
- local path=file.join(writable,category)
- lfs.mkdir(path)
- path=file.join(path,subcategory)
- lfs.mkdir(path)
- return path
-end
-function caches.getreadablepaths(category,subcategory)
- local t={}
- for i=1,#readables do
- t[i]=file.join(readables[i],category,subcategory)
- end
- return t
-end
-local function makefullname(path,name)
- if path and path~="" then
- return file.addsuffix(file.join(path,name),"lua"),file.addsuffix(file.join(path,name),usingjit and "lub" or "luc")
- end
-end
-function caches.is_writable(path,name)
- local fullname=makefullname(path,name)
- return fullname and file.is_writable(fullname)
-end
-function caches.loaddata(paths,name)
- for i=1,#paths do
- local data=false
- local luaname,lucname=makefullname(paths[i],name)
- if lucname and not lfs.isfile(lucname) and type(caches.compile)=="function" then
- texio.write(string.format("(compiling luc: %s)",lucname))
- data=loadfile(luaname)
- if data then
- data=data()
- end
- if data then
- caches.compile(data,luaname,lucname)
- return data
- end
- end
- if lucname and lfs.isfile(lucname) then
- texio.write(string.format("(load luc: %s)",lucname))
- data=loadfile(lucname)
- if data then
- data=data()
- end
- if data then
- return data
- else
- texio.write(string.format("(loading failed: %s)",lucname))
- end
- end
- if luaname and lfs.isfile(luaname) then
- texio.write(string.format("(load lua: %s)",luaname))
- data=loadfile(luaname)
- if data then
- data=data()
- end
- if data then
- return data
- end
- end
- end
-end
-function caches.savedata(path,name,data)
- local luaname,lucname=makefullname(path,name)
- if luaname then
- texio.write(string.format("(save: %s)",luaname))
- table.tofile(luaname,data,true)
- if lucname and type(caches.compile)=="function" then
- os.remove(lucname)
- texio.write(string.format("(save: %s)",lucname))
- caches.compile(data,luaname,lucname)
- end
- end
-end
-function caches.compile(data,luaname,lucname)
- local d=io.loaddata(luaname)
- if not d or d=="" then
- d=table.serialize(data,true)
- end
- if d and d~="" then
- local f=io.open(lucname,'wb')
- if f then
- local s=loadstring(d)
- if s then
- f:write(string.dump(s,true))
- end
- f:close()
- end
- end
-end
-function table.setmetatableindex(t,f)
- setmetatable(t,{ __index=f })
-end
-arguments={}
-if arg then
- for i=1,#arg do
- local k,v=string.match(arg[i],"^%-%-([^=]+)=?(.-)$")
- if k and v then
- arguments[k]=v
- end
- end
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['data-con']={
- version=1.100,
- comment="companion to luat-lib.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local format,lower,gsub=string.format,string.lower,string.gsub
-local trace_cache=false trackers.register("resolvers.cache",function(v) trace_cache=v end)
-local trace_containers=false trackers.register("resolvers.containers",function(v) trace_containers=v end)
-local trace_storage=false trackers.register("resolvers.storage",function(v) trace_storage=v end)
-containers=containers or {}
-local containers=containers
-containers.usecache=true
-local report_containers=logs.reporter("resolvers","containers")
-local allocated={}
-local mt={
- __index=function(t,k)
- if k=="writable" then
- local writable=caches.getwritablepath(t.category,t.subcategory) or { "." }
- t.writable=writable
- return writable
- elseif k=="readables" then
- local readables=caches.getreadablepaths(t.category,t.subcategory) or { "." }
- t.readables=readables
- return readables
- end
- end,
- __storage__=true
-}
-function containers.define(category,subcategory,version,enabled)
- if category and subcategory then
- local c=allocated[category]
- if not c then
- c={}
- allocated[category]=c
- end
- local s=c[subcategory]
- if not s then
- s={
- category=category,
- subcategory=subcategory,
- storage={},
- enabled=enabled,
- version=version or math.pi,
- trace=false,
- }
- setmetatable(s,mt)
- c[subcategory]=s
- end
- return s
- end
-end
-function containers.is_usable(container,name)
- return container.enabled and caches and caches.is_writable(container.writable,name)
-end
-function containers.is_valid(container,name)
- if name and name~="" then
- local storage=container.storage[name]
- return storage and storage.cache_version==container.version
- else
- return false
- end
-end
-function containers.read(container,name)
- local storage=container.storage
- local stored=storage[name]
- if not stored and container.enabled and caches and containers.usecache then
- stored=caches.loaddata(container.readables,name)
- if stored and stored.cache_version==container.version then
- if trace_cache or trace_containers then
- report_containers("action %a, category %a, name %a","load",container.subcategory,name)
- end
- else
- stored=nil
- end
- storage[name]=stored
- elseif stored then
- if trace_cache or trace_containers then
- report_containers("action %a, category %a, name %a","reuse",container.subcategory,name)
- end
- end
- return stored
-end
-function containers.write(container,name,data)
- if data then
- data.cache_version=container.version
- if container.enabled and caches then
- local unique,shared=data.unique,data.shared
- data.unique,data.shared=nil,nil
- caches.savedata(container.writable,name,data)
- if trace_cache or trace_containers then
- report_containers("action %a, category %a, name %a","save",container.subcategory,name)
- end
- data.unique,data.shared=unique,shared
- end
- if trace_cache or trace_containers then
- report_containers("action %a, category %a, name %a","store",container.subcategory,name)
- end
- container.storage[name]=data
- end
- return data
-end
-function containers.content(container,name)
- return container.storage[name]
-end
-function containers.cleanname(name)
- return (gsub(lower(name),"[^%w\128-\255]+","-"))
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['luatex-fonts-nod']={
- version=1.001,
- comment="companion to luatex-fonts.lua",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-if context then
- texio.write_nl("fatal error: this module is not for context")
- os.exit()
-end
-if tex.attribute[0]~=0 then
- texio.write_nl("log","!")
- texio.write_nl("log","! Attribute 0 is reserved for ConTeXt's font feature management and has to be")
- texio.write_nl("log","! set to zero. Also, some attributes in the range 1-255 are used for special")
- texio.write_nl("log","! purposes so setting them at the TeX end might break the font handler.")
- texio.write_nl("log","!")
- tex.attribute[0]=0
-end
-attributes=attributes or {}
-attributes.unsetvalue=-0x7FFFFFFF
-local numbers,last={},127
-attributes.private=attributes.private or function(name)
- local number=numbers[name]
- if not number then
- if last<255 then
- last=last+1
- end
- number=last
- numbers[name]=number
- end
- return number
-end
-nodes={}
-nodes.pool={}
-nodes.handlers={}
-local nodecodes={} for k,v in next,node.types () do nodecodes[string.gsub(v,"_","")]=k end
-local whatcodes={} for k,v in next,node.whatsits() do whatcodes[string.gsub(v,"_","")]=k end
-local glyphcodes={ [0]="character","glyph","ligature","ghost","left","right" }
-local disccodes={ [0]="discretionary","explicit","automatic","regular","first","second" }
-nodes.nodecodes=nodecodes
-nodes.whatcodes=whatcodes
-nodes.whatsitcodes=whatcodes
-nodes.glyphcodes=glyphcodes
-nodes.disccodes=disccodes
-local free_node=node.free
-local remove_node=node.remove
-local new_node=node.new
-local traverse_id=node.traverse_id
-nodes.handlers.protectglyphs=node.protect_glyphs
-nodes.handlers.unprotectglyphs=node.unprotect_glyphs
-local math_code=nodecodes.math
-local end_of_math=node.end_of_math
-function node.end_of_math(n)
- if n.id==math_code and n.subtype==1 then
- return n
- else
- return end_of_math(n)
- end
-end
-function nodes.remove(head,current,free_too)
- local t=current
- head,current=remove_node(head,current)
- if t then
- if free_too then
- free_node(t)
- t=nil
- else
- t.next,t.prev=nil,nil
- end
- end
- return head,current,t
-end
-function nodes.delete(head,current)
- return nodes.remove(head,current,true)
-end
-function nodes.pool.kern(k)
- local n=new_node("kern",1)
- n.kern=k
- return n
-end
-local getfield=node.getfield or function(n,tag) return n[tag] end
-local setfield=node.setfield or function(n,tag,value) n[tag]=value end
-nodes.getfield=getfield
-nodes.setfield=setfield
-nodes.getattr=getfield
-nodes.setattr=setfield
-if node.getid then nodes.getid=node.getid else function nodes.getid (n) return getfield(n,"id") end end
-if node.getsubtype then nodes.getsubtype=node.getsubtype else function nodes.getsubtype(n) return getfield(n,"subtype") end end
-if node.getnext then nodes.getnext=node.getnext else function nodes.getnext (n) return getfield(n,"next") end end
-if node.getprev then nodes.getprev=node.getprev else function nodes.getprev (n) return getfield(n,"prev") end end
-if node.getchar then nodes.getchar=node.getchar else function nodes.getchar (n) return getfield(n,"char") end end
-if node.getfont then nodes.getfont=node.getfont else function nodes.getfont (n) return getfield(n,"font") end end
-if node.getlist then nodes.getlist=node.getlist else function nodes.getlist (n) return getfield(n,"list") end end
-function nodes.tonut (n) return n end
-function nodes.tonode(n) return n end
-nodes.tostring=node.tostring or tostring
-nodes.copy=node.copy
-nodes.copy_list=node.copy_list
-nodes.delete=node.delete
-nodes.dimensions=node.dimensions
-nodes.end_of_math=node.end_of_math
-nodes.flush_list=node.flush_list
-nodes.flush_node=node.flush_node
-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.slide=node.slide
-nodes.vpack=node.vpack
-nodes.first_glyph=node.first_glyph
-nodes.first_character=node.first_character
-nodes.has_glyph=node.has_glyph or node.first_glyph
-nodes.current_attr=node.current_attr
-nodes.do_ligature_n=node.do_ligature_n
-nodes.has_field=node.has_field
-nodes.last_node=node.last_node
-nodes.usedlist=node.usedlist
-nodes.protrusion_skippable=node.protrusion_skippable
-nodes.write=node.write
-nodes.has_attribute=node.has_attribute
-nodes.set_attribute=node.set_attribute
-nodes.unset_attribute=node.unset_attribute
-nodes.protect_glyphs=node.protect_glyphs
-nodes.unprotect_glyphs=node.unprotect_glyphs
-nodes.kerning=node.kerning
-nodes.ligaturing=node.ligaturing
-nodes.mlist_to_hlist=node.mlist_to_hlist
-nodes.nuts=nodes
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-ini']={
- version=1.001,
- comment="companion to font-ini.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local allocate=utilities.storage.allocate
-local report_defining=logs.reporter("fonts","defining")
-fonts=fonts or {}
-local fonts=fonts
-fonts.hashes={ identifiers=allocate() }
-fonts.tables=fonts.tables or {}
-fonts.helpers=fonts.helpers or {}
-fonts.tracers=fonts.tracers or {}
-fonts.specifiers=fonts.specifiers or {}
-fonts.analyzers={}
-fonts.readers={}
-fonts.definers={ methods={} }
-fonts.loggers={ register=function() end }
-fontloader.totable=fontloader.to_table
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-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"
-}
-local next,tostring,rawget=next,tostring,rawget
-local format,match,lower,gsub=string.format,string.match,string.lower,string.gsub
-local utfbyte=utf.byte
-local sort,insert,concat,sortedkeys,serialize,fastcopy=table.sort,table.insert,table.concat,table.sortedkeys,table.serialize,table.fastcopy
-local derivetable=table.derive
-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")
-local fonts=fonts
-local constructors=fonts.constructors or {}
-fonts.constructors=constructors
-local handlers=fonts.handlers or {}
-fonts.handlers=handlers
-local allocate=utilities.storage.allocate
-local setmetatableindex=table.setmetatableindex
-constructors.dontembed=allocate()
-constructors.autocleanup=true
-constructors.namemode="fullpath"
-constructors.version=1.01
-constructors.cache=containers.define("fonts","constructors",constructors.version,false)
-constructors.privateoffset=0xF0000
-constructors.keys={
- properties={
- encodingbytes="number",
- embedding="number",
- cidinfo={},
- format="string",
- fontname="string",
- fullname="string",
- filename="filename",
- psname="string",
- name="string",
- virtualized="boolean",
- hasitalics="boolean",
- autoitalicamount="basepoints",
- nostackmath="boolean",
- noglyphnames="boolean",
- mode="string",
- hasmath="boolean",
- mathitalics="boolean",
- textitalics="boolean",
- finalized="boolean",
- },
- parameters={
- mathsize="number",
- scriptpercentage="float",
- scriptscriptpercentage="float",
- units="cardinal",
- designsize="scaledpoints",
- expansion={
- stretch="integerscale",
- shrink="integerscale",
- step="integerscale",
- auto="boolean",
- },
- protrusion={
- auto="boolean",
- },
- slantfactor="float",
- extendfactor="float",
- factor="float",
- hfactor="float",
- vfactor="float",
- size="scaledpoints",
- units="scaledpoints",
- scaledpoints="scaledpoints",
- slantperpoint="scaledpoints",
- spacing={
- width="scaledpoints",
- stretch="scaledpoints",
- shrink="scaledpoints",
- extra="scaledpoints",
- },
- xheight="scaledpoints",
- quad="scaledpoints",
- ascender="scaledpoints",
- descender="scaledpoints",
- synonyms={
- space="spacing.width",
- spacestretch="spacing.stretch",
- spaceshrink="spacing.shrink",
- extraspace="spacing.extra",
- x_height="xheight",
- space_stretch="spacing.stretch",
- space_shrink="spacing.shrink",
- extra_space="spacing.extra",
- em="quad",
- ex="xheight",
- slant="slantperpoint",
- },
- },
- description={
- width="basepoints",
- height="basepoints",
- depth="basepoints",
- boundingbox={},
- },
- character={
- width="scaledpoints",
- height="scaledpoints",
- depth="scaledpoints",
- italic="scaledpoints",
- },
-}
-local designsizes=allocate()
-constructors.designsizes=designsizes
-local loadedfonts=allocate()
-constructors.loadedfonts=loadedfonts
-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)
- if scaledpoints<0 then
- if designsize then
- local factor=constructors.factor
- if designsize>factor then
- return (- scaledpoints/1000)*designsize
- else
- return (- scaledpoints/1000)*designsize*factor
- end
- else
- return (- scaledpoints/1000)*10*factor
- end
- else
- return scaledpoints
- end
-end
-function constructors.cleanuptable(tfmdata)
- if constructors.autocleanup and tfmdata.properties.virtualized then
- for k,v in next,tfmdata.characters do
- if v.commands then v.commands=nil end
- end
- end
-end
-function constructors.calculatescale(tfmdata,scaledpoints)
- local parameters=tfmdata.parameters
- if scaledpoints<0 then
- scaledpoints=(- scaledpoints/1000)*(tfmdata.designsize or parameters.designsize)
- end
- return scaledpoints,scaledpoints/(parameters.units or 1000)
-end
-local unscaled={
- ScriptPercentScaleDown=true,
- ScriptScriptPercentScaleDown=true,
- RadicalDegreeBottomRaisePercent=true
-}
-function constructors.assignmathparameters(target,original)
- local mathparameters=original.mathparameters
- if mathparameters and next(mathparameters) then
- local targetparameters=target.parameters
- local targetproperties=target.properties
- local targetmathparameters={}
- local factor=targetproperties.math_is_scaled and 1 or targetparameters.factor
- for name,value in next,mathparameters do
- if unscaled[name] then
- targetmathparameters[name]=value
- else
- targetmathparameters[name]=value*factor
- end
- end
- if not targetmathparameters.FractionDelimiterSize then
- targetmathparameters.FractionDelimiterSize=1.01*targetparameters.size
- end
- if not mathparameters.FractionDelimiterDisplayStyleSize then
- targetmathparameters.FractionDelimiterDisplayStyleSize=2.40*targetparameters.size
- end
- target.mathparameters=targetmathparameters
- end
-end
-function constructors.beforecopyingcharacters(target,original)
-end
-function constructors.aftercopyingcharacters(target,original)
-end
-constructors.sharefonts=false
-constructors.nofsharedfonts=0
-local sharednames={}
-function constructors.trytosharefont(target,tfmdata)
- if constructors.sharefonts then
- local characters=target.characters
- local n=1
- local t={ target.psname }
- 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 h=md5.HEX(concat(t," "))
- local s=sharednames[h]
- if s then
- if trace_defining then
- report_defining("font %a uses backend resources of font %a",target.fullname,s)
- end
- target.fullname=s
- constructors.nofsharedfonts=constructors.nofsharedfonts+1
- target.properties.sharedwith=s
- else
- sharednames[h]=target.fullname
- end
- end
-end
-function constructors.enhanceparameters(parameters)
- local xheight=parameters.x_height
- local quad=parameters.quad
- local space=parameters.space
- local stretch=parameters.space_stretch
- local shrink=parameters.space_shrink
- local extra=parameters.extra_space
- local slant=parameters.slant
- parameters.xheight=xheight
- parameters.spacestretch=stretch
- parameters.spaceshrink=shrink
- parameters.extraspace=extra
- parameters.em=quad
- parameters.ex=xheight
- parameters.slantperpoint=slant
- parameters.spacing={
- width=space,
- stretch=stretch,
- shrink=shrink,
- extra=extra,
- }
-end
-function constructors.scale(tfmdata,specification)
- local target={}
- 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 {}
- local characters=tfmdata.characters or {}
- local changed=tfmdata.changed or {}
- 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
- target.characters=targetcharacters
- target.descriptions=targetdescriptions
- target.parameters=targetparameters
- target.properties=targetproperties
- target.goodies=targetgoodies
- target.shared=shared
- target.resources=resources
- target.unscaled=tfmdata
- local mathsize=tonumber(specification.mathsize) or 0
- local textsize=tonumber(specification.textsize) or scaledpoints
- local forcedsize=tonumber(parameters.mathsize ) or 0
- 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
- scaledpoints=forcedsize
- end
- targetparameters.mathsize=mathsize
- targetparameters.textsize=textsize
- targetparameters.forcedsize=forcedsize
- targetparameters.extrafactor=extrafactor
- local tounicode=resources.tounicode
- local defaultwidth=resources.defaultwidth or 0
- local defaultheight=resources.defaultheight or 0
- local defaultdepth=resources.defaultdepth or 0
- local units=parameters.units or 1000
- if target.fonts then
- target.fonts=fastcopy(target.fonts)
- end
- targetproperties.language=properties.language or "dflt"
- targetproperties.script=properties.script or "dflt"
- targetproperties.mode=properties.mode or "base"
- local askedscaledpoints=scaledpoints
- local scaledpoints,delta=constructors.calculatescale(tfmdata,scaledpoints,nil,specification)
- local hdelta=delta
- local vdelta=delta
- target.designsize=parameters.designsize
- target.units_per_em=units
- local direction=properties.direction or tfmdata.direction or 0
- target.direction=direction
- properties.direction=direction
- target.size=scaledpoints
- target.encodingbytes=properties.encodingbytes or 1
- target.embedding=properties.embedding or "subset"
- target.tounicode=1
- target.cidinfo=properties.cidinfo
- target.format=properties.format
- 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
- if not psname or psname=="" then
- psname=fontname or (fullname and fonts.names.cleanname(fullname))
- end
- 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
- local expansion=parameters.expansion
- if expansion then
- target.stretch=expansion.stretch
- target.shrink=expansion.shrink
- target.step=expansion.step
- target.auto_expand=expansion.auto
- end
- local protrusion=parameters.protrusion
- if protrusion then
- target.auto_protrude=protrusion.auto
- end
- local extendfactor=parameters.extendfactor or 0
- if extendfactor~=0 and extendfactor~=1 then
- hdelta=hdelta*extendfactor
- target.extend=extendfactor*1000
- else
- target.extend=1000
- end
- local slantfactor=parameters.slantfactor or 0
- if slantfactor~=0 then
- target.slant=slantfactor*1000
- else
- target.slant=0
- end
- targetparameters.factor=delta
- targetparameters.hfactor=hdelta
- targetparameters.vfactor=vdelta
- targetparameters.size=scaledpoints
- targetparameters.units=units
- targetparameters.scaledpoints=askedscaledpoints
- local isvirtual=properties.virtualized or tfmdata.type=="virtual"
- local hasquality=target.auto_expand or target.auto_protrude
- local hasitalics=properties.hasitalics
- local autoitalicamount=properties.autoitalicamount
- local stackmath=not properties.nostackmath
- local nonames=properties.noglyphnames
- local nodemode=properties.mode=="node"
- if changed and not next(changed) then
- changed=false
- end
- target.type=isvirtual and "virtual" or "real"
- target.postprocessors=tfmdata.postprocessors
- local targetslant=(parameters.slant or parameters[1] or 0)*factors.pt
- 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
- 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 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)
- local protrusionfactor=(targetquad~=0 and 1000/targetquad) or 0
- 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)
- properties.hasmath=true
- target.nomath=false
- target.MathConstants=target.mathparameters
- else
- properties.hasmath=false
- target.nomath=true
- target.mathparameters=nil
- end
- local italickey="italic"
- local useitalics=true
- if hasmath then
- autoitalicamount=false
- elseif properties.textitalics then
- italickey="italic_correction"
- useitalics=false
- if properties.delaytextitalics then
- autoitalicamount=false
- end
- end
- if trace_defining then
- report_defining("defining tfm, name %a, fullname %a, filename %a, hscale %a, vscale %a, math %a, italics %a",
- name,fullname,filename,hdelta,vdelta,
- hasmath and "enabled" or "disabled",useitalics and "enabled" or "disabled")
- end
- constructors.beforecopyingcharacters(target,tfmdata)
- local sharedkerns={}
- for unicode,character in next,characters do
- local chr,description,index,touni
- if changed then
- local c=changed[unicode]
- if c then
- local ligatures=character.ligatures
- description=descriptions[c] or descriptions[unicode] or character
- character=characters[c] or character
- index=description.index or c
- if tounicode then
- touni=tounicode[index]
- if not touni then
- local d=descriptions[unicode] or characters[unicode]
- local i=d.index or unicode
- touni=tounicode[i]
- end
- end
- if ligatures and not character.ligatures then
- character.ligatures=ligatures
- end
- else
- description=descriptions[unicode] or character
- index=description.index or unicode
- if tounicode then
- touni=tounicode[index]
- end
- end
- else
- description=descriptions[unicode] or character
- index=description.index or unicode
- if tounicode then
- touni=tounicode[index]
- end
- end
- local width=description.width
- local height=description.height
- local depth=description.depth
- if width then width=hdelta*width else width=scaledwidth end
- if height then height=vdelta*height else height=scaledheight end
- if depth and depth~=0 then
- depth=delta*depth
- if nonames then
- chr={
- index=index,
- height=height,
- depth=depth,
- width=width,
- }
- else
- chr={
- name=description.name,
- index=index,
- height=height,
- depth=depth,
- width=width,
- }
- end
- else
- if nonames then
- chr={
- index=index,
- height=height,
- width=width,
- }
- else
- chr={
- name=description.name,
- index=index,
- height=height,
- width=width,
- }
- end
- end
- if touni then
- chr.tounicode=touni
- end
- if hasquality then
- local ve=character.expansion_factor
- if ve then
- chr.expansion_factor=ve*1000
- end
- local vl=character.left_protruding
- if vl then
- chr.left_protruding=protrusionfactor*width*vl
- end
- local vr=character.right_protruding
- if vr then
- chr.right_protruding=protrusionfactor*width*vr
- end
- end
- if autoitalicamount then
- local vi=description.italic
- if not vi then
- local vi=description.boundingbox[3]-description.width+autoitalicamount
- if vi>0 then
- chr[italickey]=vi*hdelta
- end
- elseif vi~=0 then
- chr[italickey]=vi*hdelta
- end
- elseif hasitalics then
- local vi=description.italic
- if vi and vi~=0 then
- chr[italickey]=vi*hdelta
- end
- end
- if hasmath then
- 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]
- t[i]={
- ["start"]=(vvi["start"] or 0)*vdelta,
- ["end"]=(vvi["end"] or 0)*vdelta,
- ["advance"]=(vvi["advance"] or 0)*vdelta,
- ["extender"]=vvi["extender"],
- ["glyph"]=vvi["glyph"],
- }
- end
- chr.vert_variants=t
- else
- local hv=character.horiz_variants
- if hv then
- local t={}
- for i=1,#hv do
- local hvi=hv[i]
- t[i]={
- ["start"]=(hvi["start"] or 0)*hdelta,
- ["end"]=(hvi["end"] or 0)*hdelta,
- ["advance"]=(hvi["advance"] or 0)*hdelta,
- ["extender"]=hvi["extender"],
- ["glyph"]=hvi["glyph"],
- }
- end
- chr.horiz_variants=t
- end
- end
- end
- local va=character.top_accent
- if va then
- chr.top_accent=vdelta*va
- end
- if stackmath then
- local mk=character.mathkerns
- if mk then
- local kerns={}
- local v=mk.top_right if v then local k={} for i=1,#v do local vi=v[i]
- k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern }
- end kerns.top_right=k end
- local v=mk.top_left if v then local k={} for i=1,#v do local vi=v[i]
- k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern }
- end kerns.top_left=k end
- local v=mk.bottom_left if v then local k={} for i=1,#v do local vi=v[i]
- k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern }
- end kerns.bottom_left=k end
- local v=mk.bottom_right if v then local k={} for i=1,#v do local vi=v[i]
- k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern }
- end kerns.bottom_right=k end
- chr.mathkern=kerns
- end
- end
- end
- if not nodemode 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
- local vl=character.ligatures
- if vl then
- if true then
- chr.ligatures=vl
- else
- local tt={}
- for i,l in next,vl do
- tt[i]=l
- end
- chr.ligatures=tt
- end
- end
- end
- if isvirtual then
- local vc=character.commands
- if vc then
- local ok=false
- for i=1,#vc do
- local key=vc[i][1]
- if key=="right" or key=="down" then
- ok=true
- break
- end
- end
- if ok then
- local tt={}
- for i=1,#vc do
- local ivc=vc[i]
- local key=ivc[1]
- if key=="right" then
- tt[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
- tt[i]=ivc
- end
- end
- chr.commands=tt
- else
- chr.commands=vc
- end
- chr.index=nil
- end
- end
- targetcharacters[unicode]=chr
- end
- constructors.aftercopyingcharacters(target,tfmdata)
- constructors.trytosharefont(target,tfmdata)
- 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,
- auto=tfmdata.auto_expand or false,
- }
- end
- if not parameters.protrusion then
- parameters.protrusion={
- auto=auto_protrude
- }
- end
- if not parameters.size then
- parameters.size=tfmdata.size
- end
- if not parameters.extendfactor then
- parameters.extendfactor=tfmdata.extend or 0
- end
- if not parameters.slantfactor then
- parameters.slantfactor=tfmdata.slant or 0
- end
- if not parameters.designsize then
- parameters.designsize=tfmdata.designsize or (factors.pt*10)
- end
- if not parameters.units then
- parameters.units=tfmdata.units_per_em or 1000
- end
- if not tfmdata.descriptions then
- local descriptions={}
- 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
- if not properties.virtualized then
- properties.virtualized=tfmdata.type=="virtual"
- end
- if not tfmdata.properties then
- tfmdata.properties={
- fontname=tfmdata.fontname,
- filename=tfmdata.filename,
- fullname=tfmdata.fullname,
- name=tfmdata.name,
- psname=tfmdata.psname,
- encodingbytes=tfmdata.encodingbytes or 1,
- embedding=tfmdata.embedding or "subset",
- tounicode=tfmdata.tounicode or 1,
- cidinfo=tfmdata.cidinfo or nil,
- format=tfmdata.format or "type1",
- direction=tfmdata.direction or 0,
- }
- end
- if not tfmdata.resources then
- tfmdata.resources={}
- end
- if not tfmdata.shared then
- tfmdata.shared={}
- end
- 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
- tfmdata.psname=nil
- tfmdata.encodingbytes=nil
- tfmdata.embedding=nil
- tfmdata.tounicode=nil
- tfmdata.cidinfo=nil
- tfmdata.format=nil
- tfmdata.direction=nil
- tfmdata.type=nil
- tfmdata.nomath=nil
- tfmdata.designsize=nil
- tfmdata.size=nil
- tfmdata.stretch=nil
- tfmdata.shrink=nil
- tfmdata.step=nil
- tfmdata.auto_expand=nil
- tfmdata.auto_protrude=nil
- tfmdata.extend=nil
- tfmdata.slant=nil
- tfmdata.units_per_em=nil
- properties.finalized=true
- return tfmdata
-end
-local hashmethods={}
-constructors.hashmethods=hashmethods
-function constructors.hashfeatures(specification)
- local features=specification.features
- if features then
- local t,tn={},0
- for category,list in next,features do
- if next(list) then
- local hasher=hashmethods[category]
- if hasher then
- local hash=hasher(list)
- if hash then
- tn=tn+1
- t[tn]=category..":"..hash
- end
- end
- end
- end
- if tn>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
- elseif k=="number" or k=="features" then
- else
- n=n+1
- s[n]=k
- end
- end
- if n>0 then
- sort(s)
- for i=1,n do
- local k=s[i]
- s[i]=k..'='..tostring(list[k])
- end
- return concat(s,"+")
- end
-end
-function constructors.hashinstance(specification,force)
- local hash,size,fallbacks=specification.hash,specification.size,specification.fallbacks
- if force or not hash then
- hash=constructors.hashfeatures(specification)
- specification.hash=hash
- end
- if size<1000 and designsizes[hash] then
- size=math.round(constructors.scaled(size,designsizes[hash]))
- specification.size=size
- end
- if fallbacks then
- return hash..' @ '..tostring(size)..' @ '..fallbacks
- else
- return hash..' @ '..tostring(size)
- end
-end
-function constructors.setname(tfmdata,specification)
- if constructors.namemode=="specification" then
- local specname=specification.specification
- if specname then
- tfmdata.properties.name=specname
- if trace_defining then
- report_otf("overloaded fontname %a",specname)
- end
- end
- end
-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)
- 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)
-local locations={}
-local function setindeed(mode,target,group,name,action,position)
- 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
- 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 node=source.node
- local base=source.base
- local position=source.position
- if node then
- setindeed("node",target,group,name,node,position)
- end
- if base then
- setindeed("base",target,group,name,base,position)
- end
-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
-function constructors.newhandler(what)
- local handler=handlers[what]
- if not handler then
- handler={}
- handlers[what]=handler
- end
- return handler
-end
-function constructors.newfeatures(what)
- local handler=handlers[what]
- local features=handler.features
- if not features then
- local tables=handler.tables
- local statistics=handler.statistics
- features=allocate {
- defaults={},
- descriptions=tables and tables.features or {},
- used=statistics and statistics.usedfeatures or {},
- initializers={ base={},node={} },
- processors={ base={},node={} },
- manipulators={ base={},node={} },
- }
- features.register=function(specification) return register(features,specification) end
- handler.features=features
- end
- return features
-end
-function constructors.checkedfeatures(what,features)
- local defaults=handlers[what].features.defaults
- if features and next(features) then
- features=fastcopy(features)
- for key,value in next,defaults do
- if features[key]==nil then
- features[key]=value
- end
- end
- return features
- else
- return fastcopy(defaults)
- end
-end
-function constructors.initializefeatures(what,tfmdata,features,trace,report)
- if features and next(features) then
- local properties=tfmdata.properties or {}
- local whathandler=handlers[what]
- local whatfeatures=whathandler.features
- local whatinitializers=whatfeatures.initializers
- local whatmodechecker=whatfeatures.modechecker
- local mode=properties.mode or (whatmodechecker and whatmodechecker(tfmdata,features,features.mode)) or features.mode or "base"
- properties.mode=mode
- features.mode=mode
- 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
- local value=features[feature]
- if not value then
- elseif done[feature] then
- 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)
- if mode~=properties.mode or mode~=features.mode then
- if whatmodechecker then
- properties.mode=whatmodechecker(tfmdata,features,properties.mode)
- 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
- return true
- else
- return false
- end
-end
-function constructors.collectprocessors(what,tfmdata,features,trace,report)
- local processes,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
-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
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['luatex-font-enc']={
- version=1.001,
- comment="companion to luatex-*.tex",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-if context then
- texio.write_nl("fatal error: this module is not for context")
- os.exit()
-end
-local fonts=fonts
-fonts.encodings={}
-fonts.encodings.agl={}
-fonts.encodings.known={}
-setmetatable(fonts.encodings.agl,{ __index=function(t,k)
- if k=="unicodes" then
- texio.write(" <loading (extended) adobe glyph list>")
- local unicodes=dofile(resolvers.findfile("font-age.lua"))
- fonts.encodings.agl={ unicodes=unicodes }
- return unicodes
- else
- return nil
- end
-end })
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-cid']={
- version=1.001,
- comment="companion to font-otf.lua (cidmaps)",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local format,match,lower=string.format,string.match,string.lower
-local tonumber=tonumber
-local P,S,R,C,V,lpegmatch=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.V,lpeg.match
-local fonts,logs,trackers=fonts,logs,trackers
-local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end)
-local report_otf=logs.reporter("fonts","otf loading")
-local cid={}
-fonts.cid=cid
-local cidmap={}
-local cidmax=10
-local number=C(R("09","af","AF")^1)
-local space=S(" \n\r\t")
-local spaces=space^0
-local period=P(".")
-local periods=period*period
-local name=P("/")*C((1-space)^1)
-local unicodes,names={},{}
-local function do_one(a,b)
- unicodes[tonumber(a)]=tonumber(b,16)
-end
-local function do_range(a,b,c)
- c=tonumber(c,16)
- for i=tonumber(a),tonumber(b) do
- unicodes[i]=c
- c=c+1
- end
-end
-local function do_name(a,b)
- names[tonumber(a)]=b
-end
-local grammar=P { "start",
- start=number*spaces*number*V("series"),
- series=(spaces*(V("one")+V("range")+V("named")))^1,
- one=(number*spaces*number)/do_one,
- range=(number*periods*number*spaces*number)/do_range,
- named=(number*spaces*name)/do_name
-}
-local function loadcidfile(filename)
- local data=io.loaddata(filename)
- if data then
- unicodes,names={},{}
- lpegmatch(grammar,data)
- local supplement,registry,ordering=match(filename,"^(.-)%-(.-)%-()%.(.-)$")
- return {
- supplement=supplement,
- registry=registry,
- ordering=ordering,
- filename=filename,
- unicodes=unicodes,
- names=names
- }
- end
-end
-cid.loadfile=loadcidfile
-local template="%s-%s-%s.cidmap"
-local function locate(registry,ordering,supplement)
- local filename=format(template,registry,ordering,supplement)
- local hashname=lower(filename)
- local found=cidmap[hashname]
- if not found then
- if trace_loading then
- report_otf("checking cidmap, registry %a, ordering %a, supplement %a, filename %a",registry,ordering,supplement,filename)
- end
- local fullname=resolvers.findfile(filename,'cid') or ""
- if fullname~="" then
- found=loadcidfile(fullname)
- if found then
- if trace_loading then
- report_otf("using cidmap file %a",filename)
- end
- cidmap[hashname]=found
- found.usedname=file.basename(filename)
- end
- end
- end
- return found
-end
-function cid.getmap(specification)
- if not specification then
- report_otf("invalid cidinfo specification, table expected")
- return
- end
- local registry=specification.registry
- local ordering=specification.ordering
- local supplement=specification.supplement
- local filename=format(registry,ordering,supplement)
- local found=cidmap[lower(filename)]
- if found then
- return found
- end
- if trace_loading then
- report_otf("cidmap needed, registry %a, ordering %a, supplement %a",registry,ordering,supplement)
- end
- found=locate(registry,ordering,supplement)
- if not found then
- local supnum=tonumber(supplement)
- local cidnum=nil
- if supnum<cidmax then
- for s=supnum+1,cidmax do
- local c=locate(registry,ordering,s)
- if c then
- found,cidnum=c,s
- break
- end
- end
- end
- if not found and supnum>0 then
- for s=supnum-1,0,-1 do
- local c=locate(registry,ordering,s)
- if c then
- found,cidnum=c,s
- break
- end
- end
- end
- registry=lower(registry)
- ordering=lower(ordering)
- if found and cidnum>0 then
- for s=0,cidnum-1 do
- local filename=format(template,registry,ordering,s)
- if not cidmap[filename] then
- cidmap[filename]=found
- end
- end
- end
- end
- return found
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-map']={
- version=1.001,
- comment="companion to font-ini.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local tonumber=tonumber
-local match,format,find,concat,gsub,lower=string.match,string.format,string.find,table.concat,string.gsub,string.lower
-local P,R,S,C,Ct,Cc,lpegmatch=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cc,lpeg.match
-local utfbyte=utf.byte
-local floor=math.floor
-local trace_loading=false trackers.register("fonts.loading",function(v) trace_loading=v end)
-local trace_mapping=false trackers.register("fonts.mapping",function(v) trace_unimapping=v end)
-local report_fonts=logs.reporter("fonts","loading")
-local fonts=fonts or {}
-local mappings=fonts.mappings or {}
-fonts.mappings=mappings
-local function loadlumtable(filename)
- local lumname=file.replacesuffix(file.basename(filename),"lum")
- local lumfile=resolvers.findfile(lumname,"map") or ""
- if lumfile~="" and lfs.isfile(lumfile) then
- if trace_loading or trace_mapping then
- report_fonts("loading map table %a",lumfile)
- end
- lumunic=dofile(lumfile)
- return lumunic,lumfile
- end
-end
-local hex=R("AF","09")
-local hexfour=(hex*hex*hex*hex)/function(s) return tonumber(s,16) end
-local hexsix=(hex*hex*hex*hex*hex*hex)/function(s) return tonumber(s,16) end
-local dec=(R("09")^1)/tonumber
-local period=P(".")
-local unicode=P("uni")*(hexfour*(period+P(-1))*Cc(false)+Ct(hexfour^1)*Cc(true))
-local ucode=P("u")*(hexsix*(period+P(-1))*Cc(false)+Ct(hexsix^1)*Cc(true))
-local index=P("index")*dec*Cc(false)
-local parser=unicode+ucode+index
-local parsers={}
-local function makenameparser(str)
- if not str or str=="" then
- return parser
- else
- local p=parsers[str]
- if not p then
- p=P(str)*period*dec*Cc(false)
- parsers[str]=p
- end
- return p
- end
-end
-local function tounicode16(unicode,name)
- if unicode<0x10000 then
- return format("%04X",unicode)
- elseif unicode<0x1FFFFFFFFF then
- return format("%04X%04X",floor(unicode/1024),unicode%1024+0xDC00)
- else
- report_fonts("can't convert %a in %a into tounicode",unicode,name)
- end
-end
-local function tounicode16sequence(unicodes,name)
- local t={}
- for l=1,#unicodes do
- local unicode=unicodes[l]
- if unicode<0x10000 then
- t[l]=format("%04X",unicode)
- elseif unicode<0x1FFFFFFFFF then
- t[l]=format("%04X%04X",floor(unicode/1024),unicode%1024+0xDC00)
- else
- report_fonts ("can't convert %a in %a into tounicode",unicode,name)
- end
- end
- return concat(t)
-end
-local function fromunicode16(str)
- if #str==4 then
- return tonumber(str,16)
- else
- local l,r=match(str,"(....)(....)")
- return (tonumber(l,16))*0x400+tonumber(r,16)-0xDC00
- end
-end
-mappings.loadlumtable=loadlumtable
-mappings.makenameparser=makenameparser
-mappings.tounicode16=tounicode16
-mappings.tounicode16sequence=tounicode16sequence
-mappings.fromunicode16=fromunicode16
-local ligseparator=P("_")
-local varseparator=P(".")
-local namesplitter=Ct(C((1-ligseparator-varseparator)^1)*(ligseparator*C((1-ligseparator-varseparator)^1))^0)
-function mappings.addtounicode(data,filename)
- local resources=data.resources
- local properties=data.properties
- local descriptions=data.descriptions
- local unicodes=resources.unicodes
- if not unicodes then
- return
- end
- unicodes['space']=unicodes['space'] or 32
- unicodes['hyphen']=unicodes['hyphen'] or 45
- unicodes['zwj']=unicodes['zwj'] or 0x200D
- unicodes['zwnj']=unicodes['zwnj'] or 0x200C
- local private=fonts.constructors.privateoffset
- local unknown=format("%04X",utfbyte("?"))
- local unicodevector=fonts.encodings.agl.unicodes
- local tounicode={}
- local originals={}
- resources.tounicode=tounicode
- resources.originals=originals
- local lumunic,uparser,oparser
- local cidinfo,cidnames,cidcodes,usedmap
- if false then
- lumunic=loadlumtable(filename)
- lumunic=lumunic and lumunic.tounicode
- end
- cidinfo=properties.cidinfo
- usedmap=cidinfo and fonts.cid.getmap(cidinfo)
- if usedmap then
- oparser=usedmap and makenameparser(cidinfo.ordering)
- cidnames=usedmap.names
- cidcodes=usedmap.unicodes
- end
- uparser=makenameparser()
- local ns,nl=0,0
- for unic,glyph in next,descriptions do
- local index=glyph.index
- local name=glyph.name
- if unic==-1 or unic>=private or (unic>=0xE000 and unic<=0xF8FF) or unic==0xFFFE or unic==0xFFFF then
- local unicode=lumunic and lumunic[name] or unicodevector[name]
- if unicode then
- originals[index]=unicode
- tounicode[index]=tounicode16(unicode,name)
- ns=ns+1
- end
- if (not unicode) and usedmap then
- local foundindex=lpegmatch(oparser,name)
- if foundindex then
- unicode=cidcodes[foundindex]
- if unicode then
- originals[index]=unicode
- tounicode[index]=tounicode16(unicode,name)
- ns=ns+1
- else
- local reference=cidnames[foundindex]
- if reference then
- local foundindex=lpegmatch(oparser,reference)
- if foundindex then
- unicode=cidcodes[foundindex]
- if unicode then
- originals[index]=unicode
- tounicode[index]=tounicode16(unicode,name)
- ns=ns+1
- end
- end
- if not unicode or unicode=="" then
- local foundcodes,multiple=lpegmatch(uparser,reference)
- if foundcodes then
- originals[index]=foundcodes
- if multiple then
- tounicode[index]=tounicode16sequence(foundcodes)
- nl=nl+1
- unicode=true
- else
- tounicode[index]=tounicode16(foundcodes,name)
- ns=ns+1
- unicode=foundcodes
- end
- end
- end
- end
- end
- end
- end
- if not unicode or unicode=="" then
- local split=lpegmatch(namesplitter,name)
- local nsplit=split and #split or 0
- local t,n={},0
- unicode=true
- for l=1,nsplit do
- local base=split[l]
- local u=unicodes[base] or unicodevector[base]
- if not u then
- break
- elseif type(u)=="table" then
- if u[1]>=private then
- unicode=false
- break
- end
- n=n+1
- t[n]=u[1]
- else
- if u>=private then
- unicode=false
- break
- end
- n=n+1
- t[n]=u
- end
- end
- if n==0 then
- elseif n==1 then
- originals[index]=t[1]
- tounicode[index]=tounicode16(t[1],name)
- else
- originals[index]=t
- tounicode[index]=tounicode16sequence(t)
- end
- nl=nl+1
- end
- if not unicode or unicode=="" then
- local foundcodes,multiple=lpegmatch(uparser,name)
- if foundcodes then
- if multiple then
- originals[index]=foundcodes
- tounicode[index]=tounicode16sequence(foundcodes,name)
- nl=nl+1
- unicode=true
- else
- originals[index]=foundcodes
- tounicode[index]=tounicode16(foundcodes,name)
- ns=ns+1
- unicode=foundcodes
- end
- end
- end
- end
- end
- if trace_mapping then
- for unic,glyph in table.sortedhash(descriptions) do
- local name=glyph.name
- local index=glyph.index
- local toun=tounicode[index]
- if toun then
- report_fonts("internal slot %U, name %a, unicode %U, tounicode %a",index,name,unic,toun)
- else
- report_fonts("internal slot %U, name %a, unicode %U",index,name,unic)
- end
- end
- end
- if trace_loading and (ns>0 or nl>0) then
- report_fonts("%s tounicode entries added, ligatures %s",nl+ns,ns)
- end
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['luatex-fonts-syn']={
- version=1.001,
- comment="companion to luatex-*.tex",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-if context then
- texio.write_nl("fatal error: this module is not for context")
- os.exit()
-end
-local fonts=fonts
-fonts.names=fonts.names or {}
-fonts.names.version=1.001
-fonts.names.basename="luatex-fonts-names"
-fonts.names.new_to_old={}
-fonts.names.old_to_new={}
-fonts.names.cache=containers.define("fonts","data",fonts.names.version,true)
-local data,loaded=nil,false
-local fileformats={ "lua","tex","other text files" }
-function fonts.names.reportmissingbase()
- texio.write("<missing font database, run: mtxrun --script fonts --reload --simple>")
- fonts.names.reportmissingbase=nil
-end
-function fonts.names.reportmissingname()
- texio.write("<unknown font in database, run: mtxrun --script fonts --reload --simple>")
- fonts.names.reportmissingname=nil
-end
-function fonts.names.resolve(name,sub)
- if not loaded then
- local basename=fonts.names.basename
- if basename and basename~="" then
- data=containers.read(fonts.names.cache,basename)
- if not data then
- basename=file.addsuffix(basename,"lua")
- for i=1,#fileformats do
- local format=fileformats[i]
- local foundname=resolvers.findfile(basename,format) or ""
- if foundname~="" then
- data=dofile(foundname)
- texio.write("<font database loaded: ",foundname,">")
- break
- end
- end
- end
- end
- loaded=true
- end
- if type(data)=="table" and data.version==fonts.names.version then
- local condensed=string.gsub(string.lower(name),"[^%a%d]","")
- local found=data.mappings and data.mappings[condensed]
- if found then
- local fontname,filename,subfont=found[1],found[2],found[3]
- if subfont then
- return filename,fontname
- else
- return filename,false
- end
- elseif fonts.names.reportmissingname then
- fonts.names.reportmissingname()
- return name,false
- end
- elseif fonts.names.reportmissingbase then
- fonts.names.reportmissingbase()
- end
-end
-fonts.names.resolvespec=fonts.names.resolve
-function fonts.names.getfilename(askedname,suffix)
- return ""
-end
-function fonts.names.ignoredfile(filename)
- return false
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-tfm']={
- version=1.001,
- comment="companion to font-ini.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local next=next
-local match=string.match
-local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end)
-local trace_features=false trackers.register("tfm.features",function(v) trace_features=v end)
-local report_defining=logs.reporter("fonts","defining")
-local report_tfm=logs.reporter("fonts","tfm loading")
-local findbinfile=resolvers.findbinfile
-local fonts=fonts
-local handlers=fonts.handlers
-local readers=fonts.readers
-local constructors=fonts.constructors
-local encodings=fonts.encodings
-local tfm=constructors.newhandler("tfm")
-local tfmfeatures=constructors.newfeatures("tfm")
-local registertfmfeature=tfmfeatures.register
-constructors.resolvevirtualtoo=false
-fonts.formats.tfm="type1"
-function tfm.setfeatures(tfmdata,features)
- local okay=constructors.initializefeatures("tfm",tfmdata,features,trace_features,report_tfm)
- if okay then
- return constructors.collectprocessors("tfm",tfmdata,features,trace_features,report_tfm)
- else
- return {}
- end
-end
-local function read_from_tfm(specification)
- local filename=specification.filename
- local size=specification.size
- if trace_defining then
- report_defining("loading tfm file %a at size %s",filename,size)
- end
- local tfmdata=font.read_tfm(filename,size)
- if tfmdata then
- local features=specification.features and specification.features.normal or {}
- local resources=tfmdata.resources or {}
- local properties=tfmdata.properties or {}
- local parameters=tfmdata.parameters or {}
- local shared=tfmdata.shared or {}
- properties.name=tfmdata.name
- properties.fontname=tfmdata.fontname
- properties.psname=tfmdata.psname
- properties.filename=specification.filename
- parameters.size=size
- shared.rawdata={}
- shared.features=features
- shared.processes=next(features) and tfm.setfeatures(tfmdata,features) or nil
- tfmdata.properties=properties
- tfmdata.resources=resources
- tfmdata.parameters=parameters
- tfmdata.shared=shared
- parameters.slant=parameters.slant or parameters[1] or 0
- parameters.space=parameters.space or parameters[2] or 0
- parameters.space_stretch=parameters.space_stretch or parameters[3] or 0
- parameters.space_shrink=parameters.space_shrink or parameters[4] or 0
- parameters.x_height=parameters.x_height or parameters[5] or 0
- parameters.quad=parameters.quad or parameters[6] or 0
- parameters.extra_space=parameters.extra_space or parameters[7] or 0
- constructors.enhanceparameters(parameters)
- if constructors.resolvevirtualtoo then
- fonts.loggers.register(tfmdata,file.suffix(filename),specification)
- local vfname=findbinfile(specification.name,'ovf')
- if vfname and vfname~="" then
- local vfdata=font.read_vf(vfname,size)
- if vfdata then
- local chars=tfmdata.characters
- for k,v in next,vfdata.characters do
- chars[k].commands=v.commands
- end
- properties.virtualized=true
- tfmdata.fonts=vfdata.fonts
- end
- end
- end
- local allfeatures=tfmdata.shared.features or specification.features.normal
- constructors.applymanipulators("tfm",tfmdata,allfeatures.normal,trace_features,report_tfm)
- if not features.encoding then
- local encoding,filename=match(properties.filename,"^(.-)%-(.*)$")
- if filename and encoding and encodings.known and encodings.known[encoding] then
- features.encoding=encoding
- end
- end
- return tfmdata
- end
-end
-local function check_tfm(specification,fullname)
- local foundname=findbinfile(fullname,'tfm') or ""
- if foundname=="" then
- foundname=findbinfile(fullname,'ofm') or ""
- end
- if foundname=="" then
- foundname=fonts.names.getfilename(fullname,"tfm") or ""
- end
- if foundname~="" then
- specification.filename=foundname
- specification.format="ofm"
- return read_from_tfm(specification)
- elseif trace_defining then
- report_defining("loading tfm with name %a fails",specification.name)
- end
-end
-readers.check_tfm=check_tfm
-function readers.tfm(specification)
- local fullname=specification.filename or ""
- if fullname=="" then
- local forced=specification.forced or ""
- if forced~="" then
- fullname=specification.name.."."..forced
- else
- fullname=specification.name
- end
- end
- return check_tfm(specification,fullname)
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-afm']={
- 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 fonts,logs,trackers,containers,resolvers=fonts,logs,trackers,containers,resolvers
-local next,type,tonumber=next,type,tonumber
-local format,match,gmatch,lower,gsub,strip=string.format,string.match,string.gmatch,string.lower,string.gsub,string.strip
-local abs=math.abs
-local P,S,C,R,lpegmatch,patterns=lpeg.P,lpeg.S,lpeg.C,lpeg.R,lpeg.match,lpeg.patterns
-local derivetable=table.derive
-local trace_features=false trackers.register("afm.features",function(v) trace_features=v end)
-local trace_indexing=false trackers.register("afm.indexing",function(v) trace_indexing=v end)
-local trace_loading=false trackers.register("afm.loading",function(v) trace_loading=v end)
-local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end)
-local report_afm=logs.reporter("fonts","afm loading")
-local findbinfile=resolvers.findbinfile
-local definers=fonts.definers
-local readers=fonts.readers
-local constructors=fonts.constructors
-local afm=constructors.newhandler("afm")
-local pfb=constructors.newhandler("pfb")
-local afmfeatures=constructors.newfeatures("afm")
-local registerafmfeature=afmfeatures.register
-afm.version=1.410
-afm.cache=containers.define("fonts","afm",afm.version,true)
-afm.autoprefixed=true
-afm.helpdata={}
-afm.syncspace=true
-afm.addligatures=true
-afm.addtexligatures=true
-afm.addkerns=true
-local applyruntimefixes=fonts.treatments and fonts.treatments.applyfixes
-local function setmode(tfmdata,value)
- if value then
- tfmdata.properties.mode=lower(value)
- end
-end
-registerafmfeature {
- name="mode",
- description="mode",
- initializers={
- base=setmode,
- node=setmode,
- }
-}
-local comment=P("Comment")
-local spacing=patterns.spacer
-local lineend=patterns.newline
-local words=C((1-lineend)^1)
-local number=C((R("09")+S("."))^1)/tonumber*spacing^0
-local data=lpeg.Carg(1)
-local pattern=(
- comment*spacing*(
- data*(
- ("CODINGSCHEME"*spacing*words )/function(fd,a) end+("DESIGNSIZE"*spacing*number*words )/function(fd,a) fd[ 1]=a end+("CHECKSUM"*spacing*number*words )/function(fd,a) fd[ 2]=a end+("SPACE"*spacing*number*"plus"*number*"minus"*number)/function(fd,a,b,c) fd[ 3],fd[ 4],fd[ 5]=a,b,c end+("QUAD"*spacing*number )/function(fd,a) fd[ 6]=a end+("EXTRASPACE"*spacing*number )/function(fd,a) fd[ 7]=a end+("NUM"*spacing*number*number*number )/function(fd,a,b,c) fd[ 8],fd[ 9],fd[10]=a,b,c end+("DENOM"*spacing*number*number )/function(fd,a,b ) fd[11],fd[12]=a,b end+("SUP"*spacing*number*number*number )/function(fd,a,b,c) fd[13],fd[14],fd[15]=a,b,c end+("SUB"*spacing*number*number )/function(fd,a,b) fd[16],fd[17]=a,b end+("SUPDROP"*spacing*number )/function(fd,a) fd[18]=a end+("SUBDROP"*spacing*number )/function(fd,a) fd[19]=a end+("DELIM"*spacing*number*number )/function(fd,a,b) fd[20],fd[21]=a,b end+("AXISHEIGHT"*spacing*number )/function(fd,a) fd[22]=a end
- )+(1-lineend)^0
- )+(1-comment)^1
-)^0
-local function scan_comment(str)
- local fd={}
- lpegmatch(pattern,str,1,fd)
- return fd
-end
-local keys={}
-function keys.FontName (data,line) data.metadata.fontname=strip (line)
- data.metadata.fullname=strip (line) end
-function keys.ItalicAngle (data,line) data.metadata.italicangle=tonumber (line) end
-function keys.IsFixedPitch(data,line) data.metadata.isfixedpitch=toboolean(line,true) end
-function keys.CharWidth (data,line) data.metadata.charwidth=tonumber (line) end
-function keys.XHeight (data,line) data.metadata.xheight=tonumber (line) end
-function keys.Descender (data,line) data.metadata.descender=tonumber (line) end
-function keys.Ascender (data,line) data.metadata.ascender=tonumber (line) end
-function keys.Comment (data,line)
- line=lower(line)
- local designsize=match(line,"designsize[^%d]*(%d+)")
- if designsize then data.metadata.designsize=tonumber(designsize) end
-end
-local function get_charmetrics(data,charmetrics,vector)
- local characters=data.characters
- local chr,ind={},0
- for k,v in gmatch(charmetrics,"([%a]+) +(.-) *;") do
- if k=='C' then
- v=tonumber(v)
- if v<0 then
- ind=ind+1
- else
- ind=v
- end
- chr={
- index=ind
- }
- elseif k=='WX' then
- chr.width=tonumber(v)
- elseif k=='N' then
- characters[v]=chr
- elseif k=='B' then
- local llx,lly,urx,ury=match(v,"^ *(.-) +(.-) +(.-) +(.-)$")
- chr.boundingbox={ tonumber(llx),tonumber(lly),tonumber(urx),tonumber(ury) }
- elseif k=='L' then
- local plus,becomes=match(v,"^(.-) +(.-)$")
- local ligatures=chr.ligatures
- if ligatures then
- ligatures[plus]=becomes
- else
- chr.ligatures={ [plus]=becomes }
- end
- end
- end
-end
-local function get_kernpairs(data,kernpairs)
- local characters=data.characters
- for one,two,value in gmatch(kernpairs,"KPX +(.-) +(.-) +(.-)\n") do
- local chr=characters[one]
- if chr then
- local kerns=chr.kerns
- if kerns then
- kerns[two]=tonumber(value)
- else
- chr.kerns={ [two]=tonumber(value) }
- end
- end
- end
-end
-local function get_variables(data,fontmetrics)
- for key,rest in gmatch(fontmetrics,"(%a+) *(.-)[\n\r]") do
- local keyhandler=keys[key]
- if keyhandler then
- keyhandler(data,rest)
- end
- end
-end
-local function get_indexes(data,pfbname)
- data.resources.filename=resolvers.unresolve(pfbname)
- local pfbblob=fontloader.open(pfbname)
- if pfbblob then
- local characters=data.characters
- local pfbdata=fontloader.to_table(pfbblob)
- if pfbdata then
- local glyphs=pfbdata.glyphs
- if glyphs then
- if trace_loading then
- report_afm("getting index data from %a",pfbname)
- end
- for index,glyph in next,glyphs do
- local name=glyph.name
- if name then
- local char=characters[name]
- if char then
- if trace_indexing then
- report_afm("glyph %a has index %a",name,index)
- end
- char.index=index
- end
- end
- end
- elseif trace_loading then
- report_afm("no glyph data in pfb file %a",pfbname)
- end
- elseif trace_loading then
- report_afm("no data in pfb file %a",pfbname)
- end
- fontloader.close(pfbblob)
- elseif trace_loading then
- report_afm("invalid pfb file %a",pfbname)
- end
-end
-local function readafm(filename)
- local ok,afmblob,size=resolvers.loadbinfile(filename)
- if ok and afmblob then
- local data={
- resources={
- filename=resolvers.unresolve(filename),
- version=afm.version,
- creator="context mkiv",
- },
- properties={
- hasitalics=false,
- },
- goodies={},
- metadata={
- filename=file.removesuffix(file.basename(filename))
- },
- characters={
- },
- descriptions={
- },
- }
- afmblob=gsub(afmblob,"StartCharMetrics(.-)EndCharMetrics",function(charmetrics)
- if trace_loading then
- report_afm("loading char metrics")
- end
- get_charmetrics(data,charmetrics,vector)
- return ""
- end)
- afmblob=gsub(afmblob,"StartKernPairs(.-)EndKernPairs",function(kernpairs)
- if trace_loading then
- report_afm("loading kern pairs")
- end
- get_kernpairs(data,kernpairs)
- return ""
- end)
- afmblob=gsub(afmblob,"StartFontMetrics%s+([%d%.]+)(.-)EndFontMetrics",function(version,fontmetrics)
- if trace_loading then
- report_afm("loading variables")
- end
- data.afmversion=version
- get_variables(data,fontmetrics)
- data.fontdimens=scan_comment(fontmetrics)
- return ""
- end)
- return data
- else
- if trace_loading then
- report_afm("no valid afm file %a",filename)
- end
- return nil
- end
-end
-local addkerns,addligatures,addtexligatures,unify,normalize
-function afm.load(filename)
- filename=resolvers.findfile(filename,'afm') or ""
- if filename~="" and not fonts.names.ignoredfile(filename) then
- local name=file.removesuffix(file.basename(filename))
- local data=containers.read(afm.cache,name)
- local attr=lfs.attributes(filename)
- local size,time=attr.size or 0,attr.modification or 0
- local pfbfile=file.replacesuffix(name,"pfb")
- local pfbname=resolvers.findfile(pfbfile,"pfb") or ""
- if pfbname=="" then
- pfbname=resolvers.findfile(file.basename(pfbfile),"pfb") or ""
- end
- local pfbsize,pfbtime=0,0
- if pfbname~="" then
- local attr=lfs.attributes(pfbname)
- pfbsize=attr.size or 0
- pfbtime=attr.modification or 0
- end
- if not data or data.size~=size or data.time~=time or data.pfbsize~=pfbsize or data.pfbtime~=pfbtime then
- report_afm("reading %a",filename)
- data=readafm(filename)
- if data then
- if pfbname~="" then
- get_indexes(data,pfbname)
- elseif trace_loading then
- report_afm("no pfb file for %a",filename)
- end
- report_afm("unifying %a",filename)
- unify(data,filename)
- if afm.addligatures then
- report_afm("add ligatures")
- addligatures(data)
- end
- if afm.addtexligatures then
- report_afm("add tex ligatures")
- addtexligatures(data)
- end
- if afm.addkerns then
- report_afm("add extra kerns")
- addkerns(data)
- end
- normalize(data)
- report_afm("add tounicode data")
- fonts.mappings.addtounicode(data,filename)
- data.size=size
- data.time=time
- data.pfbsize=pfbsize
- data.pfbtime=pfbtime
- report_afm("saving %a in cache",name)
- data=containers.write(afm.cache,name,data)
- data=containers.read(afm.cache,name)
- end
- if applyruntimefixes and data then
- applyruntimefixes(filename,data)
- end
- end
- return data
- else
- return nil
- end
-end
-local uparser=fonts.mappings.makenameparser()
-unify=function(data,filename)
- local unicodevector=fonts.encodings.agl.unicodes
- local unicodes,names={},{}
- local private=constructors.privateoffset
- local descriptions=data.descriptions
- for name,blob in next,data.characters do
- local code=unicodevector[name]
- if not code then
- code=lpegmatch(uparser,name)
- if not code then
- code=private
- private=private+1
- report_afm("assigning private slot %U for unknown glyph name %a",code,name)
- end
- end
- local index=blob.index
- unicodes[name]=code
- names[name]=index
- blob.name=name
- descriptions[code]={
- boundingbox=blob.boundingbox,
- width=blob.width,
- kerns=blob.kerns,
- index=index,
- name=name,
- }
- end
- for unicode,description in next,descriptions do
- local kerns=description.kerns
- if kerns then
- local krn={}
- for name,kern in next,kerns do
- local unicode=unicodes[name]
- if unicode then
- krn[unicode]=kern
- else
- end
- end
- description.kerns=krn
- end
- end
- data.characters=nil
- local resources=data.resources
- local filename=resources.filename or file.removesuffix(file.basename(filename))
- resources.filename=resolvers.unresolve(filename)
- resources.unicodes=unicodes
- resources.marks={}
- resources.names=names
- resources.private=private
-end
-normalize=function(data)
-end
-local addthem=function(rawdata,ligatures)
- if ligatures then
- local descriptions=rawdata.descriptions
- local resources=rawdata.resources
- local unicodes=resources.unicodes
- local names=resources.names
- for ligname,ligdata in next,ligatures do
- local one=descriptions[unicodes[ligname]]
- if one then
- for _,pair in next,ligdata do
- local two,three=unicodes[pair[1]],unicodes[pair[2]]
- if two and three then
- local ol=one.ligatures
- if ol then
- if not ol[two] then
- ol[two]=three
- end
- else
- one.ligatures={ [two]=three }
- end
- end
- end
- end
- end
- end
-end
-addligatures=function(rawdata) addthem(rawdata,afm.helpdata.ligatures ) end
-addtexligatures=function(rawdata) addthem(rawdata,afm.helpdata.texligatures) end
-addkerns=function(rawdata)
- local descriptions=rawdata.descriptions
- local resources=rawdata.resources
- local unicodes=resources.unicodes
- local function do_it_left(what)
- if what then
- for unicode,description in next,descriptions do
- local kerns=description.kerns
- if kerns then
- local extrakerns
- for complex,simple in next,what do
- complex=unicodes[complex]
- simple=unicodes[simple]
- if complex and simple then
- local ks=kerns[simple]
- if ks and not kerns[complex] then
- if extrakerns then
- extrakerns[complex]=ks
- else
- extrakerns={ [complex]=ks }
- end
- end
- end
- end
- if extrakerns then
- description.extrakerns=extrakerns
- end
- end
- end
- end
- end
- local function do_it_copy(what)
- if what then
- for complex,simple in next,what do
- complex=unicodes[complex]
- simple=unicodes[simple]
- if complex and simple then
- local complexdescription=descriptions[complex]
- if complexdescription then
- local simpledescription=descriptions[complex]
- if simpledescription then
- local extrakerns
- local kerns=simpledescription.kerns
- if kerns then
- for unicode,kern in next,kerns do
- if extrakerns then
- extrakerns[unicode]=kern
- else
- extrakerns={ [unicode]=kern }
- end
- end
- end
- local extrakerns=simpledescription.extrakerns
- if extrakerns then
- for unicode,kern in next,extrakerns do
- if extrakerns then
- extrakerns[unicode]=kern
- else
- extrakerns={ [unicode]=kern }
- end
- end
- end
- if extrakerns then
- complexdescription.extrakerns=extrakerns
- end
- end
- end
- end
- end
- end
- end
- do_it_left(afm.helpdata.leftkerned)
- do_it_left(afm.helpdata.bothkerned)
- do_it_copy(afm.helpdata.bothkerned)
- do_it_copy(afm.helpdata.rightkerned)
-end
-local function adddimensions(data)
- if data then
- for unicode,description in next,data.descriptions do
- local bb=description.boundingbox
- if bb then
- local ht,dp=bb[4],-bb[2]
- if ht==0 or ht<0 then
- else
- description.height=ht
- end
- if dp==0 or dp<0 then
- else
- description.depth=dp
- end
- end
- end
- end
-end
-local function copytotfm(data)
- if data and data.descriptions then
- local metadata=data.metadata
- local resources=data.resources
- local properties=derivetable(data.properties)
- local descriptions=derivetable(data.descriptions)
- local goodies=derivetable(data.goodies)
- local characters={}
- local parameters={}
- local unicodes=resources.unicodes
- for unicode,description in next,data.descriptions do
- characters[unicode]={}
- end
- local filename=constructors.checkedfilename(resources)
- local fontname=metadata.fontname or metadata.fullname
- local fullname=metadata.fullname or metadata.fontname
- local endash=unicodes['space']
- local emdash=unicodes['emdash']
- local spacer="space"
- local spaceunits=500
- local monospaced=metadata.isfixedpitch
- local charwidth=metadata.charwidth
- local italicangle=metadata.italicangle
- local charxheight=metadata.xheight and metadata.xheight>0 and metadata.xheight
- properties.monospaced=monospaced
- parameters.italicangle=italicangle
- parameters.charwidth=charwidth
- parameters.charxheight=charxheight
- if properties.monospaced then
- if descriptions[endash] then
- spaceunits,spacer=descriptions[endash].width,"space"
- end
- if not spaceunits and descriptions[emdash] then
- spaceunits,spacer=descriptions[emdash].width,"emdash"
- end
- if not spaceunits and charwidth then
- spaceunits,spacer=charwidth,"charwidth"
- end
- else
- if descriptions[endash] then
- spaceunits,spacer=descriptions[endash].width,"space"
- end
- if not spaceunits and charwidth then
- spaceunits,spacer=charwidth,"charwidth"
- end
- end
- spaceunits=tonumber(spaceunits)
- if spaceunits<200 then
- end
- parameters.slant=0
- parameters.space=spaceunits
- parameters.space_stretch=500
- parameters.space_shrink=333
- parameters.x_height=400
- parameters.quad=1000
- if italicangle and italicangle~=0 then
- parameters.italicangle=italicangle
- parameters.italicfactor=math.cos(math.rad(90+italicangle))
- parameters.slant=- math.tan(italicangle*math.pi/180)
- end
- if monospaced then
- parameters.space_stretch=0
- parameters.space_shrink=0
- elseif afm.syncspace then
- parameters.space_stretch=spaceunits/2
- parameters.space_shrink=spaceunits/3
- end
- parameters.extra_space=parameters.space_shrink
- if charxheight then
- parameters.x_height=charxheight
- else
- local x=unicodes['x']
- if x then
- local x=descriptions[x]
- if x then
- parameters.x_height=x.height
- end
- end
- end
- local fd=data.fontdimens
- if fd and fd[8] and fd[9] and fd[10] then
- for k,v in next,fd do
- parameters[k]=v
- end
- end
- parameters.designsize=(metadata.designsize or 10)*65536
- parameters.ascender=abs(metadata.ascender or 0)
- 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
- properties.fullname=fullname
- properties.psname=fullname
- properties.name=filename or fullname or fontname
- if next(characters) then
- return {
- characters=characters,
- descriptions=descriptions,
- parameters=parameters,
- resources=resources,
- properties=properties,
- goodies=goodies,
- }
- end
- end
- return nil
-end
-function afm.setfeatures(tfmdata,features)
- local okay=constructors.initializefeatures("afm",tfmdata,features,trace_features,report_afm)
- if okay then
- return constructors.collectprocessors("afm",tfmdata,features,trace_features,report_afm)
- else
- return {}
- end
-end
-local function checkfeatures(specification)
-end
-local function afmtotfm(specification)
- local afmname=specification.filename or specification.name
- if specification.forced=="afm" or specification.format=="afm" then
- if trace_loading then
- report_afm("forcing afm format for %a",afmname)
- end
- else
- local tfmname=findbinfile(afmname,"ofm") or ""
- if tfmname~="" then
- if trace_loading then
- report_afm("fallback from afm to tfm for %a",afmname)
- end
- return
- end
- end
- if afmname~="" then
- local features=constructors.checkedfeatures("afm",specification.features.normal)
- specification.features.normal=features
- constructors.hashinstance(specification,true)
- specification=definers.resolve(specification)
- local cache_id=specification.hash
- local tfmdata=containers.read(constructors.cache,cache_id)
- if not tfmdata then
- local rawdata=afm.load(afmname)
- if rawdata and next(rawdata) then
- adddimensions(rawdata)
- tfmdata=copytotfm(rawdata)
- if tfmdata and next(tfmdata) then
- local shared=tfmdata.shared
- if not shared then
- shared={}
- tfmdata.shared=shared
- end
- shared.rawdata=rawdata
- shared.features=features
- shared.processes=afm.setfeatures(tfmdata,features)
- end
- elseif trace_loading then
- report_afm("no (valid) afm file found with name %a",afmname)
- end
- tfmdata=containers.write(constructors.cache,cache_id,tfmdata)
- end
- return tfmdata
- end
-end
-local function read_from_afm(specification)
- local tfmdata=afmtotfm(specification)
- if tfmdata then
- tfmdata.properties.name=specification.name
- tfmdata=constructors.scale(tfmdata,specification)
- local allfeatures=tfmdata.shared.features or specification.features.normal
- constructors.applymanipulators("afm",tfmdata,allfeatures,trace_features,report_afm)
- fonts.loggers.register(tfmdata,'afm',specification)
- end
- return tfmdata
-end
-local function prepareligatures(tfmdata,ligatures,value)
- if value then
- local descriptions=tfmdata.descriptions
- for unicode,character in next,tfmdata.characters do
- local description=descriptions[unicode]
- local dligatures=description.ligatures
- if dligatures then
- local cligatures=character.ligatures
- if not cligatures then
- cligatures={}
- character.ligatures=cligatures
- end
- for unicode,ligature in next,dligatures do
- cligatures[unicode]={
- char=ligature,
- type=0
- }
- end
- end
- end
- end
-end
-local function preparekerns(tfmdata,kerns,value)
- if value then
- local rawdata=tfmdata.shared.rawdata
- local resources=rawdata.resources
- local unicodes=resources.unicodes
- local descriptions=tfmdata.descriptions
- for u,chr in next,tfmdata.characters do
- local d=descriptions[u]
- local newkerns=d[kerns]
- if newkerns then
- local kerns=chr.kerns
- if not kerns then
- kerns={}
- chr.kerns=kerns
- end
- for k,v in next,newkerns do
- local uk=unicodes[k]
- if uk then
- kerns[uk]=v
- end
- end
- end
- end
- end
-end
-local list={
- [0x0027]=0x2019,
-}
-local function texreplacements(tfmdata,value)
- local descriptions=tfmdata.descriptions
- local characters=tfmdata.characters
- for k,v in next,list do
- characters [k]=characters [v]
- descriptions[k]=descriptions[v]
- end
-end
-local function ligatures (tfmdata,value) prepareligatures(tfmdata,'ligatures',value) end
-local function texligatures(tfmdata,value) prepareligatures(tfmdata,'texligatures',value) end
-local function kerns (tfmdata,value) preparekerns (tfmdata,'kerns',value) end
-local function extrakerns (tfmdata,value) preparekerns (tfmdata,'extrakerns',value) end
-registerafmfeature {
- name="liga",
- description="traditional ligatures",
- initializers={
- base=ligatures,
- node=ligatures,
- }
-}
-registerafmfeature {
- name="kern",
- description="intercharacter kerning",
- initializers={
- base=kerns,
- node=kerns,
- }
-}
-registerafmfeature {
- name="extrakerns",
- description="additional intercharacter kerning",
- initializers={
- base=extrakerns,
- node=extrakerns,
- }
-}
-registerafmfeature {
- name='tlig',
- description='tex ligatures',
- initializers={
- base=texligatures,
- node=texligatures,
- }
-}
-registerafmfeature {
- name='trep',
- description='tex replacements',
- initializers={
- base=texreplacements,
- node=texreplacements,
- }
-}
-local check_tfm=readers.check_tfm
-fonts.formats.afm="type1"
-fonts.formats.pfb="type1"
-local function check_afm(specification,fullname)
- local foundname=findbinfile(fullname,'afm') or ""
- if foundname=="" then
- foundname=fonts.names.getfilename(fullname,"afm") or ""
- end
- if foundname=="" and afm.autoprefixed then
- local encoding,shortname=match(fullname,"^(.-)%-(.*)$")
- if encoding and shortname and fonts.encodings.known[encoding] then
- shortname=findbinfile(shortname,'afm') or ""
- if shortname~="" then
- foundname=shortname
- if trace_defining then
- report_afm("stripping encoding prefix from filename %a",afmname)
- end
- end
- end
- end
- if foundname~="" then
- specification.filename=foundname
- specification.format="afm"
- return read_from_afm(specification)
- end
-end
-function readers.afm(specification,method)
- local fullname,tfmdata=specification.filename or "",nil
- if fullname=="" then
- local forced=specification.forced or ""
- if forced~="" then
- tfmdata=check_afm(specification,specification.name.."."..forced)
- end
- if not tfmdata then
- method=method or definers.method or "afm or tfm"
- if method=="tfm" then
- tfmdata=check_tfm(specification,specification.name)
- elseif method=="afm" then
- tfmdata=check_afm(specification,specification.name)
- elseif method=="tfm or afm" then
- tfmdata=check_tfm(specification,specification.name) or check_afm(specification,specification.name)
- else
- tfmdata=check_afm(specification,specification.name) or check_tfm(specification,specification.name)
- end
- end
- else
- tfmdata=check_afm(specification,fullname)
- end
- return tfmdata
-end
-function readers.pfb(specification,method)
- local original=specification.specification
- if trace_defining then
- report_afm("using afm reader for %a",original)
- end
- specification.specification=gsub(original,"%.pfb",".afm")
- specification.forced="afm"
- return readers.afm(specification,method)
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-afk']={
- version=1.001,
- comment="companion to font-afm.lua",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files",
- dataonly=true,
-}
-local allocate=utilities.storage.allocate
-fonts.handlers.afm.helpdata={
- ligatures=allocate {
- ['f']={
- { 'f','ff' },
- { 'i','fi' },
- { 'l','fl' },
- },
- ['ff']={
- { 'i','ffi' }
- },
- ['fi']={
- { 'i','fii' }
- },
- ['fl']={
- { 'i','fli' }
- },
- ['s']={
- { 't','st' }
- },
- ['i']={
- { 'j','ij' }
- },
- },
- texligatures=allocate {
- ['quoteleft']={
- { 'quoteleft','quotedblleft' }
- },
- ['quoteright']={
- { 'quoteright','quotedblright' }
- },
- ['hyphen']={
- { 'hyphen','endash' }
- },
- ['endash']={
- { 'hyphen','emdash' }
- }
- },
- leftkerned=allocate {
- AEligature="A",aeligature="a",
- OEligature="O",oeligature="o",
- IJligature="I",ijligature="i",
- AE="A",ae="a",
- OE="O",oe="o",
- IJ="I",ij="i",
- Ssharp="S",ssharp="s",
- },
- rightkerned=allocate {
- AEligature="E",aeligature="e",
- OEligature="E",oeligature="e",
- IJligature="J",ijligature="j",
- AE="E",ae="e",
- OE="E",oe="e",
- IJ="J",ij="j",
- Ssharp="S",ssharp="s",
- },
- bothkerned=allocate {
- Acircumflex="A",acircumflex="a",
- Ccircumflex="C",ccircumflex="c",
- Ecircumflex="E",ecircumflex="e",
- Gcircumflex="G",gcircumflex="g",
- Hcircumflex="H",hcircumflex="h",
- Icircumflex="I",icircumflex="i",
- Jcircumflex="J",jcircumflex="j",
- Ocircumflex="O",ocircumflex="o",
- Scircumflex="S",scircumflex="s",
- Ucircumflex="U",ucircumflex="u",
- Wcircumflex="W",wcircumflex="w",
- Ycircumflex="Y",ycircumflex="y",
- Agrave="A",agrave="a",
- Egrave="E",egrave="e",
- Igrave="I",igrave="i",
- Ograve="O",ograve="o",
- Ugrave="U",ugrave="u",
- Ygrave="Y",ygrave="y",
- Atilde="A",atilde="a",
- Itilde="I",itilde="i",
- Otilde="O",otilde="o",
- Utilde="U",utilde="u",
- Ntilde="N",ntilde="n",
- Adiaeresis="A",adiaeresis="a",Adieresis="A",adieresis="a",
- Ediaeresis="E",ediaeresis="e",Edieresis="E",edieresis="e",
- Idiaeresis="I",idiaeresis="i",Idieresis="I",idieresis="i",
- Odiaeresis="O",odiaeresis="o",Odieresis="O",odieresis="o",
- Udiaeresis="U",udiaeresis="u",Udieresis="U",udieresis="u",
- Ydiaeresis="Y",ydiaeresis="y",Ydieresis="Y",ydieresis="y",
- Aacute="A",aacute="a",
- Cacute="C",cacute="c",
- Eacute="E",eacute="e",
- Iacute="I",iacute="i",
- Lacute="L",lacute="l",
- Nacute="N",nacute="n",
- Oacute="O",oacute="o",
- Racute="R",racute="r",
- Sacute="S",sacute="s",
- Uacute="U",uacute="u",
- Yacute="Y",yacute="y",
- Zacute="Z",zacute="z",
- Dstroke="D",dstroke="d",
- Hstroke="H",hstroke="h",
- Tstroke="T",tstroke="t",
- Cdotaccent="C",cdotaccent="c",
- Edotaccent="E",edotaccent="e",
- Gdotaccent="G",gdotaccent="g",
- Idotaccent="I",idotaccent="i",
- Zdotaccent="Z",zdotaccent="z",
- Amacron="A",amacron="a",
- Emacron="E",emacron="e",
- Imacron="I",imacron="i",
- Omacron="O",omacron="o",
- Umacron="U",umacron="u",
- Ccedilla="C",ccedilla="c",
- Kcedilla="K",kcedilla="k",
- Lcedilla="L",lcedilla="l",
- Ncedilla="N",ncedilla="n",
- Rcedilla="R",rcedilla="r",
- Scedilla="S",scedilla="s",
- Tcedilla="T",tcedilla="t",
- Ohungarumlaut="O",ohungarumlaut="o",
- Uhungarumlaut="U",uhungarumlaut="u",
- Aogonek="A",aogonek="a",
- Eogonek="E",eogonek="e",
- Iogonek="I",iogonek="i",
- Uogonek="U",uogonek="u",
- Aring="A",aring="a",
- Uring="U",uring="u",
- Abreve="A",abreve="a",
- Ebreve="E",ebreve="e",
- Gbreve="G",gbreve="g",
- Ibreve="I",ibreve="i",
- Obreve="O",obreve="o",
- Ubreve="U",ubreve="u",
- Ccaron="C",ccaron="c",
- Dcaron="D",dcaron="d",
- Ecaron="E",ecaron="e",
- Lcaron="L",lcaron="l",
- Ncaron="N",ncaron="n",
- Rcaron="R",rcaron="r",
- Scaron="S",scaron="s",
- Tcaron="T",tcaron="t",
- Zcaron="Z",zcaron="z",
- dotlessI="I",dotlessi="i",
- dotlessJ="J",dotlessj="j",
- AEligature="AE",aeligature="ae",AE="AE",ae="ae",
- OEligature="OE",oeligature="oe",OE="OE",oe="oe",
- IJligature="IJ",ijligature="ij",IJ="IJ",ij="ij",
- Lstroke="L",lstroke="l",Lslash="L",lslash="l",
- Ostroke="O",ostroke="o",Oslash="O",oslash="o",
- Ssharp="SS",ssharp="ss",
- Aumlaut="A",aumlaut="a",
- Eumlaut="E",eumlaut="e",
- Iumlaut="I",iumlaut="i",
- Oumlaut="O",oumlaut="o",
- Uumlaut="U",uumlaut="u",
- }
-}
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['luatex-fonts-tfm']={
- version=1.001,
- comment="companion to luatex-*.tex",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-if context then
- texio.write_nl("fatal error: this module is not for context")
- os.exit()
-end
-local fonts=fonts
-local tfm={}
-fonts.handlers.tfm=tfm
-fonts.formats.tfm="type1"
-function fonts.readers.tfm(specification)
- local fullname=specification.filename or ""
- if fullname=="" then
- local forced=specification.forced or ""
- if forced~="" then
- fullname=specification.name.."."..forced
- else
- fullname=specification.name
- end
- end
- local foundname=resolvers.findbinfile(fullname,'tfm') or ""
- if foundname=="" then
- foundname=resolvers.findbinfile(fullname,'ofm') or ""
- end
- if foundname~="" then
- specification.filename=foundname
- specification.format="ofm"
- return font.read_tfm(specification.filename,specification.size)
- end
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-oti']={
- version=1.001,
- comment="companion to font-ini.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local lower=string.lower
-local fonts=fonts
-local constructors=fonts.constructors
-local otf=constructors.newhandler("otf")
-local otffeatures=constructors.newfeatures("otf")
-local otftables=otf.tables
-local registerotffeature=otffeatures.register
-local allocate=utilities.storage.allocate
-registerotffeature {
- name="features",
- description="initialization of feature handler",
- default=true,
-}
-local function setmode(tfmdata,value)
- if value then
- tfmdata.properties.mode=lower(value)
- end
-end
-local function setlanguage(tfmdata,value)
- if value then
- local cleanvalue=lower(value)
- local languages=otftables and otftables.languages
- local properties=tfmdata.properties
- if not languages then
- properties.language=cleanvalue
- elseif languages[value] then
- properties.language=cleanvalue
- else
- properties.language="dflt"
- end
- end
-end
-local function setscript(tfmdata,value)
- if value then
- local cleanvalue=lower(value)
- local scripts=otftables and otftables.scripts
- local properties=tfmdata.properties
- if not scripts then
- properties.script=cleanvalue
- elseif scripts[value] then
- properties.script=cleanvalue
- else
- properties.script="dflt"
- end
- end
-end
-registerotffeature {
- name="mode",
- description="mode",
- initializers={
- base=setmode,
- node=setmode,
- }
-}
-registerotffeature {
- name="language",
- description="language",
- initializers={
- base=setlanguage,
- node=setlanguage,
- }
-}
-registerotffeature {
- name="script",
- description="script",
- initializers={
- base=setscript,
- node=setscript,
- }
-}
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-otf']={
- version=1.001,
- comment="companion to font-ini.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local utfbyte=utf.byte
-local format,gmatch,gsub,find,match,lower,strip=string.format,string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip
-local type,next,tonumber,tostring=type,next,tonumber,tostring
-local abs=math.abs
-local insert=table.insert
-local lpegmatch=lpeg.match
-local reversed,concat,remove,sortedkeys=table.reversed,table.concat,table.remove,table.sortedkeys
-local ioflush=io.flush
-local fastcopy,tohash,derivetable=table.fastcopy,table.tohash,table.derive
-local formatters=string.formatters
-local allocate=utilities.storage.allocate
-local registertracker=trackers.register
-local registerdirective=directives.register
-local starttiming=statistics.starttiming
-local stoptiming=statistics.stoptiming
-local elapsedtime=statistics.elapsedtime
-local findbinfile=resolvers.findbinfile
-local trace_private=false registertracker("otf.private",function(v) trace_private=v end)
-local trace_loading=false registertracker("otf.loading",function(v) trace_loading=v end)
-local trace_features=false registertracker("otf.features",function(v) trace_features=v end)
-local trace_dynamics=false registertracker("otf.dynamics",function(v) trace_dynamics=v end)
-local trace_sequences=false registertracker("otf.sequences",function(v) trace_sequences=v end)
-local trace_markwidth=false registertracker("otf.markwidth",function(v) trace_markwidth=v end)
-local trace_defining=false registertracker("fonts.defining",function(v) trace_defining=v end)
-local report_otf=logs.reporter("fonts","otf loading")
-local fonts=fonts
-local otf=fonts.handlers.otf
-otf.glists={ "gsub","gpos" }
-otf.version=2.759
-otf.cache=containers.define("fonts","otf",otf.version,true)
-local fontdata=fonts.hashes.identifiers
-local chardata=characters and characters.data
-local otffeatures=fonts.constructors.newfeatures("otf")
-local registerotffeature=otffeatures.register
-local enhancers=allocate()
-otf.enhancers=enhancers
-local patches={}
-enhancers.patches=patches
-local definers=fonts.definers
-local readers=fonts.readers
-local constructors=fonts.constructors
-local forceload=false
-local cleanup=0
-local usemetatables=false
-local packdata=true
-local syncspace=true
-local forcenotdef=false
-local includesubfonts=false
-local overloadkerns=false
-local applyruntimefixes=fonts.treatments and fonts.treatments.applyfixes
-local wildcard="*"
-local default="dflt"
-local fontloaderfields=fontloader.fields
-local mainfields=nil
-local glyphfields=nil
-local formats=fonts.formats
-formats.otf="opentype"
-formats.ttf="truetype"
-formats.ttc="truetype"
-formats.dfont="truetype"
-registerdirective("fonts.otf.loader.cleanup",function(v) cleanup=tonumber(v) or (v and 1) or 0 end)
-registerdirective("fonts.otf.loader.force",function(v) forceload=v end)
-registerdirective("fonts.otf.loader.usemetatables",function(v) usemetatables=v end)
-registerdirective("fonts.otf.loader.pack",function(v) packdata=v end)
-registerdirective("fonts.otf.loader.syncspace",function(v) syncspace=v end)
-registerdirective("fonts.otf.loader.forcenotdef",function(v) forcenotdef=v end)
-registerdirective("fonts.otf.loader.overloadkerns",function(v) overloadkerns=v end)
-function otf.fileformat(filename)
- local leader=lower(io.loadchunk(filename,4))
- local suffix=lower(file.suffix(filename))
- if leader=="otto" then
- return formats.otf,suffix=="otf"
- elseif leader=="ttcf" then
- return formats.ttc,suffix=="ttc"
- elseif suffix=="ttc" then
- return formats.ttc,true
- elseif suffix=="dfont" then
- return formats.dfont,true
- else
- return formats.ttf,suffix=="ttf"
- end
-end
-local function otf_format(filename)
- local format,okay=otf.fileformat(filename)
- if not okay then
- report_otf("font %a is actually an %a file",filename,format)
- end
- return format
-end
-local function load_featurefile(raw,featurefile)
- if featurefile and featurefile~="" then
- if trace_loading then
- report_otf("using featurefile %a",featurefile)
- end
- fontloader.apply_featurefile(raw,featurefile)
- end
-end
-local function showfeatureorder(rawdata,filename)
- local sequences=rawdata.resources.sequences
- if sequences and #sequences>0 then
- if trace_loading then
- report_otf("font %a has %s sequences",filename,#sequences)
- report_otf(" ")
- end
- for nos=1,#sequences do
- local sequence=sequences[nos]
- local typ=sequence.type or "no-type"
- local name=sequence.name or "no-name"
- local subtables=sequence.subtables or { "no-subtables" }
- local features=sequence.features
- if trace_loading then
- report_otf("%3i %-15s %-20s [% t]",nos,name,typ,subtables)
- end
- if features then
- for feature,scripts in next,features do
- local tt={}
- if type(scripts)=="table" then
- for script,languages in next,scripts do
- local ttt={}
- for language,_ in next,languages do
- ttt[#ttt+1]=language
- end
- tt[#tt+1]=formatters["[%s: % t]"](script,ttt)
- end
- if trace_loading then
- report_otf(" %s: % t",feature,tt)
- end
- else
- if trace_loading then
- report_otf(" %s: %S",feature,scripts)
- end
- end
- end
- end
- end
- if trace_loading then
- report_otf("\n")
- end
- elseif trace_loading then
- report_otf("font %a has no sequences",filename)
- end
-end
-local valid_fields=table.tohash {
- "ascent",
- "cidinfo",
- "copyright",
- "descent",
- "design_range_bottom",
- "design_range_top",
- "design_size",
- "encodingchanged",
- "extrema_bound",
- "familyname",
- "fontname",
- "fontname",
- "fontstyle_id",
- "fontstyle_name",
- "fullname",
- "hasvmetrics",
- "horiz_base",
- "issans",
- "isserif",
- "italicangle",
- "macstyle",
- "onlybitmaps",
- "origname",
- "os2_version",
- "pfminfo",
- "serifcheck",
- "sfd_version",
- "strokedfont",
- "strokewidth",
- "table_version",
- "ttf_tables",
- "uni_interp",
- "uniqueid",
- "units_per_em",
- "upos",
- "use_typo_metrics",
- "uwidth",
- "validation_state",
- "version",
- "vert_base",
- "weight",
- "weight_width_slope_only",
-}
-local ordered_enhancers={
- "prepare tables",
- "prepare glyphs",
- "prepare lookups",
- "analyze glyphs",
- "analyze math",
- "prepare tounicode",
- "reorganize lookups",
- "reorganize mark classes",
- "reorganize anchor classes",
- "reorganize glyph kerns",
- "reorganize glyph lookups",
- "reorganize glyph anchors",
- "merge kern classes",
- "reorganize features",
- "reorganize subtables",
- "check glyphs",
- "check metadata",
- "check extra features",
- "check encoding",
- "add duplicates",
- "cleanup tables",
-}
-local actions=allocate()
-local before=allocate()
-local after=allocate()
-patches.before=before
-patches.after=after
-local function enhance(name,data,filename,raw)
- local enhancer=actions[name]
- if enhancer then
- if trace_loading then
- report_otf("apply enhancement %a to file %a",name,filename)
- ioflush()
- end
- enhancer(data,filename,raw)
- else
- end
-end
-function enhancers.apply(data,filename,raw)
- local basename=file.basename(lower(filename))
- if trace_loading then
- report_otf("%s enhancing file %a","start",filename)
- end
- ioflush()
- for e=1,#ordered_enhancers do
- local enhancer=ordered_enhancers[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)
- 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()
- end
- if trace_loading then
- report_otf("%s enhancing file %a","stop",filename)
- end
- ioflush()
-end
-function patches.register(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}
- end
- end
-end
-function patches.report(fmt,...)
- if trace_loading then
- report_otf("patching: %s",formatters[fmt](...))
- end
-end
-function enhancers.register(what,action)
- actions[what]=action
-end
-function otf.load(filename,sub,featurefile)
- local base=file.basename(file.removesuffix(filename))
- local name=file.removesuffix(base)
- local attr=lfs.attributes(filename)
- local size=attr and attr.size or 0
- local time=attr and attr.modification or 0
- if featurefile then
- name=name.."@"..file.removesuffix(file.basename(featurefile))
- end
- if sub=="" then
- sub=false
- end
- local hash=name
- if sub then
- hash=hash.."-"..sub
- end
- hash=containers.cleanname(hash)
- local featurefiles
- if featurefile then
- featurefiles={}
- for s in gmatch(featurefile,"[^,]+") do
- local name=resolvers.findfile(file.addsuffix(s,'fea'),'fea') or ""
- if name=="" then
- report_otf("loading error, no featurefile %a",s)
- else
- local attr=lfs.attributes(name)
- featurefiles[#featurefiles+1]={
- name=name,
- size=attr and attr.size or 0,
- time=attr and attr.modification or 0,
- }
- end
- end
- if #featurefiles==0 then
- featurefiles=nil
- end
- end
- local data=containers.read(otf.cache,hash)
- local reload=not data or data.size~=size or data.time~=time
- if forceload then
- report_otf("forced reload of %a due to hard coded flag",filename)
- reload=true
- end
- if not reload then
- local featuredata=data.featuredata
- if featurefiles then
- if not featuredata or #featuredata~=#featurefiles then
- reload=true
- else
- for i=1,#featurefiles do
- local fi,fd=featurefiles[i],featuredata[i]
- if fi.name~=fd.name or fi.size~=fd.size or fi.time~=fd.time then
- reload=true
- break
- end
- end
- end
- elseif featuredata then
- reload=true
- end
- if reload then
- report_otf("loading: forced reload due to changed featurefile specification %a",featurefile)
- end
- end
- if reload then
- report_otf("loading %a, hash %a",filename,hash)
- local fontdata,messages
- if sub then
- fontdata,messages=fontloader.open(filename,sub)
- else
- fontdata,messages=fontloader.open(filename)
- end
- if fontdata then
- mainfields=mainfields or (fontloaderfields and fontloaderfields(fontdata))
- end
- if trace_loading and messages and #messages>0 then
- if type(messages)=="string" then
- report_otf("warning: %s",messages)
- else
- for m=1,#messages do
- report_otf("warning: %S",messages[m])
- end
- end
- else
- report_otf("loading done")
- end
- if fontdata then
- if featurefiles then
- for i=1,#featurefiles do
- load_featurefile(fontdata,featurefiles[i].name)
- end
- end
- local unicodes={
- }
- local splitter=lpeg.splitter(" ",unicodes)
- data={
- size=size,
- time=time,
- format=otf_format(filename),
- featuredata=featurefiles,
- resources={
- filename=resolvers.unresolve(filename),
- version=otf.version,
- creator="context mkiv",
- unicodes=unicodes,
- indices={
- },
- duplicates={
- },
- variants={
- },
- lookuptypes={},
- },
- metadata={
- },
- properties={
- },
- descriptions={},
- goodies={},
- helpers={
- tounicodelist=splitter,
- tounicodetable=lpeg.Ct(splitter),
- },
- }
- starttiming(data)
- report_otf("file size: %s",size)
- enhancers.apply(data,filename,fontdata)
- local packtime={}
- if packdata then
- if cleanup>0 then
- collectgarbage("collect")
- end
- starttiming(packtime)
- enhance("pack",data,filename,nil)
- stoptiming(packtime)
- end
- report_otf("saving %a in cache",filename)
- data=containers.write(otf.cache,hash,data)
- if cleanup>1 then
- collectgarbage("collect")
- end
- stoptiming(data)
- if elapsedtime then
- report_otf("preprocessing and caching time %s, packtime %s",
- elapsedtime(data),packdata and elapsedtime(packtime) or 0)
- end
- fontloader.close(fontdata)
- if cleanup>3 then
- collectgarbage("collect")
- end
- data=containers.read(otf.cache,hash)
- if cleanup>2 then
- collectgarbage("collect")
- end
- else
- data=nil
- report_otf("loading failed due to read error")
- end
- end
- if data then
- if trace_defining then
- report_otf("loading from cache using hash %a",hash)
- end
- enhance("unpack",data,filename,nil,false)
- if applyruntimefixes then
- applyruntimefixes(filename,data)
- end
- enhance("add dimensions",data,filename,nil,false)
- if trace_sequences then
- showfeatureorder(data,filename)
- end
- end
- return data
-end
-local mt={
- __index=function(t,k)
- if k=="height" then
- local ht=t.boundingbox[4]
- return ht<0 and 0 or ht
- elseif k=="depth" then
- local dp=-t.boundingbox[2]
- return dp<0 and 0 or dp
- elseif k=="width" then
- return 0
- elseif k=="name" then
- return forcenotdef and ".notdef"
- end
- end
-}
-actions["prepare tables"]=function(data,filename,raw)
- data.properties.hasitalics=false
-end
-actions["add dimensions"]=function(data,filename)
- if data then
- local descriptions=data.descriptions
- local resources=data.resources
- local defaultwidth=resources.defaultwidth or 0
- local defaultheight=resources.defaultheight or 0
- local defaultdepth=resources.defaultdepth or 0
- local basename=trace_markwidth and file.basename(filename)
- if usemetatables then
- for _,d in next,descriptions do
- local wd=d.width
- if not wd then
- d.width=defaultwidth
- elseif trace_markwidth and wd~=0 and d.class=="mark" then
- report_otf("mark %a with width %b found in %a",d.name or "<noname>",wd,basename)
- end
- setmetatable(d,mt)
- end
- else
- for _,d in next,descriptions do
- local bb,wd=d.boundingbox,d.width
- if not wd then
- d.width=defaultwidth
- elseif trace_markwidth and wd~=0 and d.class=="mark" then
- report_otf("mark %a with width %b found in %a",d.name or "<noname>",wd,basename)
- end
- if bb then
- local ht,dp=bb[4],-bb[2]
- if ht==0 or ht<0 then
- else
- d.height=ht
- end
- if dp==0 or dp<0 then
- else
- d.depth=dp
- end
- end
- end
- end
- end
-end
-local function somecopy(old)
- if old then
- local new={}
- if type(old)=="table" then
- for k,v in next,old do
- if k=="glyphs" then
- elseif type(v)=="table" then
- new[k]=somecopy(v)
- else
- new[k]=v
- end
- end
- else
- for i=1,#mainfields do
- local k=mainfields[i]
- local v=old[k]
- if k=="glyphs" then
- elseif type(v)=="table" then
- new[k]=somecopy(v)
- else
- new[k]=v
- end
- end
- end
- return new
- else
- return {}
- end
-end
-actions["prepare glyphs"]=function(data,filename,raw)
- local rawglyphs=raw.glyphs
- local rawsubfonts=raw.subfonts
- local rawcidinfo=raw.cidinfo
- local criterium=constructors.privateoffset
- local private=criterium
- local resources=data.resources
- local metadata=data.metadata
- local properties=data.properties
- local descriptions=data.descriptions
- local unicodes=resources.unicodes
- local indices=resources.indices
- local duplicates=resources.duplicates
- local variants=resources.variants
- if rawsubfonts then
- metadata.subfonts=includesubfonts and {}
- properties.cidinfo=rawcidinfo
- if rawcidinfo.registry then
- local cidmap=fonts.cid.getmap(rawcidinfo)
- if cidmap then
- rawcidinfo.usedname=cidmap.usedname
- local nofnames,nofunicodes=0,0
- local cidunicodes,cidnames=cidmap.unicodes,cidmap.names
- for cidindex=1,#rawsubfonts do
- local subfont=rawsubfonts[cidindex]
- local cidglyphs=subfont.glyphs
- if includesubfonts then
- metadata.subfonts[cidindex]=somecopy(subfont)
- end
- for index=0,subfont.glyphcnt-1 do
- local glyph=cidglyphs[index]
- if glyph then
- local unicode=glyph.unicode
-if unicode>=0x00E000 and unicode<=0x00F8FF then
- unicode=-1
-elseif unicode>=0x0F0000 and unicode<=0x0FFFFD then
- unicode=-1
-elseif unicode>=0x100000 and unicode<=0x10FFFD then
- unicode=-1
-end
- local name=glyph.name or cidnames[index]
- if not unicode or unicode==-1 then
- unicode=cidunicodes[index]
- end
- if unicode and descriptions[unicode] then
- if trace_private then
- report_otf("preventing glyph %a at index %H to overload unicode %U",name or "noname",index,unicode)
- end
- unicode=-1
- end
- if not unicode or unicode==-1 then
- if not name then
- name=format("u%06X.ctx",private)
- end
- unicode=private
- unicodes[name]=private
- if trace_private then
- report_otf("glyph %a at index %H is moved to private unicode slot %U",name,index,private)
- end
- private=private+1
- nofnames=nofnames+1
- else
- if not name then
- name=format("u%06X.ctx",unicode)
- end
- unicodes[name]=unicode
- nofunicodes=nofunicodes+1
- end
- indices[index]=unicode
- local description={
- boundingbox=glyph.boundingbox,
- name=glyph.name or name or "unknown",
- cidindex=cidindex,
- index=index,
- glyph=glyph,
- }
- descriptions[unicode]=description
- else
- end
- end
- end
- if trace_loading then
- report_otf("cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes,nofnames,nofunicodes+nofnames)
- end
- elseif trace_loading then
- report_otf("unable to remap cid font, missing cid file for %a",filename)
- end
- elseif trace_loading then
- report_otf("font %a has no glyphs",filename)
- end
- else
- for index=0,raw.glyphcnt-1 do
- local glyph=rawglyphs[index]
- if glyph then
- local unicode=glyph.unicode
- local name=glyph.name
- if not unicode or unicode==-1 then
- unicode=private
- unicodes[name]=private
- if trace_private then
- report_otf("glyph %a at index %H is moved to private unicode slot %U",name,index,private)
- end
- private=private+1
- else
- if unicode>criterium then
- local taken=descriptions[unicode]
- if taken then
- if unicode>=private then
- private=unicode+1
- else
- private=private+1
- end
- descriptions[private]=taken
- unicodes[taken.name]=private
- indices[taken.index]=private
- if trace_private then
- report_otf("slot %U is moved to %U due to private in font",unicode)
- end
- else
- if unicode>=private then
- private=unicode+1
- end
- end
- end
- unicodes[name]=unicode
- end
- indices[index]=unicode
- descriptions[unicode]={
- boundingbox=glyph.boundingbox,
- name=name,
- index=index,
- glyph=glyph,
- }
- local altuni=glyph.altuni
- if altuni then
- for i=1,#altuni do
- local a=altuni[i]
- local u=a.unicode
- local v=a.variant
- if v then
- local vv=variants[v]
- if vv then
- vv[u]=unicode
- else
- vv={ [u]=unicode }
- variants[v]=vv
- end
- end
- end
- end
- else
- report_otf("potential problem: glyph %U is used but empty",index)
- end
- end
- end
- resources.private=private
-end
-actions["check encoding"]=function(data,filename,raw)
- local descriptions=data.descriptions
- local resources=data.resources
- local properties=data.properties
- local unicodes=resources.unicodes
- local indices=resources.indices
- local duplicates=resources.duplicates
- local mapdata=raw.map or {}
- local unicodetoindex=mapdata and mapdata.map or {}
- local indextounicode=mapdata and mapdata.backmap or {}
- local encname=lower(data.enc_name or mapdata.enc_name or "")
- local criterium=0xFFFF
- local privateoffset=constructors.privateoffset
- if find(encname,"unicode") then
- if trace_loading then
- report_otf("checking embedded unicode map %a",encname)
- end
- local reported={}
- for maybeunicode,index in next,unicodetoindex do
- if descriptions[maybeunicode] then
- else
- local unicode=indices[index]
- if not unicode then
- elseif maybeunicode==unicode then
- elseif unicode>privateoffset then
- else
- local d=descriptions[unicode]
- if d then
- local c=d.copies
- if c then
- c[maybeunicode]=true
- else
- d.copies={ [maybeunicode]=true }
- end
- elseif index and not reported[index] then
- report_otf("missing index %i",index)
- reported[index]=true
- end
- end
- end
- end
- for unicode,data in next,descriptions do
- local d=data.copies
- if d then
- duplicates[unicode]=sortedkeys(d)
- data.copies=nil
- end
- end
- elseif properties.cidinfo then
- report_otf("warning: no unicode map, used cidmap %a",properties.cidinfo.usedname)
- else
- report_otf("warning: non unicode map %a, only using glyph unicode data",encname or "whatever")
- end
- if mapdata then
- mapdata.map={}
- mapdata.backmap={}
- end
-end
-actions["add duplicates"]=function(data,filename,raw)
- local descriptions=data.descriptions
- local resources=data.resources
- local properties=data.properties
- local unicodes=resources.unicodes
- local indices=resources.indices
- local duplicates=resources.duplicates
- for unicode,d in next,duplicates do
- local nofduplicates=#d
- if nofduplicates>4 then
- if trace_loading then
- report_otf("ignoring excessive duplicates of %U (n=%s)",unicode,nofduplicates)
- end
- else
- for i=1,nofduplicates do
- local u=d[i]
- if not descriptions[u] then
- local description=descriptions[unicode]
- local n=0
- for _,description in next,descriptions do
- if kerns then
- local kerns=description.kerns
- for _,k in next,kerns do
- local ku=k[unicode]
- if ku then
- k[u]=ku
- n=n+1
- end
- end
- end
- end
- if u>0 then
- local duplicate=table.copy(description)
- duplicate.comment=format("copy of U+%05X",unicode)
- descriptions[u]=duplicate
- if trace_loading then
- report_otf("duplicating %U to %U with index %H (%s kerns)",unicode,u,description.index,n)
- end
- end
- end
- end
- end
- end
-end
-actions["analyze glyphs"]=function(data,filename,raw)
- local descriptions=data.descriptions
- local resources=data.resources
- local metadata=data.metadata
- local properties=data.properties
- local hasitalics=false
- local widths={}
- local marks={}
- for unicode,description in next,descriptions do
- local glyph=description.glyph
- local italic=glyph.italic_correction
- if not italic then
- elseif italic==0 then
- else
- description.italic=italic
- hasitalics=true
- end
- local width=glyph.width
- widths[width]=(widths[width] or 0)+1
- local class=glyph.class
- if class then
- if class=="mark" then
- marks[unicode]=true
- end
- description.class=class
- end
- end
- properties.hasitalics=hasitalics
- resources.marks=marks
- local wd,most=0,1
- for k,v in next,widths do
- if v>most then
- wd,most=k,v
- end
- end
- if most>1000 then
- if trace_loading then
- report_otf("most common width: %s (%s times), sharing (cjk font)",wd,most)
- end
- for unicode,description in next,descriptions do
- if description.width==wd then
- else
- description.width=description.glyph.width
- end
- end
- resources.defaultwidth=wd
- else
- for unicode,description in next,descriptions do
- description.width=description.glyph.width
- end
- end
-end
-actions["reorganize mark classes"]=function(data,filename,raw)
- local mark_classes=raw.mark_classes
- if mark_classes then
- local resources=data.resources
- local unicodes=resources.unicodes
- local markclasses={}
- resources.markclasses=markclasses
- for name,class in next,mark_classes do
- local t={}
- for s in gmatch(class,"[^ ]+") do
- t[unicodes[s]]=true
- end
- markclasses[name]=t
- end
- end
-end
-actions["reorganize features"]=function(data,filename,raw)
- local features={}
- data.resources.features=features
- for k,what in next,otf.glists do
- local dw=raw[what]
- if dw then
- local f={}
- features[what]=f
- for i=1,#dw do
- local d=dw[i]
- local dfeatures=d.features
- if dfeatures then
- for i=1,#dfeatures do
- local df=dfeatures[i]
- local tag=strip(lower(df.tag))
- local ft=f[tag]
- if not ft then
- ft={}
- f[tag]=ft
- end
- local dscripts=df.scripts
- for i=1,#dscripts do
- local d=dscripts[i]
- local languages=d.langs
- local script=strip(lower(d.script))
- local fts=ft[script] if not fts then fts={} ft[script]=fts end
- for i=1,#languages do
- fts[strip(lower(languages[i]))]=true
- end
- end
- end
- end
- end
- end
- end
-end
-actions["reorganize anchor classes"]=function(data,filename,raw)
- local resources=data.resources
- local anchor_to_lookup={}
- local lookup_to_anchor={}
- resources.anchor_to_lookup=anchor_to_lookup
- resources.lookup_to_anchor=lookup_to_anchor
- local classes=raw.anchor_classes
- if classes then
- for c=1,#classes do
- local class=classes[c]
- local anchor=class.name
- local lookups=class.lookup
- if type(lookups)~="table" then
- lookups={ lookups }
- end
- local a=anchor_to_lookup[anchor]
- if not a then
- a={}
- anchor_to_lookup[anchor]=a
- end
- for l=1,#lookups do
- local lookup=lookups[l]
- local l=lookup_to_anchor[lookup]
- if l then
- l[anchor]=true
- else
- l={ [anchor]=true }
- lookup_to_anchor[lookup]=l
- end
- a[lookup]=true
- end
- end
- end
-end
-actions["prepare tounicode"]=function(data,filename,raw)
- fonts.mappings.addtounicode(data,filename)
-end
-local g_directions={
- gsub_contextchain=1,
- gpos_contextchain=1,
- gsub_reversecontextchain=-1,
- gpos_reversecontextchain=-1,
-}
-actions["reorganize subtables"]=function(data,filename,raw)
- local resources=data.resources
- local sequences={}
- local lookups={}
- local chainedfeatures={}
- resources.sequences=sequences
- resources.lookups=lookups
- for _,what in next,otf.glists do
- local dw=raw[what]
- if dw then
- for k=1,#dw do
- local gk=dw[k]
- local features=gk.features
- local typ=gk.type
- local chain=g_directions[typ] or 0
- local subtables=gk.subtables
- if subtables then
- local t={}
- for s=1,#subtables do
- t[s]=subtables[s].name
- end
- subtables=t
- end
- local flags,markclass=gk.flags,nil
- if flags then
- local t={
- (flags.ignorecombiningmarks and "mark") or false,
- (flags.ignoreligatures and "ligature") or false,
- (flags.ignorebaseglyphs and "base") or false,
- flags.r2l or false,
- }
- markclass=flags.mark_class
- if markclass then
- markclass=resources.markclasses[markclass]
- end
- flags=t
- end
- local name=gk.name
- if not name then
- report_otf("skipping weird lookup number %s",k)
- elseif features then
- local f={}
- local o={}
- for i=1,#features do
- local df=features[i]
- local tag=strip(lower(df.tag))
- local ft=f[tag]
- if not ft then
- ft={}
- f[tag]=ft
- o[#o+1]=tag
- end
- local dscripts=df.scripts
- for i=1,#dscripts do
- local d=dscripts[i]
- local languages=d.langs
- local script=strip(lower(d.script))
- local fts=ft[script] if not fts then fts={} ft[script]=fts end
- for i=1,#languages do
- fts[strip(lower(languages[i]))]=true
- end
- end
- end
- sequences[#sequences+1]={
- type=typ,
- chain=chain,
- flags=flags,
- name=name,
- subtables=subtables,
- markclass=markclass,
- features=f,
- order=o,
- }
- else
- lookups[name]={
- type=typ,
- chain=chain,
- flags=flags,
- subtables=subtables,
- markclass=markclass,
- }
- end
- end
- end
- end
-end
-actions["prepare lookups"]=function(data,filename,raw)
- local lookups=raw.lookups
- if lookups then
- data.lookups=lookups
- end
-end
-local function t_uncover(splitter,cache,covers)
- local result={}
- for n=1,#covers do
- local cover=covers[n]
- local uncovered=cache[cover]
- if not uncovered then
- uncovered=lpegmatch(splitter,cover)
- cache[cover]=uncovered
- end
- result[n]=uncovered
- end
- return result
-end
-local function s_uncover(splitter,cache,cover)
- if cover=="" then
- return nil
- else
- local uncovered=cache[cover]
- if not uncovered then
- uncovered=lpegmatch(splitter,cover)
- cache[cover]=uncovered
- end
- return { uncovered }
- end
-end
-local function t_hashed(t,cache)
- if t then
- local ht={}
- for i=1,#t do
- local ti=t[i]
- local tih=cache[ti]
- if not tih then
- tih={}
- for i=1,#ti do
- tih[ti[i]]=true
- end
- cache[ti]=tih
- end
- ht[i]=tih
- end
- return ht
- else
- return nil
- end
-end
-local function s_hashed(t,cache)
- if t then
- local ht={}
- local tf=t[1]
- for i=1,#tf do
- ht[i]={ [tf[i]]=true }
- end
- return ht
- else
- return nil
- end
-end
-local function r_uncover(splitter,cache,cover,replacements)
- if cover=="" then
- return nil
- else
- local uncovered=cover[1]
- local replaced=cache[replacements]
- if not replaced then
- replaced=lpegmatch(splitter,replacements)
- cache[replacements]=replaced
- end
- local nu,nr=#uncovered,#replaced
- local r={}
- if nu==nr then
- for i=1,nu do
- r[uncovered[i]]=replaced[i]
- end
- end
- return r
- end
-end
-actions["reorganize lookups"]=function(data,filename,raw)
- if data.lookups then
- local splitter=data.helpers.tounicodetable
- local t_u_cache={}
- local s_u_cache=t_u_cache
- local t_h_cache={}
- local s_h_cache=t_h_cache
- local r_u_cache={}
- for _,lookup in next,data.lookups do
- local rules=lookup.rules
- if rules then
- local format=lookup.format
- if format=="class" then
- local before_class=lookup.before_class
- if before_class then
- before_class=t_uncover(splitter,t_u_cache,reversed(before_class))
- end
- local current_class=lookup.current_class
- if current_class then
- current_class=t_uncover(splitter,t_u_cache,current_class)
- end
- local after_class=lookup.after_class
- if after_class then
- after_class=t_uncover(splitter,t_u_cache,after_class)
- end
- for i=1,#rules do
- local rule=rules[i]
- local class=rule.class
- local before=class.before
- if before then
- for i=1,#before do
- before[i]=before_class[before[i]] or {}
- end
- rule.before=t_hashed(before,t_h_cache)
- end
- local current=class.current
- local lookups=rule.lookups
- if current then
- for i=1,#current do
- current[i]=current_class[current[i]] or {}
- if lookups and not lookups[i] then
- lookups[i]=""
- end
- end
- rule.current=t_hashed(current,t_h_cache)
- end
- local after=class.after
- if after then
- for i=1,#after do
- after[i]=after_class[after[i]] or {}
- end
- rule.after=t_hashed(after,t_h_cache)
- end
- rule.class=nil
- end
- lookup.before_class=nil
- lookup.current_class=nil
- lookup.after_class=nil
- lookup.format="coverage"
- elseif format=="coverage" then
- for i=1,#rules do
- local rule=rules[i]
- local coverage=rule.coverage
- if coverage then
- local before=coverage.before
- if before then
- before=t_uncover(splitter,t_u_cache,reversed(before))
- rule.before=t_hashed(before,t_h_cache)
- end
- local current=coverage.current
- if current then
- current=t_uncover(splitter,t_u_cache,current)
- local lookups=rule.lookups
- if lookups then
- for i=1,#current do
- if not lookups[i] then
- lookups[i]=""
- end
- end
- end
- rule.current=t_hashed(current,t_h_cache)
- end
- local after=coverage.after
- if after then
- after=t_uncover(splitter,t_u_cache,after)
- rule.after=t_hashed(after,t_h_cache)
- end
- rule.coverage=nil
- end
- end
- elseif format=="reversecoverage" then
- for i=1,#rules do
- local rule=rules[i]
- local reversecoverage=rule.reversecoverage
- if reversecoverage then
- local before=reversecoverage.before
- if before then
- before=t_uncover(splitter,t_u_cache,reversed(before))
- rule.before=t_hashed(before,t_h_cache)
- end
- local current=reversecoverage.current
- if current then
- current=t_uncover(splitter,t_u_cache,current)
- rule.current=t_hashed(current,t_h_cache)
- end
- local after=reversecoverage.after
- if after then
- after=t_uncover(splitter,t_u_cache,after)
- rule.after=t_hashed(after,t_h_cache)
- end
- local replacements=reversecoverage.replacements
- if replacements then
- rule.replacements=r_uncover(splitter,r_u_cache,current,replacements)
- end
- rule.reversecoverage=nil
- end
- end
- elseif format=="glyphs" then
- for i=1,#rules do
- local rule=rules[i]
- local glyphs=rule.glyphs
- if glyphs then
- local fore=glyphs.fore
- if fore and fore~="" then
- fore=s_uncover(splitter,s_u_cache,fore)
- rule.after=s_hashed(fore,s_h_cache)
- end
- local back=glyphs.back
- if back then
- back=s_uncover(splitter,s_u_cache,back)
- rule.before=s_hashed(back,s_h_cache)
- end
- local names=glyphs.names
- if names then
- names=s_uncover(splitter,s_u_cache,names)
- rule.current=s_hashed(names,s_h_cache)
- end
- rule.glyphs=nil
- local lookups=rule.lookups
- if lookups then
- for i=1,#names do
- if not lookups[i] then
- lookups[i]=""
- end
- end
- end
- end
- end
- end
- end
- end
- end
-end
-local function check_variants(unicode,the_variants,splitter,unicodes)
- local variants=the_variants.variants
- if variants then
- local glyphs=lpegmatch(splitter,variants)
- local done={ [unicode]=true }
- local n=0
- for i=1,#glyphs do
- local g=glyphs[i]
- if done[g] then
- if i>1 then
- report_otf("skipping cyclic reference %U in math variant %U",g,unicode)
- end
- else
- if n==0 then
- n=1
- variants={ g }
- else
- n=n+1
- variants[n]=g
- end
- done[g]=true
- end
- end
- if n==0 then
- variants=nil
- end
- end
- local parts=the_variants.parts
- if parts then
- local p=#parts
- if p>0 then
- for i=1,p do
- local pi=parts[i]
- pi.glyph=unicodes[pi.component] or 0
- pi.component=nil
- end
- else
- parts=nil
- end
- end
- local italic_correction=the_variants.italic_correction
- if italic_correction and italic_correction==0 then
- italic_correction=nil
- end
- return variants,parts,italic_correction
-end
-actions["analyze math"]=function(data,filename,raw)
- if raw.math then
- data.metadata.math=raw.math
- local unicodes=data.resources.unicodes
- local splitter=data.helpers.tounicodetable
- for unicode,description in next,data.descriptions do
- local glyph=description.glyph
- local mathkerns=glyph.mathkern
- local horiz_variants=glyph.horiz_variants
- local vert_variants=glyph.vert_variants
- local top_accent=glyph.top_accent
- if mathkerns or horiz_variants or vert_variants or top_accent then
- local math={}
- if top_accent then
- math.top_accent=top_accent
- end
- if mathkerns then
- for k,v in next,mathkerns do
- if not next(v) then
- mathkerns[k]=nil
- else
- for k,v in next,v do
- if v==0 then
- k[v]=nil
- end
- end
- end
- end
- math.kerns=mathkerns
- end
- if horiz_variants then
- math.horiz_variants,math.horiz_parts,math.horiz_italic_correction=check_variants(unicode,horiz_variants,splitter,unicodes)
- end
- if vert_variants then
- math.vert_variants,math.vert_parts,math.vert_italic_correction=check_variants(unicode,vert_variants,splitter,unicodes)
- end
- local italic_correction=description.italic
- if italic_correction and italic_correction~=0 then
- math.italic_correction=italic_correction
- end
- description.math=math
- end
- end
- end
-end
-actions["reorganize glyph kerns"]=function(data,filename,raw)
- local descriptions=data.descriptions
- local resources=data.resources
- local unicodes=resources.unicodes
- for unicode,description in next,descriptions do
- local kerns=description.glyph.kerns
- if kerns then
- local newkerns={}
- for k,kern in next,kerns do
- local name=kern.char
- local offset=kern.off
- local lookup=kern.lookup
- if name and offset and lookup then
- local unicode=unicodes[name]
- if unicode then
- if type(lookup)=="table" then
- for l=1,#lookup do
- local lookup=lookup[l]
- local lookupkerns=newkerns[lookup]
- if lookupkerns then
- lookupkerns[unicode]=offset
- else
- newkerns[lookup]={ [unicode]=offset }
- end
- end
- else
- local lookupkerns=newkerns[lookup]
- if lookupkerns then
- lookupkerns[unicode]=offset
- else
- newkerns[lookup]={ [unicode]=offset }
- end
- end
- elseif trace_loading then
- report_otf("problems with unicode %a of kern %a of glyph %U",name,k,unicode)
- end
- end
- end
- description.kerns=newkerns
- end
- end
-end
-actions["merge kern classes"]=function(data,filename,raw)
- local gposlist=raw.gpos
- if gposlist then
- local descriptions=data.descriptions
- local resources=data.resources
- local unicodes=resources.unicodes
- local splitter=data.helpers.tounicodetable
- local ignored=0
- local blocked=0
- for gp=1,#gposlist do
- local gpos=gposlist[gp]
- local subtables=gpos.subtables
- if subtables then
- local first_done={}
- local split={}
- for s=1,#subtables do
- local subtable=subtables[s]
- local kernclass=subtable.kernclass
- local lookup=subtable.lookup or subtable.name
- if kernclass then
- if #kernclass>0 then
- kernclass=kernclass[1]
- lookup=type(kernclass.lookup)=="string" and kernclass.lookup or lookup
- report_otf("fixing kernclass table of lookup %a",lookup)
- end
- local firsts=kernclass.firsts
- local seconds=kernclass.seconds
- local offsets=kernclass.offsets
- for n,s in next,firsts do
- split[s]=split[s] or lpegmatch(splitter,s)
- end
- local maxseconds=0
- for n,s in next,seconds do
- if n>maxseconds then
- maxseconds=n
- end
- split[s]=split[s] or lpegmatch(splitter,s)
- end
- for fk=1,#firsts do
- local fv=firsts[fk]
- local splt=split[fv]
- if splt then
- local extrakerns={}
- local baseoffset=(fk-1)*maxseconds
- for sk=2,maxseconds do
- local sv=seconds[sk]
- local splt=split[sv]
- if splt then
- local offset=offsets[baseoffset+sk]
- if offset then
- for i=1,#splt do
- extrakerns[splt[i]]=offset
- end
- end
- end
- end
- for i=1,#splt do
- local first_unicode=splt[i]
- if first_done[first_unicode] then
- report_otf("lookup %a: ignoring further kerns of %C",lookup,first_unicode)
- blocked=blocked+1
- else
- first_done[first_unicode]=true
- local description=descriptions[first_unicode]
- if description then
- local kerns=description.kerns
- if not kerns then
- kerns={}
- description.kerns=kerns
- end
- local lookupkerns=kerns[lookup]
- if not lookupkerns then
- lookupkerns={}
- kerns[lookup]=lookupkerns
- end
- if overloadkerns then
- for second_unicode,kern in next,extrakerns do
- lookupkerns[second_unicode]=kern
- end
- else
- for second_unicode,kern in next,extrakerns do
- local k=lookupkerns[second_unicode]
- if not k then
- lookupkerns[second_unicode]=kern
- elseif k~=kern then
- if trace_loading then
- report_otf("lookup %a: ignoring overload of kern between %C and %C, rejecting %a, keeping %a",lookup,first_unicode,second_unicode,k,kern)
- end
- ignored=ignored+1
- end
- end
- end
- elseif trace_loading then
- report_otf("no glyph data for %U",first_unicode)
- end
- end
- end
- end
- end
- subtable.kernclass={}
- end
- end
- end
- end
- if ignored>0 then
- report_otf("%s kern overloads ignored",ignored)
- end
- if blocked>0 then
- report_otf("%s succesive kerns blocked",blocked)
- end
- end
-end
-actions["check glyphs"]=function(data,filename,raw)
- for unicode,description in next,data.descriptions do
- description.glyph=nil
- end
-end
-actions["check metadata"]=function(data,filename,raw)
- local metadata=data.metadata
- for _,k in next,mainfields do
- if valid_fields[k] then
- local v=raw[k]
- if not metadata[k] then
- metadata[k]=v
- end
- end
- end
- local ttftables=metadata.ttf_tables
- if ttftables then
- for i=1,#ttftables do
- ttftables[i].data="deleted"
- end
- end
- if metadata.validation_state and table.contains(metadata.validation_state,"bad_ps_fontname") then
- local name=file.nameonly(filename)
- metadata.fontname="bad-fontname-"..name
- metadata.fullname="bad-fullname-"..name
- end
-end
-actions["cleanup tables"]=function(data,filename,raw)
- data.resources.indices=nil
- data.helpers=nil
-end
-actions["reorganize glyph lookups"]=function(data,filename,raw)
- local resources=data.resources
- local unicodes=resources.unicodes
- local descriptions=data.descriptions
- local splitter=data.helpers.tounicodelist
- local lookuptypes=resources.lookuptypes
- for unicode,description in next,descriptions do
- local lookups=description.glyph.lookups
- if lookups then
- for tag,lookuplist in next,lookups do
- for l=1,#lookuplist do
- local lookup=lookuplist[l]
- local specification=lookup.specification
- local lookuptype=lookup.type
- local lt=lookuptypes[tag]
- if not lt then
- lookuptypes[tag]=lookuptype
- elseif lt~=lookuptype then
- report_otf("conflicting lookuptypes, %a points to %a and %a",tag,lt,lookuptype)
- end
- if lookuptype=="ligature" then
- lookuplist[l]={ lpegmatch(splitter,specification.components) }
- elseif lookuptype=="alternate" then
- lookuplist[l]={ lpegmatch(splitter,specification.components) }
- elseif lookuptype=="substitution" then
- lookuplist[l]=unicodes[specification.variant]
- elseif lookuptype=="multiple" then
- lookuplist[l]={ lpegmatch(splitter,specification.components) }
- elseif lookuptype=="position" then
- lookuplist[l]={
- specification.x or 0,
- specification.y or 0,
- specification.h or 0,
- specification.v or 0
- }
- elseif lookuptype=="pair" then
- local one=specification.offsets[1]
- local two=specification.offsets[2]
- local paired=unicodes[specification.paired]
- if one then
- if two then
- lookuplist[l]={ paired,{ one.x or 0,one.y or 0,one.h or 0,one.v or 0 },{ two.x or 0,two.y or 0,two.h or 0,two.v or 0 } }
- else
- lookuplist[l]={ paired,{ one.x or 0,one.y or 0,one.h or 0,one.v or 0 } }
- end
- else
- if two then
- lookuplist[l]={ paired,{},{ two.x or 0,two.y or 0,two.h or 0,two.v or 0} }
- else
- lookuplist[l]={ paired }
- end
- end
- end
- end
- end
- local slookups,mlookups
- for tag,lookuplist in next,lookups do
- if #lookuplist==1 then
- if slookups then
- slookups[tag]=lookuplist[1]
- else
- slookups={ [tag]=lookuplist[1] }
- end
- else
- if mlookups then
- mlookups[tag]=lookuplist
- else
- mlookups={ [tag]=lookuplist }
- end
- end
- end
- if slookups then
- description.slookups=slookups
- end
- if mlookups then
- description.mlookups=mlookups
- end
- end
- end
-end
-actions["reorganize glyph anchors"]=function(data,filename,raw)
- local descriptions=data.descriptions
- for unicode,description in next,descriptions do
- local anchors=description.glyph.anchors
- if anchors then
- for class,data in next,anchors do
- if class=="baselig" then
- for tag,specification in next,data do
- for i=1,#specification do
- local si=specification[i]
- specification[i]={ si and si.x or 0,si and si.y or 0 }
- end
- end
- else
- for tag,specification in next,data do
- data[tag]={ specification.x or 0,specification.y or 0 }
- end
- end
- end
- description.anchors=anchors
- end
- end
-end
-function otf.setfeatures(tfmdata,features)
- local okay=constructors.initializefeatures("otf",tfmdata,features,trace_features,report_otf)
- if okay then
- return constructors.collectprocessors("otf",tfmdata,features,trace_features,report_otf)
- else
- return {}
- end
-end
-local function copytotfm(data,cache_id)
- if data then
- local metadata=data.metadata
- local resources=data.resources
- local properties=derivetable(data.properties)
- local descriptions=derivetable(data.descriptions)
- local goodies=derivetable(data.goodies)
- local characters={}
- local parameters={}
- local mathparameters={}
- local pfminfo=metadata.pfminfo or {}
- local resources=data.resources
- local unicodes=resources.unicodes
- local spaceunits=500
- local spacer="space"
- local designsize=metadata.designsize or metadata.design_size or 100
- local mathspecs=metadata.math
- if designsize==0 then
- designsize=100
- end
- if mathspecs then
- for name,value in next,mathspecs do
- mathparameters[name]=value
- end
- end
- for unicode,_ in next,data.descriptions do
- characters[unicode]={}
- end
- if mathspecs then
- for unicode,character in next,characters do
- local d=descriptions[unicode]
- local m=d.math
- if m then
- local variants=m.horiz_variants
- local parts=m.horiz_parts
- if variants then
- local c=character
- for i=1,#variants do
- local un=variants[i]
- c.next=un
- c=characters[un]
- end
- c.horiz_variants=parts
- elseif parts then
- character.horiz_variants=parts
- end
- local variants=m.vert_variants
- local parts=m.vert_parts
- if variants then
- local c=character
- for i=1,#variants do
- local un=variants[i]
- c.next=un
- c=characters[un]
- end
- c.vert_variants=parts
- elseif parts then
- character.vert_variants=parts
- end
- local italic_correction=m.vert_italic_correction
- if italic_correction then
- character.vert_italic_correction=italic_correction
- end
- local top_accent=m.top_accent
- if top_accent then
- character.top_accent=top_accent
- end
- local kerns=m.kerns
- if kerns then
- character.mathkerns=kerns
- end
- end
- end
- end
- local filename=constructors.checkedfilename(resources)
- local fontname=metadata.fontname
- local fullname=metadata.fullname or fontname
- local units=metadata.units_per_em or 1000
- if units==0 then
- units=1000
- metadata.units_per_em=1000
- report_otf("changing %a units to %a",0,units)
- end
- local monospaced=metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion=="Monospaced")
- local charwidth=pfminfo.avgwidth
- local charxheight=pfminfo.os2_xheight and pfminfo.os2_xheight>0 and pfminfo.os2_xheight
- local italicangle=metadata.italicangle
- properties.monospaced=monospaced
- parameters.italicangle=italicangle
- parameters.charwidth=charwidth
- parameters.charxheight=charxheight
- local space=0x0020
- local emdash=0x2014
- if monospaced then
- if descriptions[space] then
- spaceunits,spacer=descriptions[space].width,"space"
- end
- if not spaceunits and descriptions[emdash] then
- spaceunits,spacer=descriptions[emdash].width,"emdash"
- end
- if not spaceunits and charwidth then
- spaceunits,spacer=charwidth,"charwidth"
- end
- else
- if descriptions[space] then
- spaceunits,spacer=descriptions[space].width,"space"
- end
- if not spaceunits and descriptions[emdash] then
- spaceunits,spacer=descriptions[emdash].width/2,"emdash/2"
- end
- if not spaceunits and charwidth then
- spaceunits,spacer=charwidth,"charwidth"
- end
- end
- spaceunits=tonumber(spaceunits) or 500
- parameters.slant=0
- parameters.space=spaceunits
- parameters.space_stretch=units/2
- parameters.space_shrink=1*units/3
- parameters.x_height=2*units/5
- parameters.quad=units
- if spaceunits<2*units/5 then
- end
- if italicangle and italicangle~=0 then
- parameters.italicangle=italicangle
- parameters.italicfactor=math.cos(math.rad(90+italicangle))
- parameters.slant=- math.tan(italicangle*math.pi/180)
- end
- if monospaced then
- parameters.space_stretch=0
- parameters.space_shrink=0
- elseif syncspace then
- parameters.space_stretch=spaceunits/2
- parameters.space_shrink=spaceunits/3
- end
- parameters.extra_space=parameters.space_shrink
- if charxheight then
- parameters.x_height=charxheight
- else
- local x=0x78
- if x then
- local x=descriptions[x]
- if x then
- parameters.x_height=x.height
- end
- end
- end
- parameters.designsize=(designsize/10)*65536
- parameters.ascender=abs(metadata.ascent or 0)
- parameters.descender=abs(metadata.descent or 0)
- parameters.units=units
- properties.space=spacer
- properties.encodingbytes=2
- properties.format=data.format or otf_format(filename) or formats.otf
- properties.noglyphnames=true
- properties.filename=filename
- properties.fontname=fontname
- properties.fullname=fullname
- properties.psname=fontname or fullname
- properties.name=filename or fullname
- return {
- characters=characters,
- descriptions=descriptions,
- parameters=parameters,
- mathparameters=mathparameters,
- resources=resources,
- properties=properties,
- goodies=goodies,
- }
- end
-end
-local function otftotfm(specification)
- local cache_id=specification.hash
- local tfmdata=containers.read(constructors.cache,cache_id)
- if not tfmdata then
- local name=specification.name
- local sub=specification.sub
- local filename=specification.filename
- local features=specification.features.normal
- local rawdata=otf.load(filename,sub,features and features.featurefile)
- if rawdata and next(rawdata) then
- local descriptions=rawdata.descriptions
- local duplicates=rawdata.resources.duplicates
- if duplicates then
- local nofduplicates,nofduplicated=0,0
- for parent,list in next,duplicates do
- for i=1,#list do
- local unicode=list[i]
- if not descriptions[unicode] then
- descriptions[unicode]=descriptions[parent]
- nofduplicated=nofduplicated+1
- end
- end
- nofduplicates=nofduplicates+#list
- end
- if trace_otf and nofduplicated~=nofduplicates then
- report_otf("%i extra duplicates copied out of %i",nofduplicated,nofduplicates)
- end
- end
- rawdata.lookuphash={}
- tfmdata=copytotfm(rawdata,cache_id)
- if tfmdata and next(tfmdata) then
- local features=constructors.checkedfeatures("otf",features)
- local shared=tfmdata.shared
- if not shared then
- shared={}
- tfmdata.shared=shared
- end
- shared.rawdata=rawdata
- shared.dynamics={}
- tfmdata.changed={}
- shared.features=features
- shared.processes=otf.setfeatures(tfmdata,features)
- end
- end
- containers.write(constructors.cache,cache_id,tfmdata)
- end
- return tfmdata
-end
-local function read_from_otf(specification)
- local tfmdata=otftotfm(specification)
- if tfmdata then
- tfmdata.properties.name=specification.name
- tfmdata.properties.sub=specification.sub
- tfmdata=constructors.scale(tfmdata,specification)
- local allfeatures=tfmdata.shared.features or specification.features.normal
- constructors.applymanipulators("otf",tfmdata,allfeatures,trace_features,report_otf)
- constructors.setname(tfmdata,specification)
- fonts.loggers.register(tfmdata,file.suffix(specification.filename),specification)
- end
- return tfmdata
-end
-local function checkmathsize(tfmdata,mathsize)
- local mathdata=tfmdata.shared.rawdata.metadata.math
- local mathsize=tonumber(mathsize)
- if mathdata then
- local parameters=tfmdata.parameters
- parameters.scriptpercentage=mathdata.ScriptPercentScaleDown
- parameters.scriptscriptpercentage=mathdata.ScriptScriptPercentScaleDown
- parameters.mathsize=mathsize
- end
-end
-registerotffeature {
- name="mathsize",
- description="apply mathsize specified in the font",
- initializers={
- base=checkmathsize,
- node=checkmathsize,
- }
-}
-function otf.collectlookups(rawdata,kind,script,language)
- local sequences=rawdata.resources.sequences
- if sequences then
- local featuremap,featurelist={},{}
- for s=1,#sequences do
- local sequence=sequences[s]
- local features=sequence.features
- features=features and features[kind]
- features=features and (features[script] or features[default] or features[wildcard])
- features=features and (features[language] or features[default] or features[wildcard])
- if features then
- local subtables=sequence.subtables
- if subtables then
- for s=1,#subtables do
- local ss=subtables[s]
- if not featuremap[s] then
- featuremap[ss]=true
- featurelist[#featurelist+1]=ss
- end
- end
- end
- end
- end
- if #featurelist>0 then
- return featuremap,featurelist
- end
- end
- return nil,nil
-end
-local function check_otf(forced,specification,suffix)
- local name=specification.name
- if forced then
- name=specification.forcedname
- end
- local fullname=findbinfile(name,suffix) or ""
- if fullname=="" then
- fullname=fonts.names.getfilename(name,suffix) or ""
- end
- if fullname~="" and not fonts.names.ignoredfile(fullname) then
- specification.filename=fullname
- return read_from_otf(specification)
- end
-end
-local function opentypereader(specification,suffix)
- local forced=specification.forced or ""
- if formats[forced] then
- return check_otf(true,specification,forced)
- else
- return check_otf(false,specification,suffix)
- end
-end
-readers.opentype=opentypereader
-function readers.otf (specification) return opentypereader(specification,"otf") end
-function readers.ttf (specification) return opentypereader(specification,"ttf") end
-function readers.ttc (specification) return opentypereader(specification,"ttf") end
-function readers.dfont(specification) return opentypereader(specification,"ttf") end
-function otf.scriptandlanguage(tfmdata,attr)
- local properties=tfmdata.properties
- return properties.script or "dflt",properties.language or "dflt"
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-otb']={
- version=1.001,
- comment="companion to font-ini.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local concat=table.concat
-local format,gmatch,gsub,find,match,lower,strip=string.format,string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip
-local type,next,tonumber,tostring=type,next,tonumber,tostring
-local lpegmatch=lpeg.match
-local utfchar=utf.char
-local trace_baseinit=false trackers.register("otf.baseinit",function(v) trace_baseinit=v end)
-local trace_singles=false trackers.register("otf.singles",function(v) trace_singles=v end)
-local trace_multiples=false trackers.register("otf.multiples",function(v) trace_multiples=v end)
-local trace_alternatives=false trackers.register("otf.alternatives",function(v) trace_alternatives=v end)
-local trace_ligatures=false trackers.register("otf.ligatures",function(v) trace_ligatures=v end)
-local trace_ligatures_detail=false trackers.register("otf.ligatures.detail",function(v) trace_ligatures_detail=v end)
-local trace_kerns=false trackers.register("otf.kerns",function(v) trace_kerns=v end)
-local trace_preparing=false trackers.register("otf.preparing",function(v) trace_preparing=v end)
-local report_prepare=logs.reporter("fonts","otf prepare")
-local fonts=fonts
-local otf=fonts.handlers.otf
-local otffeatures=otf.features
-local registerotffeature=otffeatures.register
-otf.defaultbasealternate="none"
-local wildcard="*"
-local default="dflt"
-local formatters=string.formatters
-local f_unicode=formatters["%U"]
-local f_uniname=formatters["%U (%s)"]
-local f_unilist=formatters["% t (% t)"]
-local function gref(descriptions,n)
- if type(n)=="number" then
- local name=descriptions[n].name
- if name then
- return f_uniname(n,name)
- else
- return f_unicode(n)
- end
- elseif n then
- local num,nam,j={},{},0
- for i=1,#n do
- local ni=n[i]
- if tonumber(ni) then
- j=j+1
- local di=descriptions[ni]
- num[j]=f_unicode(ni)
- nam[j]=di and di.name or "-"
- end
- end
- return f_unilist(num,nam)
- else
- return "<error in base mode tracing>"
- end
-end
-local function cref(feature,lookupname)
- if lookupname then
- return formatters["feature %a, lookup %a"](feature,lookupname)
- else
- return formatters["feature %a"](feature)
- end
-end
-local function report_alternate(feature,lookupname,descriptions,unicode,replacement,value,comment)
- report_prepare("%s: base alternate %s => %s (%S => %S)",
- cref(feature,lookupname),
- gref(descriptions,unicode),
- replacement and gref(descriptions,replacement),
- value,
- comment)
-end
-local function report_substitution(feature,lookupname,descriptions,unicode,substitution)
- report_prepare("%s: base substitution %s => %S",
- cref(feature,lookupname),
- gref(descriptions,unicode),
- gref(descriptions,substitution))
-end
-local function report_ligature(feature,lookupname,descriptions,unicode,ligature)
- report_prepare("%s: base ligature %s => %S",
- cref(feature,lookupname),
- gref(descriptions,ligature),
- gref(descriptions,unicode))
-end
-local function report_kern(feature,lookupname,descriptions,unicode,otherunicode,value)
- report_prepare("%s: base kern %s + %s => %S",
- cref(feature,lookupname),
- gref(descriptions,unicode),
- gref(descriptions,otherunicode),
- value)
-end
-local basemethods={}
-local basemethod="<unset>"
-local function applybasemethod(what,...)
- local m=basemethods[basemethod][what]
- if m then
- return m(...)
- end
-end
-local basehash,basehashes,applied={},1,{}
-local function registerbasehash(tfmdata)
- local properties=tfmdata.properties
- local hash=concat(applied," ")
- local base=basehash[hash]
- if not base then
- basehashes=basehashes+1
- base=basehashes
- basehash[hash]=base
- end
- properties.basehash=base
- properties.fullname=properties.fullname.."-"..base
- applied={}
-end
-local function registerbasefeature(feature,value)
- applied[#applied+1]=feature.."="..tostring(value)
-end
-local trace=false
-local function finalize_ligatures(tfmdata,ligatures)
- local nofligatures=#ligatures
- if nofligatures>0 then
- local characters=tfmdata.characters
- local descriptions=tfmdata.descriptions
- local resources=tfmdata.resources
- local unicodes=resources.unicodes
- local private=resources.private
- local alldone=false
- while not alldone do
- local done=0
- for i=1,nofligatures do
- local ligature=ligatures[i]
- if ligature then
- local unicode,lookupdata=ligature[1],ligature[2]
- if trace_ligatures_detail then
- report_prepare("building % a into %a",lookupdata,unicode)
- end
- local size=#lookupdata
- local firstcode=lookupdata[1]
- local firstdata=characters[firstcode]
- local okay=false
- if firstdata then
- local firstname="ctx_"..firstcode
- for i=1,size-1 do
- local firstdata=characters[firstcode]
- if not firstdata then
- firstcode=private
- if trace_ligatures_detail then
- report_prepare("defining %a as %a",firstname,firstcode)
- end
- unicodes[firstname]=firstcode
- firstdata={ intermediate=true,ligatures={} }
- characters[firstcode]=firstdata
- descriptions[firstcode]={ name=firstname }
- private=private+1
- end
- local target
- local secondcode=lookupdata[i+1]
- local secondname=firstname.."_"..secondcode
- if i==size-1 then
- target=unicode
- if not unicodes[secondname] then
- unicodes[secondname]=unicode
- end
- okay=true
- else
- target=unicodes[secondname]
- if not target then
- break
- end
- end
- if trace_ligatures_detail then
- report_prepare("codes (%a,%a) + (%a,%a) -> %a",firstname,firstcode,secondname,secondcode,target)
- end
- local firstligs=firstdata.ligatures
- if firstligs then
- firstligs[secondcode]={ char=target }
- else
- firstdata.ligatures={ [secondcode]={ char=target } }
- end
- firstcode=target
- firstname=secondname
- end
- elseif trace_ligatures_detail then
- report_prepare("no glyph (%a,%a) for building %a",firstname,firstcode,target)
- end
- if okay then
- ligatures[i]=false
- done=done+1
- end
- end
- end
- alldone=done==0
- end
- if trace_ligatures_detail then
- for k,v in table.sortedhash(characters) do
- if v.ligatures then
- table.print(v,k)
- end
- end
- end
- resources.private=private
- end
-end
-local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist)
- local characters=tfmdata.characters
- local descriptions=tfmdata.descriptions
- local resources=tfmdata.resources
- local changed=tfmdata.changed
- local unicodes=resources.unicodes
- local lookuphash=resources.lookuphash
- local lookuptypes=resources.lookuptypes
- local ligatures={}
- local alternate=tonumber(value) or true and 1
- local defaultalt=otf.defaultbasealternate
- local trace_singles=trace_baseinit and trace_singles
- local trace_alternatives=trace_baseinit and trace_alternatives
- local trace_ligatures=trace_baseinit and trace_ligatures
- local actions={
- substitution=function(lookupdata,lookupname,description,unicode)
- if trace_singles then
- report_substitution(feature,lookupname,descriptions,unicode,lookupdata)
- end
- changed[unicode]=lookupdata
- end,
- alternate=function(lookupdata,lookupname,description,unicode)
- local replacement=lookupdata[alternate]
- if replacement then
- changed[unicode]=replacement
- if trace_alternatives then
- report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"normal")
- end
- elseif defaultalt=="first" then
- replacement=lookupdata[1]
- changed[unicode]=replacement
- if trace_alternatives then
- report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt)
- end
- elseif defaultalt=="last" then
- replacement=lookupdata[#data]
- if trace_alternatives then
- report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt)
- end
- else
- if trace_alternatives then
- report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"unknown")
- end
- end
- end,
- ligature=function(lookupdata,lookupname,description,unicode)
- if trace_ligatures then
- report_ligature(feature,lookupname,descriptions,unicode,lookupdata)
- end
- ligatures[#ligatures+1]={ unicode,lookupdata }
- end,
- }
- for unicode,character in next,characters do
- local description=descriptions[unicode]
- local lookups=description.slookups
- if lookups then
- for l=1,#lookuplist do
- local lookupname=lookuplist[l]
- local lookupdata=lookups[lookupname]
- if lookupdata then
- local lookuptype=lookuptypes[lookupname]
- local action=actions[lookuptype]
- if action then
- action(lookupdata,lookupname,description,unicode)
- end
- end
- end
- end
- local lookups=description.mlookups
- if lookups then
- for l=1,#lookuplist do
- local lookupname=lookuplist[l]
- local lookuplist=lookups[lookupname]
- if lookuplist then
- local lookuptype=lookuptypes[lookupname]
- local action=actions[lookuptype]
- if action then
- for i=1,#lookuplist do
- action(lookuplist[i],lookupname,description,unicode)
- end
- end
- end
- end
- end
- end
- finalize_ligatures(tfmdata,ligatures)
-end
-local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist)
- local characters=tfmdata.characters
- local descriptions=tfmdata.descriptions
- local resources=tfmdata.resources
- local unicodes=resources.unicodes
- local sharedkerns={}
- local traceindeed=trace_baseinit and trace_kerns
- for unicode,character in next,characters do
- local description=descriptions[unicode]
- local rawkerns=description.kerns
- if rawkerns then
- local s=sharedkerns[rawkerns]
- if s==false then
- elseif s then
- character.kerns=s
- else
- local newkerns=character.kerns
- local done=false
- for l=1,#lookuplist do
- local lookup=lookuplist[l]
- local kerns=rawkerns[lookup]
- if kerns then
- for otherunicode,value in next,kerns do
- if value==0 then
- elseif not newkerns then
- newkerns={ [otherunicode]=value }
- done=true
- if traceindeed then
- report_kern(feature,lookup,descriptions,unicode,otherunicode,value)
- end
- elseif not newkerns[otherunicode] then
- newkerns[otherunicode]=value
- done=true
- if traceindeed then
- report_kern(feature,lookup,descriptions,unicode,otherunicode,value)
- end
- end
- end
- end
- end
- if done then
- sharedkerns[rawkerns]=newkerns
- character.kerns=newkerns
- else
- sharedkerns[rawkerns]=false
- end
- end
- end
- end
-end
-basemethods.independent={
- preparesubstitutions=preparesubstitutions,
- preparepositionings=preparepositionings,
-}
-local function makefake(tfmdata,name,present)
- local resources=tfmdata.resources
- local private=resources.private
- local character={ intermediate=true,ligatures={} }
- resources.unicodes[name]=private
- tfmdata.characters[private]=character
- tfmdata.descriptions[private]={ name=name }
- resources.private=private+1
- present[name]=private
- return character
-end
-local function make_1(present,tree,name)
- for k,v in next,tree do
- if k=="ligature" then
- present[name]=v
- else
- make_1(present,v,name.."_"..k)
- end
- end
-end
-local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,done,lookupname)
- for k,v in next,tree do
- if k=="ligature" then
- local character=characters[preceding]
- if not character then
- if trace_baseinit then
- report_prepare("weird ligature in lookup %a, current %C, preceding %C",lookupname,v,preceding)
- end
- character=makefake(tfmdata,name,present)
- end
- local ligatures=character.ligatures
- if ligatures then
- ligatures[unicode]={ char=v }
- else
- character.ligatures={ [unicode]={ char=v } }
- end
- if done then
- local d=done[lookupname]
- if not d then
- done[lookupname]={ "dummy",v }
- else
- d[#d+1]=v
- end
- end
- else
- local code=present[name] or unicode
- local name=name.."_"..k
- make_2(present,tfmdata,characters,v,name,code,k,done,lookupname)
- end
- end
-end
-local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist)
- local characters=tfmdata.characters
- local descriptions=tfmdata.descriptions
- local resources=tfmdata.resources
- local changed=tfmdata.changed
- local lookuphash=resources.lookuphash
- local lookuptypes=resources.lookuptypes
- local ligatures={}
- local alternate=tonumber(value) or true and 1
- local defaultalt=otf.defaultbasealternate
- local trace_singles=trace_baseinit and trace_singles
- local trace_alternatives=trace_baseinit and trace_alternatives
- local trace_ligatures=trace_baseinit and trace_ligatures
- for l=1,#lookuplist do
- local lookupname=lookuplist[l]
- local lookupdata=lookuphash[lookupname]
- local lookuptype=lookuptypes[lookupname]
- for unicode,data in next,lookupdata do
- if lookuptype=="substitution" then
- if trace_singles then
- report_substitution(feature,lookupname,descriptions,unicode,data)
- end
- changed[unicode]=data
- elseif lookuptype=="alternate" then
- local replacement=data[alternate]
- if replacement then
- changed[unicode]=replacement
- if trace_alternatives then
- report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"normal")
- end
- elseif defaultalt=="first" then
- replacement=data[1]
- changed[unicode]=replacement
- if trace_alternatives then
- report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt)
- end
- elseif defaultalt=="last" then
- replacement=data[#data]
- if trace_alternatives then
- report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt)
- end
- else
- if trace_alternatives then
- report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"unknown")
- end
- end
- elseif lookuptype=="ligature" then
- ligatures[#ligatures+1]={ unicode,data,lookupname }
- if trace_ligatures then
- report_ligature(feature,lookupname,descriptions,unicode,data)
- end
- end
- end
- end
- local nofligatures=#ligatures
- if nofligatures>0 then
- local characters=tfmdata.characters
- local present={}
- local done=trace_baseinit and trace_ligatures and {}
- for i=1,nofligatures do
- local ligature=ligatures[i]
- local unicode,tree=ligature[1],ligature[2]
- make_1(present,tree,"ctx_"..unicode)
- end
- for i=1,nofligatures do
- local ligature=ligatures[i]
- local unicode,tree,lookupname=ligature[1],ligature[2],ligature[3]
- make_2(present,tfmdata,characters,tree,"ctx_"..unicode,unicode,unicode,done,lookupname)
- end
- end
-end
-local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist)
- local characters=tfmdata.characters
- local descriptions=tfmdata.descriptions
- local resources=tfmdata.resources
- local lookuphash=resources.lookuphash
- local traceindeed=trace_baseinit and trace_kerns
- for l=1,#lookuplist do
- local lookupname=lookuplist[l]
- local lookupdata=lookuphash[lookupname]
- for unicode,data in next,lookupdata do
- local character=characters[unicode]
- local kerns=character.kerns
- if not kerns then
- kerns={}
- character.kerns=kerns
- end
- if traceindeed then
- for otherunicode,kern in next,data do
- if not kerns[otherunicode] and kern~=0 then
- kerns[otherunicode]=kern
- report_kern(feature,lookup,descriptions,unicode,otherunicode,kern)
- end
- end
- else
- for otherunicode,kern in next,data do
- if not kerns[otherunicode] and kern~=0 then
- kerns[otherunicode]=kern
- end
- end
- end
- end
- end
-end
-local function initializehashes(tfmdata)
- nodeinitializers.features(tfmdata)
-end
-basemethods.shared={
- initializehashes=initializehashes,
- preparesubstitutions=preparesubstitutions,
- preparepositionings=preparepositionings,
-}
-basemethod="independent"
-local function featuresinitializer(tfmdata,value)
- if true then
- local starttime=trace_preparing and os.clock()
- local features=tfmdata.shared.features
- local fullname=tfmdata.properties.fullname or "?"
- if features then
- applybasemethod("initializehashes",tfmdata)
- local collectlookups=otf.collectlookups
- local rawdata=tfmdata.shared.rawdata
- local properties=tfmdata.properties
- local script=properties.script
- local language=properties.language
- local basesubstitutions=rawdata.resources.features.gsub
- local basepositionings=rawdata.resources.features.gpos
- if basesubstitutions or basepositionings then
- local sequences=tfmdata.resources.sequences
- for s=1,#sequences do
- local sequence=sequences[s]
- local sfeatures=sequence.features
- if sfeatures then
- local order=sequence.order
- if order then
- for i=1,#order do
- local feature=order[i]
- local value=features[feature]
- if value then
- local validlookups,lookuplist=collectlookups(rawdata,feature,script,language)
- if not validlookups then
- elseif basesubstitutions and basesubstitutions[feature] then
- if trace_preparing then
- report_prepare("filtering base %s feature %a for %a with value %a","sub",feature,fullname,value)
- end
- applybasemethod("preparesubstitutions",tfmdata,feature,value,validlookups,lookuplist)
- registerbasefeature(feature,value)
- elseif basepositionings and basepositionings[feature] then
- if trace_preparing then
- report_prepare("filtering base %a feature %a for %a with value %a","pos",feature,fullname,value)
- end
- applybasemethod("preparepositionings",tfmdata,feature,value,validlookups,lookuplist)
- registerbasefeature(feature,value)
- end
- end
- end
- end
- end
- end
- end
- registerbasehash(tfmdata)
- end
- if trace_preparing then
- report_prepare("preparation time is %0.3f seconds for %a",os.clock()-starttime,fullname)
- end
- end
-end
-registerotffeature {
- name="features",
- description="features",
- default=true,
- initializers={
- base=featuresinitializer,
- }
-}
-directives.register("fonts.otf.loader.basemethod",function(v)
- if basemethods[v] then
- basemethod=v
- end
-end)
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['node-inj']={
- version=1.001,
- comment="companion to node-ini.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files",
-}
-local next=next
-local utfchar=utf.char
-local trace_injections=false trackers.register("nodes.injections",function(v) trace_injections=v end)
-local report_injections=logs.reporter("nodes","injections")
-local attributes,nodes,node=attributes,nodes,node
-fonts=fonts
-local fontdata=fonts.hashes.identifiers
-nodes.injections=nodes.injections or {}
-local injections=nodes.injections
-local nodecodes=nodes.nodecodes
-local glyph_code=nodecodes.glyph
-local kern_code=nodecodes.kern
-local nodepool=nodes.pool
-local newkern=nodepool.kern
-local traverse_id=node.traverse_id
-local insert_node_before=node.insert_before
-local insert_node_after=node.insert_after
-local a_kernpair=attributes.private('kernpair')
-local a_ligacomp=attributes.private('ligacomp')
-local a_markbase=attributes.private('markbase')
-local a_markmark=attributes.private('markmark')
-local a_markdone=attributes.private('markdone')
-local a_cursbase=attributes.private('cursbase')
-local a_curscurs=attributes.private('curscurs')
-local a_cursdone=attributes.private('cursdone')
-function injections.installnewkern(nk)
- newkern=nk or newkern
-end
-local cursives={}
-local marks={}
-local kerns={}
-function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext)
- local dx,dy=factor*(exit[1]-entry[1]),factor*(exit[2]-entry[2])
- local ws,wn=tfmstart.width,tfmnext.width
- local bound=#cursives+1
- start[a_cursbase]=bound
- nxt[a_curscurs]=bound
- cursives[bound]={ rlmode,dx,dy,ws,wn }
- return dx,dy,bound
-end
-function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr)
- local x,y,w,h=factor*spec[1],factor*spec[2],factor*spec[3],factor*spec[4]
- if x~=0 or w~=0 or y~=0 or h~=0 then
- local bound=current[a_kernpair]
- if bound then
- local kb=kerns[bound]
- kb[2],kb[3],kb[4],kb[5]=(kb[2] or 0)+x,(kb[3] or 0)+y,(kb[4] or 0)+w,(kb[5] or 0)+h
- else
- bound=#kerns+1
- current[a_kernpair]=bound
- kerns[bound]={ rlmode,x,y,w,h,r2lflag,tfmchr.width }
- end
- return x,y,w,h,bound
- end
- return x,y,w,h
-end
-function injections.setkern(current,factor,rlmode,x,tfmchr)
- local dx=factor*x
- if dx~=0 then
- local bound=#kerns+1
- current[a_kernpair]=bound
- kerns[bound]={ rlmode,dx }
- return dx,bound
- else
- return 0,0
- end
-end
-function injections.setmark(start,base,factor,rlmode,ba,ma)
- local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2])
- local bound=base[a_markbase]
- local index=1
- if bound then
- local mb=marks[bound]
- if mb then
- index=#mb+1
- mb[index]={ dx,dy,rlmode }
- start[a_markmark]=bound
- start[a_markdone]=index
- return dx,dy,bound
- else
- report_injections("possible problem, %U is base mark without data (id %a)",base.char,bound)
- end
- end
- index=index or 1
- bound=#marks+1
- base[a_markbase]=bound
- start[a_markmark]=bound
- start[a_markdone]=index
- marks[bound]={ [index]={ dx,dy,rlmode } }
- return dx,dy,bound
-end
-local function dir(n)
- return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset"
-end
-local function trace(head)
- report_injections("begin run")
- for n in traverse_id(glyph_code,head) do
- if n.subtype<256 then
- local kp=n[a_kernpair]
- local mb=n[a_markbase]
- local mm=n[a_markmark]
- local md=n[a_markdone]
- local cb=n[a_cursbase]
- local cc=n[a_curscurs]
- local char=n.char
- report_injections("font %s, char %U, glyph %c",n.font,char,char)
- if kp then
- local k=kerns[kp]
- if k[3] then
- report_injections(" pairkern: dir %a, x %p, y %p, w %p, h %p",dir(k[1]),k[2],k[3],k[4],k[5])
- else
- report_injections(" kern: dir %a, dx %p",dir(k[1]),k[2])
- end
- end
- if mb then
- report_injections(" markbase: bound %a",mb)
- end
- if mm then
- local m=marks[mm]
- if mb then
- local m=m[mb]
- if m then
- report_injections(" markmark: bound %a, index %a, dx %p, dy %p",mm,md,m[1],m[2])
- else
- report_injections(" markmark: bound %a, missing index",mm)
- end
- else
- m=m[1]
- report_injections(" markmark: bound %a, dx %p, dy %p",mm,m and m[1],m and m[2])
- end
- end
- if cb then
- report_injections(" cursbase: bound %a",cb)
- end
- if cc then
- local c=cursives[cc]
- report_injections(" curscurs: bound %a, dir %a, dx %p, dy %p",cc,dir(c[1]),c[2],c[3])
- end
- end
- end
- report_injections("end run")
-end
-local function show_result(head)
- local current=head
- local skipping=false
- while current do
- local id=current.id
- if id==glyph_code then
- report_injections("char: %C, width %p, xoffset %p, yoffset %p",current.char,current.width,current.xoffset,current.yoffset)
- skipping=false
- elseif id==kern_code then
- report_injections("kern: %p",current.kern)
- skipping=false
- elseif not skipping then
- report_injections()
- skipping=true
- end
- current=current.next
- end
-end
-function injections.handler(head,where,keep)
- local has_marks,has_cursives,has_kerns=next(marks),next(cursives),next(kerns)
- if has_marks or has_cursives then
- if trace_injections then
- trace(head)
- end
- local done,ky,rl,valid,cx,wx,mk,nofvalid=false,{},{},{},{},{},{},0
- if has_kerns then
- local nf,tm=nil,nil
- for n in traverse_id(glyph_code,head) do
- if n.subtype<256 then
- nofvalid=nofvalid+1
- valid[nofvalid]=n
- if n.font~=nf then
- nf=n.font
- tm=fontdata[nf].resources.marks
- end
- if tm then
- mk[n]=tm[n.char]
- end
- local k=n[a_kernpair]
- if k then
- local kk=kerns[k]
- if kk then
- local x,y,w,h=kk[2] or 0,kk[3] or 0,kk[4] or 0,kk[5] or 0
- local dy=y-h
- if dy~=0 then
- ky[n]=dy
- end
- if w~=0 or x~=0 then
- wx[n]=kk
- end
- rl[n]=kk[1]
- end
- end
- end
- end
- else
- local nf,tm=nil,nil
- for n in traverse_id(glyph_code,head) do
- if n.subtype<256 then
- nofvalid=nofvalid+1
- valid[nofvalid]=n
- if n.font~=nf then
- nf=n.font
- tm=fontdata[nf].resources.marks
- end
- if tm then
- mk[n]=tm[n.char]
- end
- end
- end
- end
- if nofvalid>0 then
- local cx={}
- if has_kerns and next(ky) then
- for n,k in next,ky do
- n.yoffset=k
- end
- end
- if has_cursives then
- local p_cursbase,p=nil,nil
- local t,d,maxt={},{},0
- for i=1,nofvalid do
- local n=valid[i]
- if not mk[n] then
- local n_cursbase=n[a_cursbase]
- if p_cursbase then
- local n_curscurs=n[a_curscurs]
- if p_cursbase==n_curscurs then
- local c=cursives[n_curscurs]
- if c then
- local rlmode,dx,dy,ws,wn=c[1],c[2],c[3],c[4],c[5]
- if rlmode>=0 then
- dx=dx-ws
- else
- dx=dx+wn
- end
- if dx~=0 then
- cx[n]=dx
- rl[n]=rlmode
- end
- dy=-dy
- maxt=maxt+1
- t[maxt]=p
- d[maxt]=dy
- else
- maxt=0
- end
- end
- elseif maxt>0 then
- local ny=n.yoffset
- for i=maxt,1,-1 do
- ny=ny+d[i]
- local ti=t[i]
- ti.yoffset=ti.yoffset+ny
- end
- maxt=0
- end
- if not n_cursbase and maxt>0 then
- local ny=n.yoffset
- for i=maxt,1,-1 do
- ny=ny+d[i]
- local ti=t[i]
- ti.yoffset=ny
- end
- maxt=0
- end
- p_cursbase,p=n_cursbase,n
- end
- end
- if maxt>0 then
- local ny=n.yoffset
- for i=maxt,1,-1 do
- ny=ny+d[i]
- local ti=t[i]
- ti.yoffset=ny
- end
- maxt=0
- end
- if not keep then
- cursives={}
- end
- end
- if has_marks then
- for i=1,nofvalid do
- local p=valid[i]
- local p_markbase=p[a_markbase]
- if p_markbase then
- local mrks=marks[p_markbase]
- local nofmarks=#mrks
- for n in traverse_id(glyph_code,p.next) do
- local n_markmark=n[a_markmark]
- if p_markbase==n_markmark then
- local index=n[a_markdone] or 1
- local d=mrks[index]
- if d then
- local rlmode=d[3]
- local k=wx[p]
- if k then
- local x=k[2]
- local w=k[4]
- if w then
- if rlmode and rlmode>=0 then
- n.xoffset=p.xoffset-p.width+d[1]-(w-x)
- else
- n.xoffset=p.xoffset-d[1]-x
- end
- else
- if rlmode and rlmode>=0 then
- n.xoffset=p.xoffset-p.width+d[1]
- else
- n.xoffset=p.xoffset-d[1]-x
- end
- end
- else
- if rlmode and rlmode>=0 then
- n.xoffset=p.xoffset-p.width+d[1]
- else
- n.xoffset=p.xoffset-d[1]
- end
- local w=n.width
- if w~=0 then
- insert_node_before(head,n,newkern(-w/2))
- insert_node_after(head,n,newkern(-w/2))
- end
- end
- if mk[p] then
- n.yoffset=p.yoffset+d[2]
- else
- n.yoffset=n.yoffset+p.yoffset+d[2]
- end
- if nofmarks==1 then
- break
- else
- nofmarks=nofmarks-1
- end
- end
- else
- end
- end
- end
- end
- if not keep then
- marks={}
- end
- end
- if next(wx) then
- for n,k in next,wx do
- local x=k[2]
- local w=k[4]
- if w then
- local rl=k[1]
- local wx=w-x
- if rl<0 then
- if wx~=0 then
- insert_node_before(head,n,newkern(wx))
- end
- if x~=0 then
- insert_node_after (head,n,newkern(x))
- end
- else
- if x~=0 then
- insert_node_before(head,n,newkern(x))
- end
- if wx~=0 then
- insert_node_after (head,n,newkern(wx))
- end
- end
- elseif x~=0 then
- insert_node_before(head,n,newkern(x))
- end
- end
- end
- if next(cx) then
- for n,k in next,cx do
- if k~=0 then
- local rln=rl[n]
- if rln and rln<0 then
- insert_node_before(head,n,newkern(-k))
- else
- insert_node_before(head,n,newkern(k))
- end
- end
- end
- end
- if not keep then
- kerns={}
- end
- return head,true
- elseif not keep then
- kerns,cursives,marks={},{},{}
- end
- elseif has_kerns then
- if trace_injections then
- trace(head)
- end
- for n in traverse_id(glyph_code,head) do
- if n.subtype<256 then
- local k=n[a_kernpair]
- if k then
- local kk=kerns[k]
- if kk then
- local rl,x,y,w=kk[1],kk[2] or 0,kk[3],kk[4]
- if y and y~=0 then
- n.yoffset=y
- end
- if w then
- local wx=w-x
- if rl<0 then
- if wx~=0 then
- insert_node_before(head,n,newkern(wx))
- end
- if x~=0 then
- insert_node_after (head,n,newkern(x))
- end
- else
- if x~=0 then
- insert_node_before(head,n,newkern(x))
- end
- if wx~=0 then
- insert_node_after(head,n,newkern(wx))
- end
- end
- else
- if x~=0 then
- insert_node_before(head,n,newkern(x))
- end
- end
- end
- end
- end
- end
- if not keep then
- kerns={}
- end
- return head,true
- else
- end
- return head,false
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-ota']={
- version=1.001,
- comment="companion to font-otf.lua (analysing)",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local type=type
-if not trackers then trackers={ register=function() end } end
-local fonts,nodes,node=fonts,nodes,node
-local allocate=utilities.storage.allocate
-local otf=fonts.handlers.otf
-local analyzers=fonts.analyzers
-local initializers=allocate()
-local methods=allocate()
-analyzers.initializers=initializers
-analyzers.methods=methods
-analyzers.useunicodemarks=false
-local a_state=attributes.private('state')
-local nodecodes=nodes.nodecodes
-local glyph_code=nodecodes.glyph
-local disc_code=nodecodes.disc
-local math_code=nodecodes.math
-local traverse_id=node.traverse_id
-local traverse_node_list=node.traverse
-local end_of_math=node.end_of_math
-local fontdata=fonts.hashes.identifiers
-local categories=characters and characters.categories or {}
-local otffeatures=fonts.constructors.newfeatures("otf")
-local registerotffeature=otffeatures.register
-local s_init=1 local s_rphf=7
-local s_medi=2 local s_half=8
-local s_fina=3 local s_pref=9
-local s_isol=4 local s_blwf=10
-local s_mark=5 local s_pstf=11
-local s_rest=6
-local states={
- init=s_init,
- medi=s_medi,
- fina=s_fina,
- isol=s_isol,
- mark=s_mark,
- rest=s_rest,
- rphf=s_rphf,
- half=s_half,
- pref=s_pref,
- blwf=s_blwf,
- pstf=s_pstf,
-}
-local features={
- init=s_init,
- medi=s_medi,
- fina=s_fina,
- isol=s_isol,
- rphf=s_rphf,
- half=s_half,
- pref=s_pref,
- blwf=s_blwf,
- pstf=s_pstf,
-}
-analyzers.states=states
-analyzers.features=features
-function analyzers.setstate(head,font)
- local useunicodemarks=analyzers.useunicodemarks
- local tfmdata=fontdata[font]
- local descriptions=tfmdata.descriptions
- local first,last,current,n,done=nil,nil,head,0,false
- while current do
- local id=current.id
- if id==glyph_code and current.font==font then
- done=true
- local char=current.char
- local d=descriptions[char]
- if d then
- if d.class=="mark" or (useunicodemarks and categories[char]=="mn") then
- done=true
- current[a_state]=s_mark
- elseif n==0 then
- first,last,n=current,current,1
- current[a_state]=s_init
- else
- last,n=current,n+1
- current[a_state]=s_medi
- end
- else
- if first and first==last then
- last[a_state]=s_isol
- elseif last then
- last[a_state]=s_fina
- end
- first,last,n=nil,nil,0
- end
- elseif id==disc_code then
- current[a_state]=s_medi
- last=current
- else
- if first and first==last then
- last[a_state]=s_isol
- elseif last then
- last[a_state]=s_fina
- end
- first,last,n=nil,nil,0
- if id==math_code then
- current=end_of_math(current)
- end
- end
- current=current.next
- end
- if first and first==last then
- last[a_state]=s_isol
- elseif last then
- last[a_state]=s_fina
- end
- return head,done
-end
-local function analyzeinitializer(tfmdata,value)
- local script,language=otf.scriptandlanguage(tfmdata)
- local action=initializers[script]
- if not action then
- elseif type(action)=="function" then
- return action(tfmdata,value)
- else
- local action=action[language]
- if action then
- return action(tfmdata,value)
- 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
- elseif type(action)=="function" then
- return action(head,font,attr)
- else
- action=action[language]
- if action then
- return action(head,font,attr)
- end
- end
- return head,false
-end
-registerotffeature {
- name="analyze",
- description="analysis of character classes",
- default=true,
- initializers={
- node=analyzeinitializer,
- },
- processors={
- position=1,
- node=analyzeprocessor,
- }
-}
-methods.latn=analyzers.setstate
-local tatweel=0x0640
-local zwnj=0x200C
-local zwj=0x200D
-local isolated={
- [0x0600]=true,[0x0601]=true,[0x0602]=true,[0x0603]=true,
- [0x0604]=true,
- [0x0608]=true,[0x060B]=true,[0x0621]=true,[0x0674]=true,
- [0x06DD]=true,
- [0x0856]=true,[0x0858]=true,[0x0857]=true,
- [0x07FA]=true,
- [zwnj]=true,
- [0x08AD]=true,
-}
-local final={
- [0x0622]=true,[0x0623]=true,[0x0624]=true,[0x0625]=true,
- [0x0627]=true,[0x0629]=true,[0x062F]=true,[0x0630]=true,
- [0x0631]=true,[0x0632]=true,[0x0648]=true,[0x0671]=true,
- [0x0672]=true,[0x0673]=true,[0x0675]=true,[0x0676]=true,
- [0x0677]=true,[0x0688]=true,[0x0689]=true,[0x068A]=true,
- [0x068B]=true,[0x068C]=true,[0x068D]=true,[0x068E]=true,
- [0x068F]=true,[0x0690]=true,[0x0691]=true,[0x0692]=true,
- [0x0693]=true,[0x0694]=true,[0x0695]=true,[0x0696]=true,
- [0x0697]=true,[0x0698]=true,[0x0699]=true,[0x06C0]=true,
- [0x06C3]=true,[0x06C4]=true,[0x06C5]=true,[0x06C6]=true,
- [0x06C7]=true,[0x06C8]=true,[0x06C9]=true,[0x06CA]=true,
- [0x06CB]=true,[0x06CD]=true,[0x06CF]=true,[0x06D2]=true,
- [0x06D3]=true,[0x06D5]=true,[0x06EE]=true,[0x06EF]=true,
- [0x0759]=true,[0x075A]=true,[0x075B]=true,[0x076B]=true,
- [0x076C]=true,[0x0771]=true,[0x0773]=true,[0x0774]=true,
- [0x0778]=true,[0x0779]=true,
- [0x08AA]=true,[0x08AB]=true,[0x08AC]=true,
- [0xFEF5]=true,[0xFEF7]=true,[0xFEF9]=true,[0xFEFB]=true,
- [0x0710]=true,[0x0715]=true,[0x0716]=true,[0x0717]=true,
- [0x0718]=true,[0x0719]=true,[0x0728]=true,[0x072A]=true,
- [0x072C]=true,[0x071E]=true,
- [0x072F]=true,[0x074D]=true,
- [0x0840]=true,[0x0849]=true,[0x0854]=true,[0x0846]=true,
- [0x084F]=true,
- [0x08AE]=true,[0x08B1]=true,[0x08B2]=true,
-}
-local medial={
- [0x0626]=true,[0x0628]=true,[0x062A]=true,[0x062B]=true,
- [0x062C]=true,[0x062D]=true,[0x062E]=true,[0x0633]=true,
- [0x0634]=true,[0x0635]=true,[0x0636]=true,[0x0637]=true,
- [0x0638]=true,[0x0639]=true,[0x063A]=true,[0x063B]=true,
- [0x063C]=true,[0x063D]=true,[0x063E]=true,[0x063F]=true,
- [0x0641]=true,[0x0642]=true,[0x0643]=true,
- [0x0644]=true,[0x0645]=true,[0x0646]=true,[0x0647]=true,
- [0x0649]=true,[0x064A]=true,[0x066E]=true,[0x066F]=true,
- [0x0678]=true,[0x0679]=true,[0x067A]=true,[0x067B]=true,
- [0x067C]=true,[0x067D]=true,[0x067E]=true,[0x067F]=true,
- [0x0680]=true,[0x0681]=true,[0x0682]=true,[0x0683]=true,
- [0x0684]=true,[0x0685]=true,[0x0686]=true,[0x0687]=true,
- [0x069A]=true,[0x069B]=true,[0x069C]=true,[0x069D]=true,
- [0x069E]=true,[0x069F]=true,[0x06A0]=true,[0x06A1]=true,
- [0x06A2]=true,[0x06A3]=true,[0x06A4]=true,[0x06A5]=true,
- [0x06A6]=true,[0x06A7]=true,[0x06A8]=true,[0x06A9]=true,
- [0x06AA]=true,[0x06AB]=true,[0x06AC]=true,[0x06AD]=true,
- [0x06AE]=true,[0x06AF]=true,[0x06B0]=true,[0x06B1]=true,
- [0x06B2]=true,[0x06B3]=true,[0x06B4]=true,[0x06B5]=true,
- [0x06B6]=true,[0x06B7]=true,[0x06B8]=true,[0x06B9]=true,
- [0x06BA]=true,[0x06BB]=true,[0x06BC]=true,[0x06BD]=true,
- [0x06BE]=true,[0x06BF]=true,[0x06C1]=true,[0x06C2]=true,
- [0x06CC]=true,[0x06CE]=true,[0x06D0]=true,[0x06D1]=true,
- [0x06FA]=true,[0x06FB]=true,[0x06FC]=true,[0x06FF]=true,
- [0x0750]=true,[0x0751]=true,[0x0752]=true,[0x0753]=true,
- [0x0754]=true,[0x0755]=true,[0x0756]=true,[0x0757]=true,
- [0x0758]=true,[0x075C]=true,[0x075D]=true,[0x075E]=true,
- [0x075F]=true,[0x0760]=true,[0x0761]=true,[0x0762]=true,
- [0x0763]=true,[0x0764]=true,[0x0765]=true,[0x0766]=true,
- [0x0767]=true,[0x0768]=true,[0x0769]=true,[0x076A]=true,
- [0x076D]=true,[0x076E]=true,[0x076F]=true,[0x0770]=true,
- [0x0772]=true,[0x0775]=true,[0x0776]=true,[0x0777]=true,
- [0x077A]=true,[0x077B]=true,[0x077C]=true,[0x077D]=true,
- [0x077E]=true,[0x077F]=true,
- [0x08A0]=true,[0x08A2]=true,[0x08A4]=true,[0x08A5]=true,
- [0x08A6]=true,[0x0620]=true,[0x08A8]=true,[0x08A9]=true,
- [0x08A7]=true,[0x08A3]=true,
- [0x0712]=true,[0x0713]=true,[0x0714]=true,[0x071A]=true,
- [0x071B]=true,[0x071C]=true,[0x071D]=true,[0x071F]=true,
- [0x0720]=true,[0x0721]=true,[0x0722]=true,[0x0723]=true,
- [0x0724]=true,[0x0725]=true,[0x0726]=true,[0x0727]=true,
- [0x0729]=true,[0x072B]=true,[0x072D]=true,[0x072E]=true,
- [0x074E]=true,[0x074F]=true,
- [0x0841]=true,[0x0842]=true,[0x0843]=true,[0x0844]=true,
- [0x0845]=true,[0x0847]=true,[0x0848]=true,[0x0855]=true,
- [0x0851]=true,[0x084E]=true,[0x084D]=true,[0x084A]=true,
- [0x084B]=true,[0x084C]=true,[0x0850]=true,[0x0852]=true,
- [0x0853]=true,
- [0x07D7]=true,[0x07E8]=true,[0x07D9]=true,[0x07EA]=true,
- [0x07CA]=true,[0x07DB]=true,[0x07CC]=true,[0x07DD]=true,
- [0x07CE]=true,[0x07DF]=true,[0x07D4]=true,[0x07E5]=true,
- [0x07E9]=true,[0x07E7]=true,[0x07E3]=true,[0x07E2]=true,
- [0x07E0]=true,[0x07E1]=true,[0x07DE]=true,[0x07DC]=true,
- [0x07D1]=true,[0x07DA]=true,[0x07D8]=true,[0x07D6]=true,
- [0x07D2]=true,[0x07D0]=true,[0x07CF]=true,[0x07CD]=true,
- [0x07CB]=true,[0x07D3]=true,[0x07E4]=true,[0x07D5]=true,
- [0x07E6]=true,
- [tatweel]=true,[zwj]=true,
- [0x08A1]=true,[0x08AF]=true,[0x08B0]=true,
-}
-local arab_warned={}
-local function warning(current,what)
- local char=current.char
- if not arab_warned[char] then
- log.report("analyze","arab: character %C has no %a class",char,what)
- arab_warned[char]=true
- end
-end
-local function finish(first,last)
- if last then
- if first==last then
- local fc=first.char
- if medial[fc] or final[fc] then
- first[a_state]=s_isol
- else
- warning(first,"isol")
- first[a_state]=s_error
- end
- else
- local lc=last.char
- if medial[lc] or final[lc] then
- last[a_state]=s_fina
- else
- warning(last,"fina")
- last[a_state]=s_error
- end
- end
- first,last=nil,nil
- elseif first then
- local fc=first.char
- if medial[fc] or final[fc] then
- first[a_state]=s_isol
- else
- warning(first,"isol")
- first[a_state]=s_error
- end
- first=nil
- end
- return first,last
-end
-function methods.arab(head,font,attr)
- local useunicodemarks=analyzers.useunicodemarks
- local tfmdata=fontdata[font]
- local marks=tfmdata.resources.marks
- local first,last,current,done=nil,nil,head,false
- while current do
- local id=current.id
- if id==glyph_code and current.font==font and current.subtype<256 and not current[a_state] then
- done=true
- local char=current.char
- if marks[char] or (useunicodemarks and categories[char]=="mn") then
- current[a_state]=s_mark
- elseif isolated[char] then
- first,last=finish(first,last)
- current[a_state]=s_isol
- first,last=nil,nil
- elseif not first then
- if medial[char] then
- current[a_state]=s_init
- first,last=first or current,current
- elseif final[char] then
- current[a_state]=s_isol
- first,last=nil,nil
- else
- first,last=finish(first,last)
- end
- elseif medial[char] then
- first,last=first or current,current
- current[a_state]=s_medi
- elseif final[char] then
- if not last[a_state]==s_init then
- last[a_state]=s_medi
- end
- current[a_state]=s_fina
- first,last=nil,nil
- elseif char>=0x0600 and char<=0x06FF then
- current[a_state]=s_rest
- first,last=finish(first,last)
- else
- first,last=finish(first,last)
- end
- else
- if first or last then
- first,last=finish(first,last)
- end
- if id==math_code then
- current=end_of_math(current)
- end
- end
- current=current.next
- end
- if first or last then
- finish(first,last)
- end
- return head,done
-end
-methods.syrc=methods.arab
-methods.mand=methods.arab
-methods.nko=methods.arab
-directives.register("otf.analyze.useunicodemarks",function(v)
- analyzers.useunicodemarks=v
-end)
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-otn']={
- version=1.001,
- comment="companion to font-ini.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files",
-}
-local concat,insert,remove=table.concat,table.insert,table.remove
-local gmatch,gsub,find,match,lower,strip=string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip
-local type,next,tonumber,tostring=type,next,tonumber,tostring
-local lpegmatch=lpeg.match
-local random=math.random
-local formatters=string.formatters
-local logs,trackers,nodes,attributes=logs,trackers,nodes,attributes
-local registertracker=trackers.register
-local fonts=fonts
-local otf=fonts.handlers.otf
-local trace_lookups=false registertracker("otf.lookups",function(v) trace_lookups=v end)
-local trace_singles=false registertracker("otf.singles",function(v) trace_singles=v end)
-local trace_multiples=false registertracker("otf.multiples",function(v) trace_multiples=v end)
-local trace_alternatives=false registertracker("otf.alternatives",function(v) trace_alternatives=v end)
-local trace_ligatures=false registertracker("otf.ligatures",function(v) trace_ligatures=v end)
-local trace_contexts=false registertracker("otf.contexts",function(v) trace_contexts=v end)
-local trace_marks=false registertracker("otf.marks",function(v) trace_marks=v end)
-local trace_kerns=false registertracker("otf.kerns",function(v) trace_kerns=v end)
-local trace_cursive=false registertracker("otf.cursive",function(v) trace_cursive=v end)
-local trace_preparing=false registertracker("otf.preparing",function(v) trace_preparing=v end)
-local trace_bugs=false registertracker("otf.bugs",function(v) trace_bugs=v end)
-local trace_details=false registertracker("otf.details",function(v) trace_details=v end)
-local trace_applied=false registertracker("otf.applied",function(v) trace_applied=v end)
-local trace_steps=false registertracker("otf.steps",function(v) trace_steps=v end)
-local trace_skips=false registertracker("otf.skips",function(v) trace_skips=v end)
-local trace_directions=false registertracker("otf.directions",function(v) trace_directions=v end)
-local report_direct=logs.reporter("fonts","otf direct")
-local report_subchain=logs.reporter("fonts","otf subchain")
-local report_chain=logs.reporter("fonts","otf chain")
-local report_process=logs.reporter("fonts","otf process")
-local report_prepare=logs.reporter("fonts","otf prepare")
-local report_warning=logs.reporter("fonts","otf warning")
-registertracker("otf.verbose_chain",function(v) otf.setcontextchain(v and "verbose") end)
-registertracker("otf.normal_chain",function(v) otf.setcontextchain(v and "normal") end)
-registertracker("otf.replacements","otf.singles,otf.multiples,otf.alternatives,otf.ligatures")
-registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive")
-registertracker("otf.actions","otf.replacements,otf.positions")
-registertracker("otf.injections","nodes.injections")
-registertracker("*otf.sample","otf.steps,otf.actions,otf.analyzing")
-local insert_node_after=node.insert_after
-local delete_node=nodes.delete
-local copy_node=node.copy
-local find_node_tail=node.tail or node.slide
-local flush_node_list=node.flush_list
-local end_of_math=node.end_of_math
-local setmetatableindex=table.setmetatableindex
-local zwnj=0x200C
-local zwj=0x200D
-local wildcard="*"
-local default="dflt"
-local nodecodes=nodes.nodecodes
-local whatcodes=nodes.whatcodes
-local glyphcodes=nodes.glyphcodes
-local disccodes=nodes.disccodes
-local glyph_code=nodecodes.glyph
-local glue_code=nodecodes.glue
-local disc_code=nodecodes.disc
-local whatsit_code=nodecodes.whatsit
-local math_code=nodecodes.math
-local dir_code=whatcodes.dir
-local localpar_code=whatcodes.localpar
-local discretionary_code=disccodes.discretionary
-local ligature_code=glyphcodes.ligature
-local privateattribute=attributes.private
-local a_state=privateattribute('state')
-local a_markbase=privateattribute('markbase')
-local a_markmark=privateattribute('markmark')
-local a_markdone=privateattribute('markdone')
-local a_cursbase=privateattribute('cursbase')
-local a_curscurs=privateattribute('curscurs')
-local a_cursdone=privateattribute('cursdone')
-local a_kernpair=privateattribute('kernpair')
-local a_ligacomp=privateattribute('ligacomp')
-local injections=nodes.injections
-local setmark=injections.setmark
-local setcursive=injections.setcursive
-local setkern=injections.setkern
-local setpair=injections.setpair
-local markonce=true
-local cursonce=true
-local kernonce=true
-local fonthashes=fonts.hashes
-local fontdata=fonthashes.identifiers
-local otffeatures=fonts.constructors.newfeatures("otf")
-local registerotffeature=otffeatures.register
-local onetimemessage=fonts.loggers.onetimemessage or function() end
-otf.defaultnodealternate="none"
-local tfmdata=false
-local characters=false
-local descriptions=false
-local resources=false
-local marks=false
-local currentfont=false
-local lookuptable=false
-local anchorlookups=false
-local lookuptypes=false
-local handlers={}
-local rlmode=0
-local featurevalue=false
-local checkstep=(nodes and nodes.tracers and nodes.tracers.steppers.check) or function() end
-local registerstep=(nodes and nodes.tracers and nodes.tracers.steppers.register) or function() end
-local registermessage=(nodes and nodes.tracers and nodes.tracers.steppers.message) or function() end
-local function logprocess(...)
- if trace_steps then
- registermessage(...)
- end
- report_direct(...)
-end
-local function logwarning(...)
- report_direct(...)
-end
-local f_unicode=formatters["%U"]
-local f_uniname=formatters["%U (%s)"]
-local f_unilist=formatters["% t (% t)"]
-local function gref(n)
- if type(n)=="number" then
- local description=descriptions[n]
- local name=description and description.name
- if name then
- return f_uniname(n,name)
- else
- return f_unicode(n)
- end
- elseif n then
- local num,nam={},{}
- for i=1,#n do
- local ni=n[i]
- if tonumber(ni) then
- local di=descriptions[ni]
- num[i]=f_unicode(ni)
- nam[i]=di and di.name or "-"
- end
- end
- return f_unilist(num,nam)
- else
- return "<error in node mode tracing>"
- end
-end
-local function cref(kind,chainname,chainlookupname,lookupname,index)
- if index then
- return formatters["feature %a, chain %a, sub %a, lookup %a, index %a"](kind,chainname,chainlookupname,lookupname,index)
- elseif lookupname then
- return formatters["feature %a, chain %a, sub %a, lookup %a"](kind,chainname,chainlookupname,lookupname)
- elseif chainlookupname then
- return formatters["feature %a, chain %a, sub %a"](kind,chainname,chainlookupname)
- elseif chainname then
- return formatters["feature %a, chain %a"](kind,chainname)
- else
- return formatters["feature %a"](kind)
- end
-end
-local function pref(kind,lookupname)
- return formatters["feature %a, lookup %a"](kind,lookupname)
-end
-local function copy_glyph(g)
- local components=g.components
- if components then
- g.components=nil
- local n=copy_node(g)
- g.components=components
- return n
- else
- return copy_node(g)
- end
-end
-local function markstoligature(kind,lookupname,head,start,stop,char)
- if start==stop and start.char==char then
- return head,start
- else
- local prev=start.prev
- local next=stop.next
- start.prev=nil
- stop.next=nil
- local base=copy_glyph(start)
- if head==start then
- head=base
- end
- base.char=char
- base.subtype=ligature_code
- base.components=start
- if prev then
- prev.next=base
- end
- if next then
- next.prev=base
- end
- base.next=next
- base.prev=prev
- return head,base
- end
-end
-local function getcomponentindex(start)
- if start.id~=glyph_code then
- return 0
- elseif start.subtype==ligature_code then
- local i=0
- local components=start.components
- while components do
- i=i+getcomponentindex(components)
- components=components.next
- end
- return i
- elseif not marks[start.char] then
- return 1
- else
- return 0
- end
-end
-local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound)
- if start==stop and start.char==char then
- start.char=char
- return head,start
- end
- local prev=start.prev
- local next=stop.next
- start.prev=nil
- stop.next=nil
- local base=copy_glyph(start)
- if start==head then
- head=base
- end
- base.char=char
- base.subtype=ligature_code
- base.components=start
- if prev then
- prev.next=base
- end
- if next then
- next.prev=base
- end
- base.next=next
- base.prev=prev
- if not discfound then
- local deletemarks=markflag~="mark"
- local components=start
- local baseindex=0
- local componentindex=0
- local head=base
- local current=base
- while start do
- local char=start.char
- if not marks[char] then
- baseindex=baseindex+componentindex
- componentindex=getcomponentindex(start)
- elseif not deletemarks then
- start[a_ligacomp]=baseindex+(start[a_ligacomp] or componentindex)
- if trace_marks then
- logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp])
- end
- head,current=insert_node_after(head,current,copy_node(start))
- elseif trace_marks then
- logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char))
- end
- start=start.next
- end
- local start=current.next
- while start and start.id==glyph_code do
- local char=start.char
- if marks[char] then
- start[a_ligacomp]=baseindex+(start[a_ligacomp] or componentindex)
- if trace_marks then
- logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp])
- end
- else
- break
- end
- start=start.next
- end
- end
- return head,base
-end
-function handlers.gsub_single(head,start,kind,lookupname,replacement)
- if trace_singles then
- logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(start.char),gref(replacement))
- end
- start.char=replacement
- return head,start,true
-end
-local function get_alternative_glyph(start,alternatives,value,trace_alternatives)
- local n=#alternatives
- if value=="random" then
- local r=random(1,n)
- return alternatives[r],trace_alternatives and formatters["value %a, taking %a"](value,r)
- elseif value=="first" then
- return alternatives[1],trace_alternatives and formatters["value %a, taking %a"](value,1)
- elseif value=="last" then
- return alternatives[n],trace_alternatives and formatters["value %a, taking %a"](value,n)
- else
- value=tonumber(value)
- if type(value)~="number" then
- return alternatives[1],trace_alternatives and formatters["invalid value %s, taking %a"](value,1)
- elseif value>n then
- local defaultalt=otf.defaultnodealternate
- if defaultalt=="first" then
- return alternatives[n],trace_alternatives and formatters["invalid value %s, taking %a"](value,1)
- elseif defaultalt=="last" then
- return alternatives[1],trace_alternatives and formatters["invalid value %s, taking %a"](value,n)
- else
- return false,trace_alternatives and formatters["invalid value %a, %s"](value,"out of range")
- end
- elseif value==0 then
- return start.char,trace_alternatives and formatters["invalid value %a, %s"](value,"no change")
- elseif value<1 then
- return alternatives[1],trace_alternatives and formatters["invalid value %a, taking %a"](value,1)
- else
- return alternatives[value],trace_alternatives and formatters["value %a, taking %a"](value,value)
- end
- end
-end
-local function multiple_glyphs(head,start,multiple,ignoremarks)
- local nofmultiples=#multiple
- if nofmultiples>0 then
- start.char=multiple[1]
- if nofmultiples>1 then
- local sn=start.next
- for k=2,nofmultiples do
- local n=copy_node(start)
- n.char=multiple[k]
- n.next=sn
- n.prev=start
- if sn then
- sn.prev=n
- end
- start.next=n
- start=n
- end
- end
- return head,start,true
- else
- if trace_multiples then
- logprocess("no multiple for %s",gref(start.char))
- end
- return head,start,false
- end
-end
-function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence)
- local value=featurevalue==true and tfmdata.shared.features[kind] or featurevalue
- local choice,comment=get_alternative_glyph(start,alternative,value,trace_alternatives)
- if choice then
- if trace_alternatives then
- logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(start.char),choice,gref(choice),comment)
- end
- start.char=choice
- else
- if trace_alternatives then
- logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(start.char),comment)
- end
- end
- return head,start,true
-end
-function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence)
- if trace_multiples then
- logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple))
- end
- return multiple_glyphs(head,start,multiple,sequence.flags[1])
-end
-function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
- local s,stop,discfound=start.next,nil,false
- local startchar=start.char
- if marks[startchar] then
- while s do
- local id=s.id
- if id==glyph_code and s.font==currentfont and s.subtype<256 then
- local lg=ligature[s.char]
- if lg then
- stop=s
- ligature=lg
- s=s.next
- else
- break
- end
- else
- break
- end
- end
- if stop then
- local lig=ligature.ligature
- if lig then
- if trace_ligatures then
- local stopchar=stop.char
- head,start=markstoligature(kind,lookupname,head,start,stop,lig)
- logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char))
- else
- head,start=markstoligature(kind,lookupname,head,start,stop,lig)
- end
- return head,start,true
- else
- end
- end
- else
- local skipmark=sequence.flags[1]
- while s do
- local id=s.id
- if id==glyph_code and s.subtype<256 then
- if s.font==currentfont then
- local char=s.char
- if skipmark and marks[char] then
- s=s.next
- else
- local lg=ligature[char]
- if lg then
- stop=s
- ligature=lg
- s=s.next
- else
- break
- end
- end
- else
- break
- end
- elseif id==disc_code then
- discfound=true
- s=s.next
- else
- break
- end
- end
- local lig=ligature.ligature
- if lig then
- if stop then
- if trace_ligatures then
- local stopchar=stop.char
- head,start=toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound)
- logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char))
- else
- head,start=toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound)
- end
- return head,start,true
- else
- start.char=lig
- if trace_ligatures then
- logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig))
- end
- return head,start,true
- end
- else
- end
- end
- return head,start,false
-end
-function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence)
- local markchar=start.char
- if marks[markchar] then
- local base=start.prev
- if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
- local basechar=base.char
- if marks[basechar] then
- while true do
- base=base.prev
- if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
- basechar=base.char
- if not marks[basechar] then
- break
- end
- else
- if trace_bugs then
- logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar))
- end
- return head,start,false
- end
- end
- end
- local baseanchors=descriptions[basechar]
- if baseanchors then
- baseanchors=baseanchors.anchors
- end
- if baseanchors then
- local baseanchors=baseanchors['basechar']
- if baseanchors then
- local al=anchorlookups[lookupname]
- for anchor,ba in next,baseanchors do
- if al[anchor] then
- local ma=markanchors[anchor]
- if ma then
- local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma)
- if trace_marks then
- logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)",
- pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
- end
- return head,start,true
- end
- end
- end
- if trace_bugs then
- logwarning("%s, no matching anchors for mark %s and base %s",pref(kind,lookupname),gref(markchar),gref(basechar))
- end
- end
- elseif trace_bugs then
- onetimemessage(currentfont,basechar,"no base anchors",report_fonts)
- end
- elseif trace_bugs then
- logwarning("%s: prev node is no char",pref(kind,lookupname))
- end
- elseif trace_bugs then
- logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar))
- end
- return head,start,false
-end
-function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequence)
- local markchar=start.char
- if marks[markchar] then
- local base=start.prev
- if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
- local basechar=base.char
- if marks[basechar] then
- while true do
- base=base.prev
- if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
- basechar=base.char
- if not marks[basechar] then
- break
- end
- else
- if trace_bugs then
- logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar))
- end
- return head,start,false
- end
- end
- end
- local index=start[a_ligacomp]
- local baseanchors=descriptions[basechar]
- if baseanchors then
- baseanchors=baseanchors.anchors
- if baseanchors then
- local baseanchors=baseanchors['baselig']
- if baseanchors then
- local al=anchorlookups[lookupname]
- for anchor,ba in next,baseanchors do
- if al[anchor] then
- local ma=markanchors[anchor]
- if ma then
- ba=ba[index]
- if ba then
- local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma)
- if trace_marks then
- logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)",
- pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy)
- end
- return head,start,true
- else
- if trace_bugs then
- logwarning("%s: no matching anchors for mark %s and baselig %s with index %a",pref(kind,lookupname),gref(markchar),gref(basechar),index)
- end
- end
- end
- end
- end
- if trace_bugs then
- logwarning("%s: no matching anchors for mark %s and baselig %s",pref(kind,lookupname),gref(markchar),gref(basechar))
- end
- end
- end
- elseif trace_bugs then
- onetimemessage(currentfont,basechar,"no base anchors",report_fonts)
- end
- elseif trace_bugs then
- logwarning("%s: prev node is no char",pref(kind,lookupname))
- end
- elseif trace_bugs then
- logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar))
- end
- return head,start,false
-end
-function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence)
- local markchar=start.char
- if marks[markchar] then
- local base=start.prev
- local slc=start[a_ligacomp]
- if slc then
- while base do
- local blc=base[a_ligacomp]
- if blc and blc~=slc then
- base=base.prev
- else
- break
- end
- end
- end
- if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
- local basechar=base.char
- local baseanchors=descriptions[basechar]
- if baseanchors then
- baseanchors=baseanchors.anchors
- if baseanchors then
- baseanchors=baseanchors['basemark']
- if baseanchors then
- local al=anchorlookups[lookupname]
- for anchor,ba in next,baseanchors do
- if al[anchor] then
- local ma=markanchors[anchor]
- if ma then
- local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,true)
- if trace_marks then
- logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
- pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
- end
- return head,start,true
- end
- end
- end
- if trace_bugs then
- logwarning("%s: no matching anchors for mark %s and basemark %s",pref(kind,lookupname),gref(markchar),gref(basechar))
- end
- end
- end
- elseif trace_bugs then
- onetimemessage(currentfont,basechar,"no base anchors",report_fonts)
- end
- elseif trace_bugs then
- logwarning("%s: prev node is no mark",pref(kind,lookupname))
- end
- elseif trace_bugs then
- logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar))
- end
- return head,start,false
-end
-function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence)
- local alreadydone=cursonce and start[a_cursbase]
- if not alreadydone then
- local done=false
- local startchar=start.char
- if marks[startchar] then
- if trace_cursive then
- logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar))
- end
- else
- local nxt=start.next
- while not done and nxt and nxt.id==glyph_code and nxt.font==currentfont and nxt.subtype<256 do
- local nextchar=nxt.char
- if marks[nextchar] then
- nxt=nxt.next
- else
- local entryanchors=descriptions[nextchar]
- if entryanchors then
- entryanchors=entryanchors.anchors
- if entryanchors then
- entryanchors=entryanchors['centry']
- if entryanchors then
- local al=anchorlookups[lookupname]
- for anchor,entry in next,entryanchors do
- if al[anchor] then
- local exit=exitanchors[anchor]
- if exit then
- local dx,dy,bound=setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar])
- if trace_cursive then
- logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode)
- end
- done=true
- break
- end
- end
- end
- end
- end
- elseif trace_bugs then
- onetimemessage(currentfont,startchar,"no entry anchors",report_fonts)
- end
- break
- end
- end
- end
- return head,start,done
- else
- if trace_cursive and trace_details then
- logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone)
- end
- return head,start,false
- end
-end
-function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence)
- local startchar=start.char
- local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar])
- if trace_kerns then
- logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h)
- end
- return head,start,false
-end
-function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence)
- local snext=start.next
- if not snext then
- return head,start,false
- else
- local prev,done=start,false
- local factor=tfmdata.parameters.factor
- local lookuptype=lookuptypes[lookupname]
- while snext and snext.id==glyph_code and snext.font==currentfont and snext.subtype<256 do
- local nextchar=snext.char
- local krn=kerns[nextchar]
- if not krn and marks[nextchar] then
- prev=snext
- snext=snext.next
- else
- if not krn then
- elseif type(krn)=="table" then
- if lookuptype=="pair" then
- local a,b=krn[2],krn[3]
- if a and #a>0 then
- local startchar=start.char
- local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])
- if trace_kerns then
- logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
- end
- if b and #b>0 then
- local startchar=start.char
- local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])
- if trace_kerns then
- logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
- end
- else
- report_process("%s: check this out (old kern stuff)",pref(kind,lookupname))
- end
- done=true
- elseif krn~=0 then
- local k=setkern(snext,factor,rlmode,krn)
- if trace_kerns then
- logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar))
- end
- done=true
- end
- break
- end
- end
- return head,start,done
- end
-end
-local chainmores={}
-local chainprocs={}
-local function logprocess(...)
- if trace_steps then
- registermessage(...)
- end
- report_subchain(...)
-end
-local logwarning=report_subchain
-local function logprocess(...)
- if trace_steps then
- registermessage(...)
- end
- report_chain(...)
-end
-local logwarning=report_chain
-function chainprocs.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname)
- logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname))
- return head,start,false
-end
-function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n)
- logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname))
- return head,start,false
-end
-function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,lookuphash,replacements)
- local char=start.char
- local replacement=replacements[char]
- if replacement then
- if trace_singles then
- logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement))
- end
- start.char=replacement
- return head,start,true
- else
- return head,start,false
- end
-end
-function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex)
- local current=start
- local subtables=currentlookup.subtables
- if #subtables>1 then
- logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," "))
- end
- while current do
- if current.id==glyph_code then
- local currentchar=current.char
- local lookupname=subtables[1]
- local replacement=lookuphash[lookupname]
- if not replacement then
- if trace_bugs then
- logwarning("%s: no single hits",cref(kind,chainname,chainlookupname,lookupname,chainindex))
- end
- else
- replacement=replacement[currentchar]
- if not replacement or replacement=="" then
- if trace_bugs then
- logwarning("%s: no single for %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar))
- end
- else
- if trace_singles then
- logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement))
- end
- current.char=replacement
- end
- end
- return head,start,true
- elseif current==stop then
- break
- else
- current=current.next
- end
- end
- return head,start,false
-end
-chainmores.gsub_single=chainprocs.gsub_single
-function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
- local startchar=start.char
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local replacements=lookuphash[lookupname]
- if not replacements then
- if trace_bugs then
- logwarning("%s: no multiple hits",cref(kind,chainname,chainlookupname,lookupname))
- end
- else
- replacements=replacements[startchar]
- if not replacements or replacement=="" then
- if trace_bugs then
- logwarning("%s: no multiple for %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar))
- end
- else
- if trace_multiples then
- logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements))
- end
- return multiple_glyphs(head,start,replacements,currentlookup.flags[1])
- end
- end
- return head,start,false
-end
-chainmores.gsub_multiple=chainprocs.gsub_multiple
-function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
- local current=start
- local subtables=currentlookup.subtables
- local value=featurevalue==true and tfmdata.shared.features[kind] or featurevalue
- while current do
- if current.id==glyph_code then
- local currentchar=current.char
- local lookupname=subtables[1]
- local alternatives=lookuphash[lookupname]
- if not alternatives then
- if trace_bugs then
- logwarning("%s: no alternative hit",cref(kind,chainname,chainlookupname,lookupname))
- end
- else
- alternatives=alternatives[currentchar]
- if alternatives then
- local choice,comment=get_alternative_glyph(current,alternatives,value,trace_alternatives)
- if choice then
- if trace_alternatives then
- logprocess("%s: replacing %s by alternative %a to %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(char),choice,gref(choice),comment)
- end
- start.char=choice
- else
- if trace_alternatives then
- logwarning("%s: no variant %a for %s, %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(char),comment)
- end
- end
- elseif trace_bugs then
- logwarning("%s: no alternative for %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar),comment)
- end
- end
- return head,start,true
- elseif current==stop then
- break
- else
- current=current.next
- end
- end
- return head,start,false
-end
-chainmores.gsub_alternate=chainprocs.gsub_alternate
-function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex)
- local startchar=start.char
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local ligatures=lookuphash[lookupname]
- if not ligatures then
- if trace_bugs then
- logwarning("%s: no ligature hits",cref(kind,chainname,chainlookupname,lookupname,chainindex))
- end
- else
- ligatures=ligatures[startchar]
- if not ligatures then
- if trace_bugs then
- logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))
- end
- else
- local s=start.next
- local discfound=false
- local last=stop
- local nofreplacements=0
- local skipmark=currentlookup.flags[1]
- while s do
- local id=s.id
- if id==disc_code then
- s=s.next
- discfound=true
- else
- local schar=s.char
- if skipmark and marks[schar] then
- s=s.next
- else
- local lg=ligatures[schar]
- if lg then
- ligatures,last,nofreplacements=lg,s,nofreplacements+1
- if s==stop then
- break
- else
- s=s.next
- end
- else
- break
- end
- end
- end
- end
- local l2=ligatures.ligature
- if l2 then
- if chainindex then
- stop=last
- end
- if trace_ligatures then
- if start==stop then
- logprocess("%s: replacing character %s by ligature %s case 3",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2))
- else
- logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2))
- end
- end
- head,start=toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound)
- return head,start,true,nofreplacements
- elseif trace_bugs then
- if start==stop then
- logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))
- else
- logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char))
- end
- end
- end
- end
- return head,start,false,0
-end
-chainmores.gsub_ligature=chainprocs.gsub_ligature
-function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
- local markchar=start.char
- if marks[markchar] then
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local markanchors=lookuphash[lookupname]
- if markanchors then
- markanchors=markanchors[markchar]
- end
- if markanchors then
- local base=start.prev
- if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
- local basechar=base.char
- if marks[basechar] then
- while true do
- base=base.prev
- if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
- basechar=base.char
- if not marks[basechar] then
- break
- end
- else
- if trace_bugs then
- logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar))
- end
- return head,start,false
- end
- end
- end
- local baseanchors=descriptions[basechar].anchors
- if baseanchors then
- local baseanchors=baseanchors['basechar']
- if baseanchors then
- local al=anchorlookups[lookupname]
- for anchor,ba in next,baseanchors do
- if al[anchor] then
- local ma=markanchors[anchor]
- if ma then
- local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma)
- if trace_marks then
- logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)",
- cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
- end
- return head,start,true
- end
- end
- end
- if trace_bugs then
- logwarning("%s, no matching anchors for mark %s and base %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar))
- end
- end
- end
- elseif trace_bugs then
- logwarning("%s: prev node is no char",cref(kind,chainname,chainlookupname,lookupname))
- end
- elseif trace_bugs then
- logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar))
- end
- elseif trace_bugs then
- logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar))
- end
- return head,start,false
-end
-function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
- local markchar=start.char
- if marks[markchar] then
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local markanchors=lookuphash[lookupname]
- if markanchors then
- markanchors=markanchors[markchar]
- end
- if markanchors then
- local base=start.prev
- if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
- local basechar=base.char
- if marks[basechar] then
- while true do
- base=base.prev
- if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
- basechar=base.char
- if not marks[basechar] then
- break
- end
- else
- if trace_bugs then
- logwarning("%s: no base for mark %s",cref(kind,chainname,chainlookupname,lookupname),markchar)
- end
- return head,start,false
- end
- end
- end
- local index=start[a_ligacomp]
- local baseanchors=descriptions[basechar].anchors
- if baseanchors then
- local baseanchors=baseanchors['baselig']
- if baseanchors then
- local al=anchorlookups[lookupname]
- for anchor,ba in next,baseanchors do
- if al[anchor] then
- local ma=markanchors[anchor]
- if ma then
- ba=ba[index]
- if ba then
- local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma)
- if trace_marks then
- logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)",
- cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy)
- end
- return head,start,true
- end
- end
- end
- end
- if trace_bugs then
- logwarning("%s: no matching anchors for mark %s and baselig %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar))
- end
- end
- end
- elseif trace_bugs then
- logwarning("feature %s, lookup %s: prev node is no char",kind,lookupname)
- end
- elseif trace_bugs then
- logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar))
- end
- elseif trace_bugs then
- logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar))
- end
- return head,start,false
-end
-function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
- local markchar=start.char
- if marks[markchar] then
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local markanchors=lookuphash[lookupname]
- if markanchors then
- markanchors=markanchors[markchar]
- end
- if markanchors then
- local base=start.prev
- local slc=start[a_ligacomp]
- if slc then
- while base do
- local blc=base[a_ligacomp]
- if blc and blc~=slc then
- base=base.prev
- else
- break
- end
- end
- end
- if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
- local basechar=base.char
- local baseanchors=descriptions[basechar].anchors
- if baseanchors then
- baseanchors=baseanchors['basemark']
- if baseanchors then
- local al=anchorlookups[lookupname]
- for anchor,ba in next,baseanchors do
- if al[anchor] then
- local ma=markanchors[anchor]
- if ma then
- local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,true)
- if trace_marks then
- logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
- cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
- end
- return head,start,true
- end
- end
- end
- if trace_bugs then
- logwarning("%s: no matching anchors for mark %s and basemark %s",gref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar))
- end
- end
- end
- elseif trace_bugs then
- logwarning("%s: prev node is no mark",cref(kind,chainname,chainlookupname,lookupname))
- end
- elseif trace_bugs then
- logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar))
- end
- elseif trace_bugs then
- logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar))
- end
- return head,start,false
-end
-function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
- local alreadydone=cursonce and start[a_cursbase]
- if not alreadydone then
- local startchar=start.char
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local exitanchors=lookuphash[lookupname]
- if exitanchors then
- exitanchors=exitanchors[startchar]
- end
- if exitanchors then
- local done=false
- if marks[startchar] then
- if trace_cursive then
- logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar))
- end
- else
- local nxt=start.next
- while not done and nxt and nxt.id==glyph_code and nxt.font==currentfont and nxt.subtype<256 do
- local nextchar=nxt.char
- if marks[nextchar] then
- nxt=nxt.next
- else
- local entryanchors=descriptions[nextchar]
- if entryanchors then
- entryanchors=entryanchors.anchors
- if entryanchors then
- entryanchors=entryanchors['centry']
- if entryanchors then
- local al=anchorlookups[lookupname]
- for anchor,entry in next,entryanchors do
- if al[anchor] then
- local exit=exitanchors[anchor]
- if exit then
- local dx,dy,bound=setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar])
- if trace_cursive then
- logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode)
- end
- done=true
- break
- end
- end
- end
- end
- end
- elseif trace_bugs then
- onetimemessage(currentfont,startchar,"no entry anchors",report_fonts)
- end
- break
- end
- end
- end
- return head,start,done
- else
- if trace_cursive and trace_details then
- logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone)
- end
- return head,start,false
- end
- end
- return head,start,false
-end
-function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
- local startchar=start.char
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local kerns=lookuphash[lookupname]
- if kerns then
- kerns=kerns[startchar]
- if kerns then
- local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar])
- if trace_kerns then
- logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h)
- end
- end
- end
- return head,start,false
-end
-chainmores.gpos_single=chainprocs.gpos_single
-function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
- local snext=start.next
- if snext then
- local startchar=start.char
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local kerns=lookuphash[lookupname]
- if kerns then
- kerns=kerns[startchar]
- if kerns then
- local lookuptype=lookuptypes[lookupname]
- local prev,done=start,false
- local factor=tfmdata.parameters.factor
- while snext and snext.id==glyph_code and snext.font==currentfont and snext.subtype<256 do
- local nextchar=snext.char
- local krn=kerns[nextchar]
- if not krn and marks[nextchar] then
- prev=snext
- snext=snext.next
- else
- if not krn then
- elseif type(krn)=="table" then
- if lookuptype=="pair" then
- local a,b=krn[2],krn[3]
- if a and #a>0 then
- local startchar=start.char
- local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])
- if trace_kerns then
- logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
- end
- if b and #b>0 then
- local startchar=start.char
- local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])
- if trace_kerns then
- logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
- end
- else
- report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname))
- local a,b=krn[2],krn[6]
- if a and a~=0 then
- local k=setkern(snext,factor,rlmode,a)
- if trace_kerns then
- logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar))
- end
- end
- if b and b~=0 then
- logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor)
- end
- end
- done=true
- elseif krn~=0 then
- local k=setkern(snext,factor,rlmode,krn)
- if trace_kerns then
- logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar))
- end
- done=true
- end
- break
- end
- end
- return head,start,done
- end
- end
- end
- return head,start,false
-end
-chainmores.gpos_pair=chainprocs.gpos_pair
-local function show_skip(kind,chainname,char,ck,class)
- if ck[9] then
- logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10])
- else
- logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2])
- end
-end
-local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash)
- local flags=sequence.flags
- local done=false
- local skipmark=flags[1]
- local skipligature=flags[2]
- local skipbase=flags[3]
- local someskip=skipmark or skipligature or skipbase
- local markclass=sequence.markclass
- local skipped=false
- for k=1,#contexts do
- local match=true
- local current=start
- local last=start
- local ck=contexts[k]
- local seq=ck[3]
- local s=#seq
- if s==1 then
- match=current.id==glyph_code and current.font==currentfont and current.subtype<256 and seq[1][current.char]
- else
- local f,l=ck[4],ck[5]
- if f==1 and f==l then
- else
- if f==l then
- else
- local n=f+1
- last=last.next
- while n<=l do
- if last then
- local id=last.id
- if id==glyph_code then
- if last.font==currentfont and last.subtype<256 then
- local char=last.char
- local ccd=descriptions[char]
- if ccd then
- local class=ccd.class
- if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
- skipped=true
- if trace_skips then
- show_skip(kind,chainname,char,ck,class)
- end
- last=last.next
- elseif seq[n][char] then
- if n<l then
- last=last.next
- end
- n=n+1
- else
- match=false
- break
- end
- else
- match=false
- break
- end
- else
- match=false
- break
- end
- elseif id==disc_code then
- last=last.next
- else
- match=false
- break
- end
- else
- match=false
- break
- end
- end
- end
- end
- if match and f>1 then
- local prev=start.prev
- if prev then
- local n=f-1
- while n>=1 do
- if prev then
- local id=prev.id
- if id==glyph_code then
- if prev.font==currentfont and prev.subtype<256 then
- local char=prev.char
- local ccd=descriptions[char]
- if ccd then
- local class=ccd.class
- if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
- skipped=true
- if trace_skips then
- show_skip(kind,chainname,char,ck,class)
- end
- elseif seq[n][char] then
- n=n -1
- else
- match=false
- break
- end
- else
- match=false
- break
- end
- else
- match=false
- break
- end
- elseif id==disc_code then
- elseif seq[n][32] then
- n=n -1
- else
- match=false
- break
- end
- prev=prev.prev
- elseif seq[n][32] then
- n=n -1
- else
- match=false
- break
- end
- end
- elseif f==2 then
- match=seq[1][32]
- else
- for n=f-1,1 do
- if not seq[n][32] then
- match=false
- break
- end
- end
- end
- end
- if match and s>l then
- local current=last and last.next
- if current then
- local n=l+1
- while n<=s do
- if current then
- local id=current.id
- if id==glyph_code then
- if current.font==currentfont and current.subtype<256 then
- local char=current.char
- local ccd=descriptions[char]
- if ccd then
- local class=ccd.class
- if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
- skipped=true
- if trace_skips then
- show_skip(kind,chainname,char,ck,class)
- end
- elseif seq[n][char] then
- n=n+1
- else
- match=false
- break
- end
- else
- match=false
- break
- end
- else
- match=false
- break
- end
- elseif id==disc_code then
- elseif seq[n][32] then
- n=n+1
- else
- match=false
- break
- end
- current=current.next
- elseif seq[n][32] then
- n=n+1
- else
- match=false
- break
- end
- end
- elseif s-l==1 then
- match=seq[s][32]
- else
- for n=l+1,s do
- if not seq[n][32] then
- match=false
- break
- end
- end
- end
- end
- end
- if match then
- if trace_contexts then
- local rule,lookuptype,f,l=ck[1],ck[2],ck[4],ck[5]
- local char=start.char
- if ck[9] then
- logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a, %a => %a",
- cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10])
- else
- logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a",
- cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype)
- end
- end
- local chainlookups=ck[6]
- if chainlookups then
- local nofchainlookups=#chainlookups
- if nofchainlookups==1 then
- local chainlookupname=chainlookups[1]
- local chainlookup=lookuptable[chainlookupname]
- if chainlookup then
- local cp=chainprocs[chainlookup.type]
- if cp then
- local ok
- head,start,ok=cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
- if ok then
- done=true
- end
- else
- logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type)
- end
- else
- logprocess("%s is not yet supported",cref(kind,chainname,chainlookupname))
- end
- else
- local i=1
- repeat
- if skipped then
- while true do
- local char=start.char
- local ccd=descriptions[char]
- if ccd then
- local class=ccd.class
- if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
- start=start.next
- else
- break
- end
- else
- break
- end
- end
- end
- local chainlookupname=chainlookups[i]
- local chainlookup=lookuptable[chainlookupname]
- if not chainlookup then
- i=i+1
- else
- local cp=chainmores[chainlookup.type]
- if not cp then
- logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type)
- i=i+1
- else
- local ok,n
- head,start,ok,n=cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence)
- if ok then
- done=true
- i=i+(n or 1)
- else
- i=i+1
- end
- end
- end
- if start then
- start=start.next
- else
- end
- until i>nofchainlookups
- end
- else
- local replacements=ck[7]
- if replacements then
- head,start,done=chainprocs.reversesub(head,start,last,kind,chainname,ck,lookuphash,replacements)
- else
- done=true
- if trace_contexts then
- logprocess("%s: skipping match",cref(kind,chainname))
- end
- end
- end
- end
- end
- return head,start,done
-end
-local verbose_handle_contextchain=function(font,...)
- logwarning("no verbose handler installed, reverting to 'normal'")
- otf.setcontextchain()
- return normal_handle_contextchain(...)
-end
-otf.chainhandlers={
- normal=normal_handle_contextchain,
- verbose=verbose_handle_contextchain,
-}
-function otf.setcontextchain(method)
- if not method or method=="normal" or not otf.chainhandlers[method] then
- if handlers.contextchain then
- logwarning("installing normal contextchain handler")
- end
- handlers.contextchain=normal_handle_contextchain
- else
- logwarning("installing contextchain handler %a",method)
- local handler=otf.chainhandlers[method]
- handlers.contextchain=function(...)
- return handler(currentfont,...)
- end
- end
- handlers.gsub_context=handlers.contextchain
- handlers.gsub_contextchain=handlers.contextchain
- handlers.gsub_reversecontextchain=handlers.contextchain
- handlers.gpos_contextchain=handlers.contextchain
- handlers.gpos_context=handlers.contextchain
-end
-otf.setcontextchain()
-local missing={}
-local function logprocess(...)
- if trace_steps then
- registermessage(...)
- end
- report_process(...)
-end
-local logwarning=report_process
-local function report_missing_cache(typ,lookup)
- local f=missing[currentfont] if not f then f={} missing[currentfont]=f end
- local t=f[typ] if not t then t={} f[typ]=t end
- if not t[lookup] then
- t[lookup]=true
- logwarning("missing cache for lookup %a, type %a, font %a, name %a",lookup,typ,currentfont,tfmdata.properties.fullname)
- end
-end
-local resolved={}
-local lookuphashes={}
-setmetatableindex(lookuphashes,function(t,font)
- local lookuphash=fontdata[font].resources.lookuphash
- if not lookuphash or not next(lookuphash) then
- lookuphash=false
- end
- t[font]=lookuphash
- return lookuphash
-end)
-local autofeatures=fonts.analyzers.features
-local function initialize(sequence,script,language,enabled)
- local features=sequence.features
- if features then
- local order=sequence.order
- if order then
- for i=1,#order do
- local kind=order[i]
- local valid=enabled[kind]
- if valid then
- local scripts=features[kind]
- local languages=scripts[script] or scripts[wildcard]
- if languages and (languages[language] or languages[wildcard]) then
- return { valid,autofeatures[kind] or false,sequence.chain or 0,kind,sequence }
- end
- end
- end
- else
- end
- end
- return false
-end
-function otf.dataset(tfmdata,font)
- local shared=tfmdata.shared
- local properties=tfmdata.properties
- local language=properties.language or "dflt"
- local script=properties.script or "dflt"
- local enabled=shared.features
- local res=resolved[font]
- if not res then
- res={}
- resolved[font]=res
- end
- local rs=res[script]
- if not rs then
- rs={}
- res[script]=rs
- end
- local rl=rs[language]
- if not rl then
- rl={
- }
- rs[language]=rl
- local sequences=tfmdata.resources.sequences
- for s=1,#sequences do
- local v=enabled and initialize(sequences[s],script,language,enabled)
- if v then
- rl[#rl+1]=v
- end
- end
- end
- return rl
-end
-local function featuresprocessor(head,font,attr)
- local lookuphash=lookuphashes[font]
- if not lookuphash then
- return head,false
- end
- if trace_steps then
- checkstep(head)
- end
- tfmdata=fontdata[font]
- descriptions=tfmdata.descriptions
- characters=tfmdata.characters
- resources=tfmdata.resources
- marks=resources.marks
- anchorlookups=resources.lookup_to_anchor
- lookuptable=resources.lookups
- lookuptypes=resources.lookuptypes
- currentfont=font
- rlmode=0
- local sequences=resources.sequences
- local done=false
- local datasets=otf.dataset(tfmdata,font,attr)
- local dirstack={}
- for s=1,#datasets do
- local dataset=datasets[s]
- featurevalue=dataset[1]
- local sequence=dataset[5]
- local rlparmode=0
- local topstack=0
- local success=false
- local attribute=dataset[2]
- local chain=dataset[3]
- local typ=sequence.type
- local subtables=sequence.subtables
- if chain<0 then
- local handler=handlers[typ]
- local start=find_node_tail(head)
- while start do
- local id=start.id
- if id==glyph_code then
- if start.font==font and start.subtype<256 then
- local a=start[0]
- if a then
- a=a==attr
- else
- a=true
- end
- if a then
- for i=1,#subtables do
- local lookupname=subtables[i]
- local lookupcache=lookuphash[lookupname]
- if lookupcache then
- local lookupmatch=lookupcache[start.char]
- if lookupmatch then
- head,start,success=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
- if success then
- break
- end
- end
- else
- report_missing_cache(typ,lookupname)
- end
- end
- if start then start=start.prev end
- else
- start=start.prev
- end
- else
- start=start.prev
- end
- else
- start=start.prev
- end
- end
- else
- local handler=handlers[typ]
- local ns=#subtables
- local start=head
- rlmode=0
- if ns==1 then
- local lookupname=subtables[1]
- local lookupcache=lookuphash[lookupname]
- if not lookupcache then
- report_missing_cache(typ,lookupname)
- else
- local function subrun(start)
- local head=start
- local done=false
- while start do
- local id=start.id
- if id==glyph_code and start.font==font and start.subtype<256 then
- local a=start[0]
- if a then
- a=(a==attr) and (not attribute or start[a_state]==attribute)
- else
- a=not attribute or start[a_state]==attribute
- end
- if a then
- local lookupmatch=lookupcache[start.char]
- if lookupmatch then
- local ok
- head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
- if ok then
- done=true
- end
- end
- if start then start=start.next end
- else
- start=start.next
- end
- else
- start=start.next
- end
- end
- if done then
- success=true
- return head
- end
- end
- local function kerndisc(disc)
- local prev=disc.prev
- local next=disc.next
- if prev and next then
- prev.next=next
- local a=prev[0]
- if a then
- a=(a==attr) and (not attribute or prev[a_state]==attribute)
- else
- a=not attribute or prev[a_state]==attribute
- end
- if a then
- local lookupmatch=lookupcache[prev.char]
- if lookupmatch then
- local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
- if ok then
- done=true
- success=true
- end
- end
- end
- prev.next=disc
- end
- return next
- end
- while start do
- local id=start.id
- if id==glyph_code then
- if start.font==font and start.subtype<256 then
- local a=start[0]
- if a then
- a=(a==attr) and (not attribute or start[a_state]==attribute)
- else
- a=not attribute or start[a_state]==attribute
- end
- if a then
- local lookupmatch=lookupcache[start.char]
- if lookupmatch then
- local ok
- head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
- if ok then
- success=true
- end
- end
- if start then start=start.next end
- else
- start=start.next
- end
- else
- start=start.next
- end
- elseif id==disc_code then
- if start.subtype==discretionary_code then
- local pre=start.pre
- if pre then
- local new=subrun(pre)
- if new then start.pre=new end
- end
- local post=start.post
- if post then
- local new=subrun(post)
- if new then start.post=new end
- end
- local replace=start.replace
- if replace then
- local new=subrun(replace)
- if new then start.replace=new end
- end
-elseif typ=="gpos_single" or typ=="gpos_pair" then
- kerndisc(start)
- end
- start=start.next
- elseif id==whatsit_code then
- local subtype=start.subtype
- if subtype==dir_code then
- local dir=start.dir
- if dir=="+TRT" or dir=="+TLT" then
- topstack=topstack+1
- dirstack[topstack]=dir
- elseif dir=="-TRT" or dir=="-TLT" then
- topstack=topstack-1
- end
- local newdir=dirstack[topstack]
- if newdir=="+TRT" then
- rlmode=-1
- elseif newdir=="+TLT" then
- rlmode=1
- else
- rlmode=rlparmode
- end
- if trace_directions then
- report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir)
- end
- elseif subtype==localpar_code then
- local dir=start.dir
- if dir=="TRT" then
- rlparmode=-1
- elseif dir=="TLT" then
- rlparmode=1
- else
- rlparmode=0
- end
- rlmode=rlparmode
- if trace_directions then
- report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode)
- end
- end
- start=start.next
- elseif id==math_code then
- start=end_of_math(start).next
- else
- start=start.next
- end
- end
- end
- else
- local function subrun(start)
- local head=start
- local done=false
- while start do
- local id=start.id
- if id==glyph_code and start.id==font and start.subtype<256 then
- local a=start[0]
- if a then
- a=(a==attr) and (not attribute or start[a_state]==attribute)
- else
- a=not attribute or start[a_state]==attribute
- end
- if a then
- for i=1,ns do
- local lookupname=subtables[i]
- local lookupcache=lookuphash[lookupname]
- if lookupcache then
- local lookupmatch=lookupcache[start.char]
- if lookupmatch then
- local ok
- head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
- if ok then
- done=true
- break
- elseif not start then
- break
- end
- end
- else
- report_missing_cache(typ,lookupname)
- end
- end
- if start then start=start.next end
- else
- start=start.next
- end
- else
- start=start.next
- end
- end
- if done then
- success=true
- return head
- end
- end
- local function kerndisc(disc)
- local prev=disc.prev
- local next=disc.next
- if prev and next then
- prev.next=next
- local a=prev[0]
- if a then
- a=(a==attr) and (not attribute or prev[a_state]==attribute)
- else
- a=not attribute or prev[a_state]==attribute
- end
- if a then
- for i=1,ns do
- local lookupname=subtables[i]
- local lookupcache=lookuphash[lookupname]
- if lookupcache then
- local lookupmatch=lookupcache[prev.char]
- if lookupmatch then
- local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
- if ok then
- done=true
- break
- end
- end
- else
- report_missing_cache(typ,lookupname)
- end
- end
- end
- prev.next=disc
- end
- return next
- end
- while start do
- local id=start.id
- if id==glyph_code then
- if start.font==font and start.subtype<256 then
- local a=start[0]
- if a then
- a=(a==attr) and (not attribute or start[a_state]==attribute)
- else
- a=not attribute or start[a_state]==attribute
- end
- if a then
- for i=1,ns do
- local lookupname=subtables[i]
- local lookupcache=lookuphash[lookupname]
- if lookupcache then
- local lookupmatch=lookupcache[start.char]
- if lookupmatch then
- local ok
- head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
- if ok then
- success=true
- break
- elseif not start then
- break
- end
- end
- else
- report_missing_cache(typ,lookupname)
- end
- end
- if start then start=start.next end
- else
- start=start.next
- end
- else
- start=start.next
- end
- elseif id==disc_code then
- if start.subtype==discretionary_code then
- local pre=start.pre
- if pre then
- local new=subrun(pre)
- if new then start.pre=new end
- end
- local post=start.post
- if post then
- local new=subrun(post)
- if new then start.post=new end
- end
- local replace=start.replace
- if replace then
- local new=subrun(replace)
- if new then start.replace=new end
- end
-elseif typ=="gpos_single" or typ=="gpos_pair" then
- kerndisc(start)
- end
- start=start.next
- elseif id==whatsit_code then
- local subtype=start.subtype
- if subtype==dir_code then
- local dir=start.dir
- if dir=="+TRT" or dir=="+TLT" then
- topstack=topstack+1
- dirstack[topstack]=dir
- elseif dir=="-TRT" or dir=="-TLT" then
- topstack=topstack-1
- end
- local newdir=dirstack[topstack]
- if newdir=="+TRT" then
- rlmode=-1
- elseif newdir=="+TLT" then
- rlmode=1
- else
- rlmode=rlparmode
- end
- if trace_directions then
- report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir)
- end
- elseif subtype==localpar_code then
- local dir=start.dir
- if dir=="TRT" then
- rlparmode=-1
- elseif dir=="TLT" then
- rlparmode=1
- else
- rlparmode=0
- end
- rlmode=rlparmode
- if trace_directions then
- report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode)
- end
- end
- start=start.next
- elseif id==math_code then
- start=end_of_math(start).next
- else
- start=start.next
- end
- end
- end
- end
- if success then
- done=true
- end
- if trace_steps then
- registerstep(head)
- end
- end
- return head,done
-end
-local function generic(lookupdata,lookupname,unicode,lookuphash)
- local target=lookuphash[lookupname]
- if target then
- target[unicode]=lookupdata
- else
- lookuphash[lookupname]={ [unicode]=lookupdata }
- end
-end
-local action={
- substitution=generic,
- multiple=generic,
- alternate=generic,
- position=generic,
- ligature=function(lookupdata,lookupname,unicode,lookuphash)
- local target=lookuphash[lookupname]
- if not target then
- target={}
- lookuphash[lookupname]=target
- end
- for i=1,#lookupdata do
- local li=lookupdata[i]
- local tu=target[li]
- if not tu then
- tu={}
- target[li]=tu
- end
- target=tu
- end
- target.ligature=unicode
- end,
- pair=function(lookupdata,lookupname,unicode,lookuphash)
- local target=lookuphash[lookupname]
- if not target then
- target={}
- lookuphash[lookupname]=target
- end
- local others=target[unicode]
- local paired=lookupdata[1]
- if others then
- others[paired]=lookupdata
- else
- others={ [paired]=lookupdata }
- target[unicode]=others
- end
- end,
-}
-local function prepare_lookups(tfmdata)
- local rawdata=tfmdata.shared.rawdata
- local resources=rawdata.resources
- local lookuphash=resources.lookuphash
- local anchor_to_lookup=resources.anchor_to_lookup
- local lookup_to_anchor=resources.lookup_to_anchor
- local lookuptypes=resources.lookuptypes
- local characters=tfmdata.characters
- local descriptions=tfmdata.descriptions
- for unicode,character in next,characters do
- local description=descriptions[unicode]
- if description then
- local lookups=description.slookups
- if lookups then
- for lookupname,lookupdata in next,lookups do
- action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash)
- end
- end
- local lookups=description.mlookups
- if lookups then
- for lookupname,lookuplist in next,lookups do
- local lookuptype=lookuptypes[lookupname]
- for l=1,#lookuplist do
- local lookupdata=lookuplist[l]
- action[lookuptype](lookupdata,lookupname,unicode,lookuphash)
- end
- end
- end
- local list=description.kerns
- if list then
- for lookup,krn in next,list do
- local target=lookuphash[lookup]
- if target then
- target[unicode]=krn
- else
- lookuphash[lookup]={ [unicode]=krn }
- end
- end
- end
- local list=description.anchors
- if list then
- for typ,anchors in next,list do
- if typ=="mark" or typ=="cexit" then
- for name,anchor in next,anchors do
- local lookups=anchor_to_lookup[name]
- if lookups then
- for lookup,_ in next,lookups do
- local target=lookuphash[lookup]
- if target then
- target[unicode]=anchors
- else
- lookuphash[lookup]={ [unicode]=anchors }
- end
- end
- end
- end
- end
- end
- end
- end
- end
-end
-local function split(replacement,original)
- local result={}
- for i=1,#replacement do
- result[original[i]]=replacement[i]
- end
- return result
-end
-local valid={
- coverage={ chainsub=true,chainpos=true,contextsub=true },
- reversecoverage={ reversesub=true },
- glyphs={ chainsub=true,chainpos=true },
-}
-local function prepare_contextchains(tfmdata)
- local rawdata=tfmdata.shared.rawdata
- local resources=rawdata.resources
- local lookuphash=resources.lookuphash
- local lookups=rawdata.lookups
- if lookups then
- for lookupname,lookupdata in next,rawdata.lookups do
- local lookuptype=lookupdata.type
- if lookuptype then
- local rules=lookupdata.rules
- if rules then
- local format=lookupdata.format
- local validformat=valid[format]
- if not validformat then
- report_prepare("unsupported format %a",format)
- elseif not validformat[lookuptype] then
- report_prepare("unsupported format %a, lookuptype %a, lookupname %a",format,lookuptype,lookupname)
- else
- local contexts=lookuphash[lookupname]
- if not contexts then
- contexts={}
- lookuphash[lookupname]=contexts
- end
- local t,nt={},0
- for nofrules=1,#rules do
- local rule=rules[nofrules]
- local current=rule.current
- local before=rule.before
- local after=rule.after
- local replacements=rule.replacements
- local sequence={}
- local nofsequences=0
- if before then
- for n=1,#before do
- nofsequences=nofsequences+1
- sequence[nofsequences]=before[n]
- end
- end
- local start=nofsequences+1
- for n=1,#current do
- nofsequences=nofsequences+1
- sequence[nofsequences]=current[n]
- end
- local stop=nofsequences
- if after then
- for n=1,#after do
- nofsequences=nofsequences+1
- sequence[nofsequences]=after[n]
- end
- end
- if sequence[1] then
- nt=nt+1
- t[nt]={ nofrules,lookuptype,sequence,start,stop,rule.lookups,replacements }
- for unic,_ in next,sequence[start] do
- local cu=contexts[unic]
- if not cu then
- contexts[unic]=t
- end
- end
- end
- end
- end
- else
- end
- else
- report_prepare("missing lookuptype for lookupname %a",lookupname)
- end
- end
- end
-end
-local function featuresinitializer(tfmdata,value)
- if true then
- local rawdata=tfmdata.shared.rawdata
- local properties=rawdata.properties
- if not properties.initialized then
- local starttime=trace_preparing and os.clock()
- local resources=rawdata.resources
- resources.lookuphash=resources.lookuphash or {}
- prepare_contextchains(tfmdata)
- prepare_lookups(tfmdata)
- properties.initialized=true
- if trace_preparing then
- report_prepare("preparation time is %0.3f seconds for %a",os.clock()-starttime,tfmdata.properties.fullname)
- end
- end
- end
-end
-registerotffeature {
- name="features",
- description="features",
- default=true,
- initializers={
- position=1,
- node=featuresinitializer,
- },
- processors={
- node=featuresprocessor,
- }
-}
-otf.handlers=handlers
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-otp']={
- version=1.001,
- comment="companion to font-otf.lua (packing)",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local next,type=next,type
-local sort,concat=table.sort,table.concat
-local sortedhash=table.sortedhash
-local trace_packing=false trackers.register("otf.packing",function(v) trace_packing=v end)
-local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end)
-local report_otf=logs.reporter("fonts","otf loading")
-fonts=fonts or {}
-local handlers=fonts.handlers or {}
-fonts.handlers=handlers
-local otf=handlers.otf or {}
-handlers.otf=otf
-local enhancers=otf.enhancers or {}
-otf.enhancers=enhancers
-local glists=otf.glists or { "gsub","gpos" }
-otf.glists=glists
-local criterium=1
-local threshold=0
-local function tabstr_normal(t)
- local s={}
- local n=0
- for k,v in next,t do
- n=n+1
- if type(v)=="table" then
- s[n]=k..">"..tabstr_normal(v)
- elseif v==true then
- s[n]=k.."+"
- elseif v then
- s[n]=k.."="..v
- else
- s[n]=k.."-"
- end
- end
- if n==0 then
- return ""
- elseif n==1 then
- return s[1]
- else
- sort(s)
- return concat(s,",")
- end
-end
-local function tabstr_flat(t)
- local s={}
- local n=0
- for k,v in next,t do
- n=n+1
- s[n]=k.."="..v
- end
- if n==0 then
- return ""
- elseif n==1 then
- return s[1]
- else
- sort(s)
- return concat(s,",")
- end
-end
-local function tabstr_mixed(t)
- local s={}
- local n=#t
- if n==0 then
- return ""
- elseif n==1 then
- local k=t[1]
- if k==true then
- return "++"
- elseif k==false then
- return "--"
- else
- return tostring(k)
- end
- else
- for i=1,n do
- local k=t[i]
- if k==true then
- s[i]="++"
- elseif k==false then
- s[i]="--"
- else
- s[i]=k
- end
- end
- return concat(s,",")
- end
-end
-local function tabstr_boolean(t)
- local s={}
- local n=0
- for k,v in next,t do
- n=n+1
- if v then
- s[n]=k.."+"
- else
- s[n]=k.."-"
- end
- end
- if n==0 then
- return ""
- elseif n==1 then
- return s[1]
- else
- sort(s)
- return concat(s,",")
- end
-end
-local function packdata(data)
- if data then
- local h,t,c={},{},{}
- local hh,tt,cc={},{},{}
- local nt,ntt=0,0
- local function pack_normal(v)
- local tag=tabstr_normal(v)
- local ht=h[tag]
- if ht then
- c[ht]=c[ht]+1
- return ht
- else
- nt=nt+1
- t[nt]=v
- h[tag]=nt
- c[nt]=1
- return nt
- end
- end
- local function pack_flat(v)
- local tag=tabstr_flat(v)
- local ht=h[tag]
- if ht then
- c[ht]=c[ht]+1
- return ht
- else
- nt=nt+1
- t[nt]=v
- h[tag]=nt
- c[nt]=1
- return nt
- end
- end
- local function pack_boolean(v)
- local tag=tabstr_boolean(v)
- local ht=h[tag]
- if ht then
- c[ht]=c[ht]+1
- return ht
- else
- nt=nt+1
- t[nt]=v
- h[tag]=nt
- c[nt]=1
- return nt
- end
- end
- local function pack_indexed(v)
- local tag=concat(v," ")
- local ht=h[tag]
- if ht then
- c[ht]=c[ht]+1
- return ht
- else
- nt=nt+1
- t[nt]=v
- h[tag]=nt
- c[nt]=1
- return nt
- end
- end
- local function pack_mixed(v)
- local tag=tabstr_mixed(v)
- local ht=h[tag]
- if ht then
- c[ht]=c[ht]+1
- return ht
- else
- nt=nt+1
- t[nt]=v
- h[tag]=nt
- c[nt]=1
- return nt
- end
- end
- local function pack_final(v)
- if c[v]<=criterium then
- return t[v]
- else
- local hv=hh[v]
- if hv then
- return hv
- else
- ntt=ntt+1
- tt[ntt]=t[v]
- hh[v]=ntt
- cc[ntt]=c[v]
- return ntt
- end
- end
- end
- local function success(stage,pass)
- if nt==0 then
- if trace_loading or trace_packing then
- report_otf("pack quality: nothing to pack")
- end
- return false
- elseif nt>=threshold then
- local one,two,rest=0,0,0
- if pass==1 then
- for k,v in next,c do
- if v==1 then
- one=one+1
- elseif v==2 then
- two=two+1
- else
- rest=rest+1
- end
- end
- else
- for k,v in next,cc do
- if v>20 then
- rest=rest+1
- elseif v>10 then
- two=two+1
- else
- one=one+1
- end
- end
- data.tables=tt
- end
- if trace_loading or trace_packing then
- report_otf("pack quality: stage %s, pass %s, %s packed, 1-10:%s, 11-20:%s, rest:%s (criterium: %s)",stage,pass,one+two+rest,one,two,rest,criterium)
- end
- return true
- else
- if trace_loading or trace_packing then
- report_otf("pack quality: stage %s, pass %s, %s packed, aborting pack (threshold: %s)",stage,pass,nt,threshold)
- end
- return false
- end
- end
- local function packers(pass)
- if pass==1 then
- return pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed
- else
- return pack_final,pack_final,pack_final,pack_final,pack_final
- end
- end
- local resources=data.resources
- local lookuptypes=resources.lookuptypes
- for pass=1,2 do
- if trace_packing then
- report_otf("start packing: stage 1, pass %s",pass)
- end
- local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass)
- for unicode,description in next,data.descriptions do
- local boundingbox=description.boundingbox
- if boundingbox then
- description.boundingbox=pack_indexed(boundingbox)
- end
- local slookups=description.slookups
- if slookups then
- for tag,slookup in next,slookups do
- local what=lookuptypes[tag]
- if what=="pair" then
- local t=slookup[2] if t then slookup[2]=pack_indexed(t) end
- local t=slookup[3] if t then slookup[3]=pack_indexed(t) end
- elseif what~="substitution" then
- slookups[tag]=pack_indexed(slookup)
- end
- end
- end
- local mlookups=description.mlookups
- if mlookups then
- for tag,mlookup in next,mlookups do
- local what=lookuptypes[tag]
- if what=="pair" then
- for i=1,#mlookup do
- local lookup=mlookup[i]
- local t=lookup[2] if t then lookup[2]=pack_indexed(t) end
- local t=lookup[3] if t then lookup[3]=pack_indexed(t) end
- end
- elseif what~="substitution" then
- for i=1,#mlookup do
- mlookup[i]=pack_indexed(mlookup[i])
- end
- end
- end
- end
- local kerns=description.kerns
- if kerns then
- for tag,kern in next,kerns do
- kerns[tag]=pack_flat(kern)
- end
- end
- local math=description.math
- if math then
- local kerns=math.kerns
- if kerns then
- for tag,kern in next,kerns do
- kerns[tag]=pack_normal(kern)
- end
- end
- end
- local anchors=description.anchors
- if anchors then
- for what,anchor in next,anchors do
- if what=="baselig" then
- for _,a in next,anchor do
- for k=1,#a do
- a[k]=pack_indexed(a[k])
- end
- end
- else
- for k,v in next,anchor do
- anchor[k]=pack_indexed(v)
- end
- end
- end
- end
- local altuni=description.altuni
- if altuni then
- for i=1,#altuni do
- altuni[i]=pack_flat(altuni[i])
- end
- end
- end
- local lookups=data.lookups
- if lookups then
- for _,lookup in next,lookups do
- local rules=lookup.rules
- if rules then
- for i=1,#rules do
- local rule=rules[i]
- local r=rule.before if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end
- local r=rule.after if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end
- local r=rule.current if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end
- local r=rule.replacements if r then rule.replacements=pack_flat (r) end
- local r=rule.lookups if r then rule.lookups=pack_indexed(r) end
- end
- end
- end
- end
- local anchor_to_lookup=resources.anchor_to_lookup
- if anchor_to_lookup then
- for anchor,lookup in next,anchor_to_lookup do
- anchor_to_lookup[anchor]=pack_normal(lookup)
- end
- end
- local lookup_to_anchor=resources.lookup_to_anchor
- if lookup_to_anchor then
- for lookup,anchor in next,lookup_to_anchor do
- lookup_to_anchor[lookup]=pack_normal(anchor)
- end
- end
- local sequences=resources.sequences
- if sequences then
- for feature,sequence in next,sequences do
- local flags=sequence.flags
- if flags then
- sequence.flags=pack_normal(flags)
- end
- local subtables=sequence.subtables
- if subtables then
- sequence.subtables=pack_normal(subtables)
- end
- local features=sequence.features
- if features then
- for script,feature in next,features do
- features[script]=pack_normal(feature)
- end
- end
- local order=sequence.order
- if order then
- sequence.order=pack_indexed(order)
- end
- local markclass=sequence.markclass
- if markclass then
- sequence.markclass=pack_boolean(markclass)
- end
- end
- end
- local lookups=resources.lookups
- if lookups then
- for name,lookup in next,lookups do
- local flags=lookup.flags
- if flags then
- lookup.flags=pack_normal(flags)
- end
- local subtables=lookup.subtables
- if subtables then
- lookup.subtables=pack_normal(subtables)
- end
- end
- end
- local features=resources.features
- if features then
- for _,what in next,glists do
- local list=features[what]
- if list then
- for feature,spec in next,list do
- list[feature]=pack_normal(spec)
- end
- end
- end
- end
- if not success(1,pass) then
- return
- end
- end
- if nt>0 then
- for pass=1,2 do
- if trace_packing then
- report_otf("start packing: stage 2, pass %s",pass)
- end
- local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass)
- for unicode,description in next,data.descriptions do
- local kerns=description.kerns
- if kerns then
- description.kerns=pack_normal(kerns)
- end
- local math=description.math
- if math then
- local kerns=math.kerns
- if kerns then
- math.kerns=pack_normal(kerns)
- end
- end
- local anchors=description.anchors
- if anchors then
- description.anchors=pack_normal(anchors)
- end
- local mlookups=description.mlookups
- if mlookups then
- for tag,mlookup in next,mlookups do
- mlookups[tag]=pack_normal(mlookup)
- end
- end
- local altuni=description.altuni
- if altuni then
- description.altuni=pack_normal(altuni)
- end
- end
- local lookups=data.lookups
- if lookups then
- for _,lookup in next,lookups do
- local rules=lookup.rules
- if rules then
- for i=1,#rules do
- local rule=rules[i]
- local r=rule.before if r then rule.before=pack_normal(r) end
- local r=rule.after if r then rule.after=pack_normal(r) end
- local r=rule.current if r then rule.current=pack_normal(r) end
- end
- end
- end
- end
- local sequences=resources.sequences
- if sequences then
- for feature,sequence in next,sequences do
- sequence.features=pack_normal(sequence.features)
- end
- end
- if not success(2,pass) then
- end
- end
- for pass=1,2 do
- local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass)
- for unicode,description in next,data.descriptions do
- local slookups=description.slookups
- if slookups then
- description.slookups=pack_normal(slookups)
- end
- local mlookups=description.mlookups
- if mlookups then
- description.mlookups=pack_normal(mlookups)
- end
- end
- end
- end
- end
-end
-local unpacked_mt={
- __index=function(t,k)
- t[k]=false
- return k
- end
-}
-local function unpackdata(data)
- if data then
- local tables=data.tables
- if tables then
- local resources=data.resources
- local lookuptypes=resources.lookuptypes
- local unpacked={}
- setmetatable(unpacked,unpacked_mt)
- for unicode,description in next,data.descriptions do
- local tv=tables[description.boundingbox]
- if tv then
- description.boundingbox=tv
- end
- local slookups=description.slookups
- if slookups then
- local tv=tables[slookups]
- if tv then
- description.slookups=tv
- slookups=unpacked[tv]
- end
- if slookups then
- for tag,lookup in next,slookups do
- local what=lookuptypes[tag]
- if what=="pair" then
- local tv=tables[lookup[2]]
- if tv then
- lookup[2]=tv
- end
- local tv=tables[lookup[3]]
- if tv then
- lookup[3]=tv
- end
- elseif what~="substitution" then
- local tv=tables[lookup]
- if tv then
- slookups[tag]=tv
- end
- end
- end
- end
- end
- local mlookups=description.mlookups
- if mlookups then
- local tv=tables[mlookups]
- if tv then
- description.mlookups=tv
- mlookups=unpacked[tv]
- end
- if mlookups then
- for tag,list in next,mlookups do
- local tv=tables[list]
- if tv then
- mlookups[tag]=tv
- list=unpacked[tv]
- end
- if list then
- local what=lookuptypes[tag]
- if what=="pair" then
- for i=1,#list do
- local lookup=list[i]
- local tv=tables[lookup[2]]
- if tv then
- lookup[2]=tv
- end
- local tv=tables[lookup[3]]
- if tv then
- lookup[3]=tv
- end
- end
- elseif what~="substitution" then
- for i=1,#list do
- local tv=tables[list[i]]
- if tv then
- list[i]=tv
- end
- end
- end
- end
- end
- end
- end
- local kerns=description.kerns
- if kerns then
- local tm=tables[kerns]
- if tm then
- description.kerns=tm
- kerns=unpacked[tm]
- end
- if kerns then
- for k,kern in next,kerns do
- local tv=tables[kern]
- if tv then
- kerns[k]=tv
- end
- end
- end
- end
- local math=description.math
- if math then
- local kerns=math.kerns
- if kerns then
- local tm=tables[kerns]
- if tm then
- math.kerns=tm
- kerns=unpacked[tm]
- end
- if kerns then
- for k,kern in next,kerns do
- local tv=tables[kern]
- if tv then
- kerns[k]=tv
- end
- end
- end
- end
- end
- local anchors=description.anchors
- if anchors then
- local ta=tables[anchors]
- if ta then
- description.anchors=ta
- anchors=unpacked[ta]
- end
- if anchors then
- for tag,anchor in next,anchors do
- if tag=="baselig" then
- for _,list in next,anchor do
- for i=1,#list do
- local tv=tables[list[i]]
- if tv then
- list[i]=tv
- end
- end
- end
- else
- for a,data in next,anchor do
- local tv=tables[data]
- if tv then
- anchor[a]=tv
- end
- end
- end
- end
- end
- end
- local altuni=description.altuni
- if altuni then
- local altuni=tables[altuni]
- if altuni then
- description.altuni=altuni
- for i=1,#altuni do
- local tv=tables[altuni[i]]
- if tv then
- altuni[i]=tv
- end
- end
- end
- end
- end
- local lookups=data.lookups
- if lookups then
- for _,lookup in next,lookups do
- local rules=lookup.rules
- if rules then
- for i=1,#rules do
- local rule=rules[i]
- local before=rule.before
- if before then
- local tv=tables[before]
- if tv then
- rule.before=tv
- before=unpacked[tv]
- end
- if before then
- for i=1,#before do
- local tv=tables[before[i]]
- if tv then
- before[i]=tv
- end
- end
- end
- end
- local after=rule.after
- if after then
- local tv=tables[after]
- if tv then
- rule.after=tv
- after=unpacked[tv]
- end
- if after then
- for i=1,#after do
- local tv=tables[after[i]]
- if tv then
- after[i]=tv
- end
- end
- end
- end
- local current=rule.current
- if current then
- local tv=tables[current]
- if tv then
- rule.current=tv
- current=unpacked[tv]
- end
- if current then
- for i=1,#current do
- local tv=tables[current[i]]
- if tv then
- current[i]=tv
- end
- end
- end
- end
- local replacements=rule.replacements
- if replacements then
- local tv=tables[replacements]
- if tv then
- rule.replacements=tv
- end
- end
- local lookups=rule.lookups
- if lookups then
- local tv=tables[lookups]
- if tv then
- rule.lookups=tv
- end
- end
- end
- end
- end
- end
- local anchor_to_lookup=resources.anchor_to_lookup
- if anchor_to_lookup then
- for anchor,lookup in next,anchor_to_lookup do
- local tv=tables[lookup]
- if tv then
- anchor_to_lookup[anchor]=tv
- end
- end
- end
- local lookup_to_anchor=resources.lookup_to_anchor
- if lookup_to_anchor then
- for lookup,anchor in next,lookup_to_anchor do
- local tv=tables[anchor]
- if tv then
- lookup_to_anchor[lookup]=tv
- end
- end
- end
- local ls=resources.sequences
- if ls then
- for _,feature in next,ls do
- local flags=feature.flags
- if flags then
- local tv=tables[flags]
- if tv then
- feature.flags=tv
- end
- end
- local subtables=feature.subtables
- if subtables then
- local tv=tables[subtables]
- if tv then
- feature.subtables=tv
- end
- end
- local features=feature.features
- if features then
- local tv=tables[features]
- if tv then
- feature.features=tv
- features=unpacked[tv]
- end
- if features then
- for script,data in next,features do
- local tv=tables[data]
- if tv then
- features[script]=tv
- end
- end
- end
- end
- local order=feature.order
- if order then
- local tv=tables[order]
- if tv then
- feature.order=tv
- end
- end
- local markclass=feature.markclass
- if markclass then
- local tv=tables[markclass]
- if tv then
- feature.markclass=tv
- end
- end
- end
- end
- local lookups=resources.lookups
- if lookups then
- for _,lookup in next,lookups do
- local flags=lookup.flags
- if flags then
- local tv=tables[flags]
- if tv then
- lookup.flags=tv
- end
- end
- local subtables=lookup.subtables
- if subtables then
- local tv=tables[subtables]
- if tv then
- lookup.subtables=tv
- end
- end
- end
- end
- local features=resources.features
- if features then
- for _,what in next,glists do
- local feature=features[what]
- if feature then
- for tag,spec in next,feature do
- local tv=tables[spec]
- if tv then
- feature[tag]=tv
- end
- end
- end
- end
- end
- data.tables=nil
- end
- end
-end
-if otf.enhancers.register then
- otf.enhancers.register("pack",packdata)
- otf.enhancers.register("unpack",unpackdata)
-end
-otf.enhancers.unpack=unpackdata
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['luatex-fonts-lua']={
- version=1.001,
- comment="companion to luatex-*.tex",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-if context then
- texio.write_nl("fatal error: this module is not for context")
- os.exit()
-end
-local fonts=fonts
-fonts.formats.lua="lua"
-function fonts.readers.lua(specification)
- local fullname=specification.filename or ""
- if fullname=="" then
- local forced=specification.forced or ""
- if forced~="" then
- fullname=specification.name.."."..forced
- else
- fullname=specification.name
- end
- end
- local fullname=resolvers.findfile(fullname) or ""
- if fullname~="" then
- local loader=loadfile(fullname)
- loader=loader and loader()
- return loader and loader(specification)
- end
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-def']={
- version=1.001,
- comment="companion to font-ini.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local format,gmatch,match,find,lower,gsub=string.format,string.gmatch,string.match,string.find,string.lower,string.gsub
-local tostring,next=tostring,next
-local lpegmatch=lpeg.match
-local suffixonly,removesuffix=file.suffix,file.removesuffix
-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")
-trackers.register("fonts.all","fonts.*","otf.*","afm.*","tfm.*")
-local report_defining=logs.reporter("fonts","defining")
-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' }
-local variants=allocate()
-specifiers.variants=variants
-definers.methods=definers.methods or {}
-local internalized=allocate()
-local lastdefined=nil
-local loadedfonts=constructors.loadedfonts
-local designsizes=constructors.designsizes
-local resolvefile=fontgoodies and fontgoodies.filenames and fontgoodies.filenames.resolve or function(s) return s end
-local splitter,splitspecifiers=nil,""
-local P,C,S,Cc=lpeg.P,lpeg.C,lpeg.S,lpeg.Cc
-local left=P("(")
-local right=P(")")
-local colon=P(":")
-local space=P(" ")
-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=C((1-sub-specification)^1)
- splitter=P((lookup+Cc(""))*name*(sub+Cc(""))*(specification+Cc("")))
-end
-local function addlookup(str,default)
- prefixpattern=prefixpattern+P(str)
-end
-definers.addlookup=addlookup
-addlookup("file")
-addlookup("name")
-addlookup("spec")
-local function getspecification(str)
- return lpegmatch(splitter,str or "")
-end
-definers.getspecification=getspecification
-function definers.registersplit(symbol,action,verbosename)
- addspecifier(symbol)
- variants[symbol]=action
- if verbosename then
- variants[verbosename]=action
- end
-end
-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,
- specification=specification,
- size=size,
- name=name,
- sub=sub,
- method=method,
- detail=detail,
- resolved="",
- forced="",
- features={},
- }
- return t
-end
-definers.makespecification=makespecification
-function definers.analyze(specification,size)
- local lookup,name,sub,method,detail=getspecification(specification or "")
- return makespecification(specification,lookup,name,sub,method,detail,size)
-end
-definers.resolvers=definers.resolvers or {}
-local resolvers=definers.resolvers
-function resolvers.file(specification)
- local name=resolvefile(specification.name)
- local suffix=lower(suffixonly(name))
- if fonts.formats[suffix] then
- specification.forced=suffix
- specification.forcedname=name
- specification.name=removesuffix(name)
- else
- specification.name=name
- end
-end
-function resolvers.name(specification)
- local resolve=fonts.names.resolve
- if resolve then
- local resolved,sub=resolve(specification.name,specification.sub,specification)
- if resolved then
- specification.resolved=resolved
- specification.sub=sub
- 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=resolvespec(specification.name,specification.sub,specification)
- if resolved then
- specification.resolved=resolved
- specification.sub=sub
- 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
- 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
-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)
- if type(extrahash)=="string" and extrahash~="" then
- extrahash=gsub(lower(extrahash),"[^a-z]","-")
- properties.fullname=format("%s-%s",properties.fullname,extrahash)
- end
- end
- end
- return tfmdata
-end
-local function checkembedding(tfmdata)
- local properties=tfmdata.properties
- local embedding
- if directive_embedall then
- embedding="full"
- elseif properties and properties.filename and constructors.dontembed[properties.filename] then
- embedding="no"
- else
- embedding="subset"
- end
- if properties then
- properties.embedding=embedding
- else
- tfmdata.properties={ embedding=embedding }
- end
- tfmdata.embedding=embedding
-end
-function definers.loadfont(specification)
- local hash=constructors.hashinstance(specification)
- local tfmdata=loadedfonts[hash]
- if not tfmdata then
- local forced=specification.forced or ""
- if forced~="" then
- local reader=readers[lower(forced)]
- 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
- for s=1,#sequence do
- local reader=sequence[s]
- if readers[reader] then
- 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)
- checkembedding(tfmdata)
- loadedfonts[hash]=tfmdata
- designsizes[specification.hash]=tfmdata.parameters.designsize
- 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.checkvirtualids()
-end
-function constructors.readanddefine(name,size)
- 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
- constructors.checkvirtualids(tfmdata)
- id=font.define(tfmdata)
- definers.register(tfmdata,id)
- else
- id=0
- end
- end
- return fontdata[id],id
-end
-function definers.current()
- return lastdefined
-end
-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)
- 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)
- if tfmdata then
- if trace_defining then
- report_defining("already hashed: %s",hash)
- end
- else
- tfmdata=definers.loadfont(specification)
- if tfmdata then
- 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
- lastdefined=tfmdata or id
- if not tfmdata then
- 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 %s font with id %a, name %a, size %a, bytes %a, encoding %a, fullname %a, filename %a",
- properties.format,id,properties.name,parameters.size,properties.encodingbytes,
- properties.encodingname,properties.fullname,file.basename(properties.filename))
- end
- statistics.stoptiming(fonts)
- return tfmdata
-end
-function font.getfont(id)
- return fontdata[id]
-end
-callbacks.register('define_font',definers.read,"definition of fonts (tfmdata preparation)")
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['luatex-font-def']={
- version=1.001,
- comment="companion to luatex-*.tex",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-if context then
- texio.write_nl("fatal error: this module is not for context")
- os.exit()
-end
-local fonts=fonts
-fonts.constructors.namemode="specification"
-function fonts.definers.getspecification(str)
- return "",str,"",":",str
-end
-local list={}
-local function issome () list.lookup='name' end
-local function isfile () list.lookup='file' end
-local function isname () list.lookup='name' end
-local function thename(s) list.name=s end
-local function issub (v) list.sub=v end
-local function iscrap (s) list.crap=string.lower(s) end
-local function iskey (k,v) list[k]=v end
-local function istrue (s) list[s]=true end
-local function isfalse(s) list[s]=false end
-local P,S,R,C=lpeg.P,lpeg.S,lpeg.R,lpeg.C
-local spaces=P(" ")^0
-local namespec=(1-S("/:("))^0
-local crapspec=spaces*P("/")*(((1-P(":"))^0)/iscrap)*spaces
-local filename_1=P("file:")/isfile*(namespec/thename)
-local filename_2=P("[")*P(true)/isname*(((1-P("]"))^0)/thename)*P("]")
-local fontname_1=P("name:")/isname*(namespec/thename)
-local fontname_2=P(true)/issome*(namespec/thename)
-local sometext=(R("az","AZ","09")+S("+-."))^1
-local truevalue=P("+")*spaces*(sometext/istrue)
-local falsevalue=P("-")*spaces*(sometext/isfalse)
-local keyvalue=(C(sometext)*spaces*P("=")*spaces*C(sometext))/iskey
-local somevalue=sometext/istrue
-local subvalue=P("(")*(C(P(1-S("()"))^1)/issub)*P(")")
-local option=spaces*(keyvalue+falsevalue+truevalue+somevalue)*spaces
-local options=P(":")*spaces*(P(";")^0*option)^0
-local pattern=(filename_1+filename_2+fontname_1+fontname_2)*subvalue^0*crapspec^0*options^0
-local function colonized(specification)
- list={}
- lpeg.match(pattern,specification.specification)
- list.crap=nil
- if list.name then
- specification.name=list.name
- list.name=nil
- end
- if list.lookup then
- specification.lookup=list.lookup
- list.lookup=nil
- end
- if list.sub then
- specification.sub=list.sub
- list.sub=nil
- end
- specification.features.normal=fonts.handlers.otf.features.normalize(list)
- return specification
-end
-fonts.definers.registersplit(":",colonized,"cryptic")
-fonts.definers.registersplit("",colonized,"more cryptic")
-function fonts.definers.applypostprocessors(tfmdata)
- local postprocessors=tfmdata.postprocessors
- if postprocessors then
- for i=1,#postprocessors do
- local extrahash=postprocessors[i](tfmdata)
- if type(extrahash)=="string" and extrahash~="" then
- extrahash=string.gsub(lower(extrahash),"[^a-z]","-")
- tfmdata.properties.fullname=format("%s-%s",tfmdata.properties.fullname,extrahash)
- end
- end
- end
- return tfmdata
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['luatex-fonts-ext']={
- version=1.001,
- comment="companion to luatex-*.tex",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-if context then
- texio.write_nl("fatal error: this module is not for context")
- os.exit()
-end
-local fonts=fonts
-local otffeatures=fonts.constructors.newfeatures("otf")
-local function initializeitlc(tfmdata,value)
- if value then
- local parameters=tfmdata.parameters
- local italicangle=parameters.italicangle
- if italicangle and italicangle~=0 then
- local properties=tfmdata.properties
- local factor=tonumber(value) or 1
- properties.hasitalics=true
- properties.autoitalicamount=factor*(parameters.uwidth or 40)/2
- end
- end
-end
-otffeatures.register {
- name="itlc",
- description="italic correction",
- initializers={
- base=initializeitlc,
- node=initializeitlc,
- }
-}
-local function initializeslant(tfmdata,value)
- value=tonumber(value)
- if not value then
- value=0
- elseif value>1 then
- value=1
- elseif value<-1 then
- value=-1
- end
- tfmdata.parameters.slantfactor=value
-end
-otffeatures.register {
- name="slant",
- description="slant glyphs",
- initializers={
- base=initializeslant,
- node=initializeslant,
- }
-}
-local function initializeextend(tfmdata,value)
- value=tonumber(value)
- if not value then
- value=0
- elseif value>10 then
- value=10
- elseif value<-10 then
- value=-10
- end
- tfmdata.parameters.extendfactor=value
-end
-otffeatures.register {
- name="extend",
- description="scale glyphs horizontally",
- initializers={
- base=initializeextend,
- node=initializeextend,
- }
-}
-fonts.protrusions=fonts.protrusions or {}
-fonts.protrusions.setups=fonts.protrusions.setups or {}
-local setups=fonts.protrusions.setups
-local function initializeprotrusion(tfmdata,value)
- if value then
- local setup=setups[value]
- if setup then
- local factor,left,right=setup.factor or 1,setup.left or 1,setup.right or 1
- local emwidth=tfmdata.parameters.quad
- tfmdata.parameters.protrusion={
- auto=true,
- }
- for i,chr in next,tfmdata.characters do
- local v,pl,pr=setup[i],nil,nil
- if v then
- pl,pr=v[1],v[2]
- end
- if pl and pl~=0 then chr.left_protruding=left*pl*factor end
- if pr and pr~=0 then chr.right_protruding=right*pr*factor end
- end
- end
- end
-end
-otffeatures.register {
- name="protrusion",
- description="shift characters into the left and or right margin",
- initializers={
- base=initializeprotrusion,
- node=initializeprotrusion,
- }
-}
-fonts.expansions=fonts.expansions or {}
-fonts.expansions.setups=fonts.expansions.setups or {}
-local setups=fonts.expansions.setups
-local function initializeexpansion(tfmdata,value)
- if value then
- local setup=setups[value]
- if setup then
- local factor=setup.factor or 1
- tfmdata.parameters.expansion={
- stretch=10*(setup.stretch or 0),
- shrink=10*(setup.shrink or 0),
- step=10*(setup.step or 0),
- auto=true,
- }
- for i,chr in next,tfmdata.characters do
- local v=setup[i]
- if v and v~=0 then
- chr.expansion_factor=v*factor
- else
- chr.expansion_factor=factor
- end
- end
- end
- end
-end
-otffeatures.register {
- name="expansion",
- description="apply hz optimization",
- initializers={
- base=initializeexpansion,
- node=initializeexpansion,
- }
-}
-function fonts.loggers.onetimemessage() end
-local byte=string.byte
-fonts.expansions.setups['default']={
- stretch=2,shrink=2,step=.5,factor=1,
- [byte('A')]=0.5,[byte('B')]=0.7,[byte('C')]=0.7,[byte('D')]=0.5,[byte('E')]=0.7,
- [byte('F')]=0.7,[byte('G')]=0.5,[byte('H')]=0.7,[byte('K')]=0.7,[byte('M')]=0.7,
- [byte('N')]=0.7,[byte('O')]=0.5,[byte('P')]=0.7,[byte('Q')]=0.5,[byte('R')]=0.7,
- [byte('S')]=0.7,[byte('U')]=0.7,[byte('W')]=0.7,[byte('Z')]=0.7,
- [byte('a')]=0.7,[byte('b')]=0.7,[byte('c')]=0.7,[byte('d')]=0.7,[byte('e')]=0.7,
- [byte('g')]=0.7,[byte('h')]=0.7,[byte('k')]=0.7,[byte('m')]=0.7,[byte('n')]=0.7,
- [byte('o')]=0.7,[byte('p')]=0.7,[byte('q')]=0.7,[byte('s')]=0.7,[byte('u')]=0.7,
- [byte('w')]=0.7,[byte('z')]=0.7,
- [byte('2')]=0.7,[byte('3')]=0.7,[byte('6')]=0.7,[byte('8')]=0.7,[byte('9')]=0.7,
-}
-fonts.protrusions.setups['default']={
- factor=1,left=1,right=1,
- [0x002C]={ 0,1 },
- [0x002E]={ 0,1 },
- [0x003A]={ 0,1 },
- [0x003B]={ 0,1 },
- [0x002D]={ 0,1 },
- [0x2013]={ 0,0.50 },
- [0x2014]={ 0,0.33 },
- [0x3001]={ 0,1 },
- [0x3002]={ 0,1 },
- [0x060C]={ 0,1 },
- [0x061B]={ 0,1 },
- [0x06D4]={ 0,1 },
-}
-fonts.handlers.otf.features.normalize=function(t)
- if t.rand then
- t.rand="random"
- end
- return t
-end
-function fonts.helpers.nametoslot(name)
- local t=type(name)
- if t=="string" then
- local tfmdata=fonts.hashes.identifiers[currentfont()]
- local shared=tfmdata and tfmdata.shared
- local fntdata=shared and shared.rawdata
- return fntdata and fntdata.resources.unicodes[name]
- elseif t=="number" then
- return n
- end
-end
-fonts.encodings=fonts.encodings or {}
-local reencodings={}
-fonts.encodings.reencodings=reencodings
-local function specialreencode(tfmdata,value)
- local encoding=value and reencodings[value]
- if encoding then
- local temp={}
- local char=tfmdata.characters
- for k,v in next,encoding do
- temp[k]=char[v]
- end
- for k,v in next,temp do
- char[k]=temp[k]
- end
- return string.format("reencoded:%s",value)
- end
-end
-local function reencode(tfmdata,value)
- tfmdata.postprocessors=tfmdata.postprocessors or {}
- table.insert(tfmdata.postprocessors,
- function(tfmdata)
- return specialreencode(tfmdata,value)
- end
- )
-end
-otffeatures.register {
- name="reencode",
- description="reencode characters",
- manipulators={
- base=reencode,
- node=reencode,
- }
-}
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['luatex-fonts-cbk']={
- version=1.001,
- comment="companion to luatex-*.tex",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-if context then
- texio.write_nl("fatal error: this module is not for context")
- os.exit()
-end
-local fonts=fonts
-local nodes=nodes
-local traverse_id=node.traverse_id
-local glyph_code=nodes.nodecodes.glyph
-function nodes.handlers.characters(head)
- local fontdata=fonts.hashes.identifiers
- if fontdata then
- local usedfonts,done,prevfont={},false,nil
- for n in traverse_id(glyph_code,head) do
- local font=n.font
- if font~=prevfont then
- prevfont=font
- local used=usedfonts[font]
- if not used then
- local tfmdata=fontdata[font]
- if tfmdata then
- local shared=tfmdata.shared
- if shared then
- local processors=shared.processes
- if processors and #processors>0 then
- usedfonts[font]=processors
- done=true
- end
- end
- end
- end
- end
- end
- if done then
- for font,processors in next,usedfonts do
- for i=1,#processors do
- local h,d=processors[i](head,font,0)
- head,done=h or head,done or d
- end
- end
- end
- return head,true
- else
- return head,false
- end
-end
-function nodes.simple_font_handler(head)
- head=nodes.handlers.characters(head)
- nodes.injections.handler(head)
- nodes.handlers.protectglyphs(head)
- head=node.ligaturing(head)
- head=node.kerning(head)
- return head
-end
-
-end -- closure
diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua
index 50ac0bd..b901824 100644
--- a/src/luaotfload-configuration.lua
+++ b/src/luaotfload-configuration.lua
@@ -135,7 +135,6 @@ local registered_loaders = {
reference = "reference",
unpackaged = "unpackaged",
context = "context",
- tl2014 = "tl2014",
}
local pick_fontloader = function (s)
diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua
index 4944de1..b06f3d5 100644
--- a/src/luaotfload-database.lua
+++ b/src/luaotfload-database.lua
@@ -360,7 +360,6 @@ This is a sketch of the luaotfload db:
conflicts : { barename : int; basename : int }; // filename conflict with font at index; happens with subfonts
familyname : string; // sanitized name of the font family the font belongs to, usually from the names table
fontname : string; // sanitized name of the font
- fontstyle_name : string; // the fontstyle_name field returned by fontloader.info()
format : string; // "otf" | "ttf" | "dfont" | "pfa" | "pfb" | "afm"
fullname : string; // sanitized full name of the font including style modifiers
fullpath : string; // path to font in filesystem
@@ -1842,7 +1841,7 @@ local read_font_names = function (fullname,
--- 5) check for subfonts and process each of them
- if type (info) == "table" and #info > 1 then --- ttc
+ if type (info) == "table" and #info >= 1 then --- ttc
local success = false --- true if at least one subfont got read
diff --git a/src/luaotfload-features.lua b/src/luaotfload-features.lua
index 9817e0b..79f1416 100644
--- a/src/luaotfload-features.lua
+++ b/src/luaotfload-features.lua
@@ -1,5 +1,5 @@
if not modules then modules = { } end modules ["features"] = {
- version = "2.6",
+ version = "2.7",
comment = "companion to luaotfload-main.lua",
author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang",
copyright = "PRAGMA ADE / ConTeXt Development Team",
diff --git a/src/luaotfload-init.lua b/src/luaotfload-init.lua
index 8b13fe6..bcc0e18 100644
--- a/src/luaotfload-init.lua
+++ b/src/luaotfload-init.lua
@@ -460,9 +460,6 @@ local init_main = function ()
Now that things are sorted out we can finally load the
fontloader.
- For less current distibutions we ship the code from TL 2014 that
- should be compatible with Luatex 0.76.
-
--doc]]--
local fontloader = config.luaotfload and config.luaotfload.run.fontloader
diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua
index 426aef7..3f1f602 100644
--- a/src/luaotfload-main.lua
+++ b/src/luaotfload-main.lua
@@ -13,7 +13,7 @@ local luaotfload = luaotfload
luaotfload.log = luaotfload.log or { }
luaotfload.version = "2.7"
luaotfload.loaders = { }
-luaotfload.min_luatex_version = 80 --- i. e. 0.80
+luaotfload.min_luatex_version = 95 --- i. e. 0.80
luaotfload.fontloader_package = "reference" --- default: from current Context
local authors = "\z
@@ -30,7 +30,7 @@ local authors = "\z
luaotfload.module = {
name = "luaotfload-main",
version = 2.70001,
- date = "2016/02/19",
+ date = "2016/04/19",
description = "OpenType layout system.",
author = authors,
copyright = authors,
@@ -66,29 +66,6 @@ luatexbase.provides_module (luaotfload.module)
--[[doc--
- We set the minimum version requirement for \LUATEX to v0.76,
- because the font loader requires recent features like direct
- attribute indexing and \luafunction{node.end_of_math()} that aren’t
- available in earlier versions.\footnote{%
- See Taco’s announcement of v0.76:
- \url{http://comments.gmane.org/gmane.comp.tex.luatex.user/4042}
- and this commit by Hans that introduced those features.
- \url{http://repo.or.cz/w/context.git/commitdiff/a51f6cf6ee087046a2ae5927ed4edff0a1acec1b}.
- }
-
---doc]]--
-
-if tex.luatexversion < luaotfload.min_luatex_version then
- warning ("LuaTeX v%.2f is old, v%.2f or later is recommended.",
- tex.luatexversion / 100,
- luaotfload.min_luatex_version / 100)
- warning ("using fallback fontloader -- newer functionality not available")
- luaotfload.fontloader_package = "tl2014" --- TODO fallback should be configurable too
- --- we install a fallback for older versions as a safety
-end
-
---[[doc--
-
\subsection{Module loading}
We load the files imported from \CONTEXT with function derived this way. It
automatically prepends a prefix to its argument, so we can refer to the