diff options
author | Karl Berry <karl@freefriends.org> | 2014-07-15 22:56:45 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2014-07-15 22:56:45 +0000 |
commit | 6caf348b43edcfc1dce127bf0895a5fb9bd77f2d (patch) | |
tree | 7b4b79f90596e69716ad87e3293743756b2a1954 /Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-merged.lua | |
parent | d71cd04a4c07d1cdaf85a7b7c2398022cc39beab (diff) |
luaotfload (15jul14)
git-svn-id: svn://tug.org/texlive/trunk@34622 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-merged.lua')
-rw-r--r-- | Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-merged.lua | 8157 |
1 files changed, 0 insertions, 8157 deletions
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-merged.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-merged.lua deleted file mode 100644 index 9bec2986dea..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-merged.lua +++ /dev/null @@ -1,8157 +0,0 @@ --- merged file : luaotfload-legacy-merged.lua --- parent file : luaotfload-legacy.lua --- merge date : Fri May 10 20:57:35 2013 - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['luat-dum']={ - 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" -} -local dummyfunction=function() end -statistics={ - register=dummyfunction, - starttiming=dummyfunction, - stoptiming=dummyfunction, -} -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={ - report=dummyfunction, - simple=dummyfunction, -} -tasks={ - new=dummyfunction, - actions=dummyfunction, - appendaction=dummyfunction, - prependaction=dummyfunction, -} -callbacks={ - register=function(n,f) return callback.register(n,f) end, -} -texconfig.kpse_init=true -resolvers=resolvers or {} -local remapper={ - otf="opentype fonts", - ttf="truetype fonts", - ttc="truetype fonts", - dfont="truetype fonts", - cid="cid maps", - fea="font feature files", -} -function resolvers.find_file(name,kind) - name=string.gsub(name,"\\","/") - kind=string.lower(kind) - return kpse.find_file(name,(kind and kind~="" and (remapper[kind] or kind)) or file.extname(name,"tex")) -end -function resolvers.findbinfile(name,kind) - if not kind or kind=="" then - kind=file.extname(name) - end - return resolvers.find_file(name,(kind and remapper[kind]) or kind) -end -caches={} -local writable,readables=nil,{} -if not caches.namespace or caches.namespace=="" or caches.namespace=="context" then - caches.namespace='generic' -end -do - local cachepaths - if kpse.expand_var('$TEXMFCACHE')~='$TEXMFCACHE' then - cachepaths=kpse.expand_var('$TEXMFCACHE') - elseif kpse.expand_var('$TEXMFVAR')~='$TEXMFVAR' then - cachepaths=kpse.expand_var('$TEXMFVAR') - end - if not cachepaths then - cachepaths="." - end - cachepaths=string.split(cachepaths,os.type=="windows" and ";" or ":") - for i=1,#cachepaths do - local done - writable=file.join(cachepaths[i],"luatex-cache") - writable=file.join(writable,caches.namespace) - writable,done=dir.mkdirs(writable) - if done then - break - end - end - for i=1,#cachepaths do - if file.isreadable(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\n") - os.exit() - elseif #readables==0 then - texio.write_nl("quiting: fix your readable cache path\n") - 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 - name="temp-"..name - return file.addsuffix(file.join(path,name),"lua") - end -end -function caches.iswritable(path,name) - local fullname=makefullname(path,name) - return fullname and file.iswritable(fullname) -end -function caches.loaddata(paths,name) - for i=1,#paths do - local fullname=makefullname(paths[i],name) - if fullname then - texio.write(string.format("(load: %s)",fullname)) - local data=loadfile(fullname) - return data and data() - end - end -end -function caches.savedata(path,name,data) - local fullname=makefullname(path,name) - if fullname then - texio.write(string.format("(save: %s)",fullname)) - table.tofile(fullname,data,'return',false,true,false) - end -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['luat-ovr']={ - version=1.001, - comment="companion to luatex-*.tex", - author="Khaled Hosny and Elie Roux", - copyright="Luaotfload Development Team", - license="GNU GPL v2" -} -local write_nl,format,name=texio.write_nl,string.format,"luaotfload" -local dummyfunction=function() end -callbacks={ - register=dummyfunction, -} -function logs.report(category,fmt,...) - if fmt then - write_nl('log',format("%s | %s: %s",name,category,format(fmt,...))) - elseif category then - write_nl('log',format("%s | %s",name,category)) - else - write_nl('log',format("%s |",name)) - end -end -function logs.info(category,fmt,...) - if fmt then - write_nl(format("%s | %s: %s",name,category,format(fmt,...))) - elseif category then - write_nl(format("%s | %s",name,category)) - else - write_nl(format("%s |",name)) - end - io.flush() -end -function logs.simple(fmt,...) - if fmt then - write_nl('log',format("%s | %s",name,format(fmt,...))) - else - write_nl('log',format("%s |",name)) - end -end -tex.attribute[0]=0 -tex.ctxcatcodes=luatexbase.catcodetables.latex - -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 {} -containers.usecache=true -local function report(container,tag,name) - if trace_cache or trace_containers then - logs.report(format("%s cache",container.subcategory),"%s: %s",tag,name or 'invalid') - end -end -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 -} -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.iswritable(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 - report(container,"loaded",name) - else - stored=nil - end - storage[name]=stored - elseif stored then - report(container,"reusing",name) - 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) - report(container,"saved",name) - data.unique,data.shared=unique,shared - end - report(container,"stored",name) - 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%d]+","-")) -end - -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 utf=unicode.utf8 -local format,serialize=string.format,table.serialize -local write_nl=texio.write_nl -local lower=string.lower -if not fontloader then fontloader=fontforge end -fontloader.totable=fontloader.to_table -fonts=fonts or {} -fonts.ids=fonts.ids or {} fonts.identifiers=fonts.ids -fonts.chr=fonts.chr or {} fonts.characters=fonts.chr -fonts.qua=fonts.qua or {} fonts.quads=fonts.qua -fonts.tfm=fonts.tfm or {} -fonts.mode='base' -fonts.private=0xF0000 -fonts.verbose=false -fonts.ids[0]={ - characters={}, - descriptions={}, - name="nullfont", -} -fonts.chr[0]={} -fonts.methods=fonts.methods or { - base={ tfm={},afm={},otf={},vtf={},fix={} }, - node={ tfm={},afm={},otf={},vtf={},fix={} }, -} -fonts.initializers=fonts.initializers or { - base={ tfm={},afm={},otf={},vtf={},fix={} }, - node={ tfm={},afm={},otf={},vtf={},fix={} } -} -fonts.triggers=fonts.triggers or { - 'mode', - 'language', - 'script', - 'strategy', -} -fonts.processors=fonts.processors or {} -fonts.manipulators=fonts.manipulators or {} -fonts.define=fonts.define or {} -fonts.define.specify=fonts.define.specify or {} -fonts.define.specify.synonyms=fonts.define.specify.synonyms or {} -if not fonts.color then - fonts.color={ - set=function() end, - reset=function() end, - } -end -fonts.formats={} -function fonts.fontformat(filename,default) - local extname=lower(file.extname(filename)) - local format=fonts.formats[extname] - if format then - return format - else - logs.report("fonts define","unable to determine font format for '%s'",filename) - return default - end -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['node-dum']={ - version=1.001, - comment="companion to luatex-*.tex", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -nodes=nodes or {} -fonts=fonts or {} -attributes=attributes or {} -local traverse_id=node.traverse_id -local free_node=node.free -local remove_node=node.remove -local new_node=node.new -local glyph=node.id('glyph') -local fontdata=fonts.ids or {} -function nodes.simple_font_handler(head) - head=nodes.process_characters(head) - nodes.inject_kerns(head) - nodes.protect_glyphs(head) - head=node.ligaturing(head) - head=node.kerning(head) - return head -end -if tex.attribute[0]~=0 then - texio.write_nl("log","!") - texio.write_nl("log","! Attribute 0 is reserved for ConTeXt's font feature management and has to be") - texio.write_nl("log","! set to zero. Also, some attributes in the range 1-255 are used for special") - texio.write_nl("log","! purposed so setting them at the TeX end might break the font handler.") - texio.write_nl("log","!") - tex.attribute[0]=0 -end -nodes.protect_glyphs=node.protect_glyphs -nodes.unprotect_glyphs=node.unprotect_glyphs -function nodes.process_characters(head) - local usedfonts,done,prevfont={},false,nil - for n in traverse_id(glyph,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 -end -function nodes.kern(k) - local n=new_node("kern",1) - n.kern=k - return n -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 -nodes.before=node.insert_before -nodes.after=node.insert_after -attributes.unsetvalue=-0x7FFFFFFF -local numbers,last={},127 -function attributes.private(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 - -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 trace_injections=false trackers.register("nodes.injections",function(v) trace_injections=v end) -fonts=fonts or {} -fonts.tfm=fonts.tfm or {} -fonts.ids=fonts.ids or {} -local fontdata=fonts.ids -local glyph=node.id('glyph') -local kern=node.id('kern') -local traverse_id=node.traverse_id -local unset_attribute=node.unset_attribute -local has_attribute=node.has_attribute -local set_attribute=node.set_attribute -local insert_node_before=node.insert_before -local insert_node_after=node.insert_after -local newkern=nodes.kern -local markbase=attributes.private('markbase') -local markmark=attributes.private('markmark') -local markdone=attributes.private('markdone') -local cursbase=attributes.private('cursbase') -local curscurs=attributes.private('curscurs') -local cursdone=attributes.private('cursdone') -local kernpair=attributes.private('kernpair') -local cursives={} -local marks={} -local kerns={} -function nodes.set_cursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) - local dx,dy=factor*(exit[1]-entry[1]),factor*(exit[2]-entry[2]) - local ws,wn=tfmstart.width,tfmnext.width - local bound=#cursives+1 - set_attribute(start,cursbase,bound) - set_attribute(nxt,curscurs,bound) - cursives[bound]={ rlmode,dx,dy,ws,wn } - return dx,dy,bound -end -function nodes.set_pair(current,factor,rlmode,r2lflag,spec,tfmchr) - local x,y,w,h=factor*spec[1],factor*spec[2],factor*spec[3],factor*spec[4] - if x~=0 or w~=0 or y~=0 or h~=0 then - local bound=has_attribute(current,kernpair) - if bound then - local kb=kerns[bound] - kb[2],kb[3],kb[4],kb[5]=(kb[2] or 0)+x,(kb[3] or 0)+y,(kb[4] or 0)+w,(kb[5] or 0)+h - else - bound=#kerns+1 - set_attribute(current,kernpair,bound) - kerns[bound]={ rlmode,x,y,w,h,r2lflag,tfmchr.width } - end - return x,y,w,h,bound - end - return x,y,w,h -end -function nodes.set_kern(current,factor,rlmode,x,tfmchr) - local dx=factor*x - if dx~=0 then - local bound=#kerns+1 - set_attribute(current,kernpair,bound) - kerns[bound]={ rlmode,dx } - return dx,bound - else - return 0,0 - end -end -function nodes.set_mark(start,base,factor,rlmode,ba,ma,index) - local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2]) - local bound=has_attribute(base,markbase) - if bound then - local mb=marks[bound] - if mb then - if not index then index=#mb+1 end - mb[index]={ dx,dy } - set_attribute(start,markmark,bound) - set_attribute(start,markdone,index) - return dx,dy,bound - else - logs.report("nodes mark","possible problem, U+%04X is base without data (id: %s)",base.char,bound) - end - end - index=index or 1 - bound=#marks+1 - set_attribute(base,markbase,bound) - set_attribute(start,markmark,bound) - set_attribute(start,markdone,index) - marks[bound]={ [index]={ dx,dy,rlmode } } - return dx,dy,bound -end -function nodes.trace_injection(head) - local function dir(n) - return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or ("unset") - end - local function report(...) - logs.report("nodes finisher",...) - end - report("begin run") - for n in traverse_id(glyph,head) do - if n.subtype<256 then - local kp=has_attribute(n,kernpair) - local mb=has_attribute(n,markbase) - local mm=has_attribute(n,markmark) - local md=has_attribute(n,markdone) - local cb=has_attribute(n,cursbase) - local cc=has_attribute(n,curscurs) - report("char U+%05X, font=%s",n.char,n.font) - if kp then - local k=kerns[kp] - if k[3] then - report(" pairkern: dir=%s, x=%s, y=%s, w=%s, h=%s",dir(k[1]),k[2] or "?",k[3] or "?",k[4] or "?",k[5] or "?") - else - report(" kern: dir=%s, dx=%s",dir(k[1]),k[2] or "?") - end - end - if mb then - report(" markbase: bound=%s",mb) - end - if mm then - local m=marks[mm] - if mb then - local m=m[mb] - if m then - report(" markmark: bound=%s, index=%s, dx=%s, dy=%s",mm,md or "?",m[1] or "?",m[2] or "?") - else - report(" markmark: bound=%s, missing index",mm) - end - else - m=m[1] - report(" markmark: bound=%s, dx=%s, dy=%s",mm,m[1] or "?",m[2] or "?") - end - end - if cb then - report(" cursbase: bound=%s",cb) - end - if cc then - local c=cursives[cc] - report(" curscurs: bound=%s, dir=%s, dx=%s, dy=%s",cc,dir(c[1]),c[2] or "?",c[3] or "?") - end - end - end - report("end run") -end -function nodes.inject_kerns(head,where,keep) - local has_marks,has_cursives,has_kerns=next(marks),next(cursives),next(kerns) - if has_marks or has_cursives then - if trace_injections then - nodes.trace_injection(head) - end - local done,ky,rl,valid,cx,wx,mk=false,{},{},{},{},{},{} - if has_kerns then - local nf,tm=nil,nil - for n in traverse_id(glyph,head) do - if n.subtype<256 then - valid[#valid+1]=n - if n.font~=nf then - nf=n.font - tm=fontdata[nf].marks - end - mk[n]=tm[n.char] - local k=has_attribute(n,kernpair) - if k then - local kk=kerns[k] - if kk then - local x,y,w,h=kk[2] or 0,kk[3] or 0,kk[4] or 0,kk[5] or 0 - local dy=y-h - if dy~=0 then - ky[n]=dy - end - if w~=0 or x~=0 then - wx[n]=kk - end - rl[n]=kk[1] - end - end - end - end - else - local nf,tm=nil,nil - for n in traverse_id(glyph,head) do - if n.subtype<256 then - valid[#valid+1]=n - if n.font~=nf then - nf=n.font - tm=fontdata[nf].marks - end - mk[n]=tm[n.char] - end - end - end - if #valid>0 then - 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,#valid do - local n=valid[i] - if not mk[n] then - local n_cursbase=has_attribute(n,cursbase) - if p_cursbase then - local n_curscurs=has_attribute(n,curscurs) - if p_cursbase==n_curscurs then - local c=cursives[n_curscurs] - if c then - local rlmode,dx,dy,ws,wn=c[1],c[2],c[3],c[4],c[5] - if rlmode>=0 then - dx=dx-ws - else - dx=dx+wn - end - if dx~=0 then - cx[n]=dx - rl[n]=rlmode - end - 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,#valid do - local p=valid[i] - local p_markbase=has_attribute(p,markbase) - if p_markbase then - local mrks=marks[p_markbase] - for n in traverse_id(glyph,p.next) do - local n_markmark=has_attribute(n,markmark) - if p_markbase==n_markmark then - local index=has_attribute(n,markdone) or 1 - local d=mrks[index] - if d then - local rlmode=d[3] - if rlmode and rlmode>0 then - local k=wx[p] - if k then - n.xoffset=p.xoffset-(p.width-d[1])-k[2] - else - n.xoffset=p.xoffset-(p.width-d[1]) - end - else - local k=wx[p] - if k then - n.xoffset=p.xoffset-d[1]-k[2] - else - n.xoffset=p.xoffset-d[1] - end - end - if mk[p] then - n.yoffset=p.yoffset+d[2] - else - n.yoffset=n.yoffset+p.yoffset+d[2] - end - end - else - break - end - end - end - end - if not keep then - marks={} - end - end - if next(wx) then - for n,k in next,wx do - local rl,x,w,r2l=k[1],k[2] or 0,k[4] or 0,k[6] - local wx=w-x - if r2l then - if wx~=0 then - insert_node_before(head,n,newkern(wx)) - end - if x~=0 then - insert_node_after (head,n,newkern(x)) - end - else - if x~=0 then - insert_node_before(head,n,newkern(x)) - end - if wx~=0 then - insert_node_after(head,n,newkern(wx)) - end - end - end - end - if next(cx) then - for n,k in next,cx do - if k~=0 then - local rln=rl[n] - if rln and rln<0 then - insert_node_before(head,n,newkern(-k)) - else - insert_node_before(head,n,newkern(k)) - end - end - end - end - if not keep then - kerns={} - end - return head,true - elseif not keep then - kerns,cursives,marks={},{},{} - end - elseif has_kerns then - if trace_injections then - nodes.trace_injection(head) - end - for n in traverse_id(glyph,head) do - if n.subtype<256 then - local k=has_attribute(n,kernpair) - if k then - local kk=kerns[k] - if kk then - local rl,x,y,w=kk[1],kk[2] or 0,kk[3],kk[4] - if y and y~=0 then - n.yoffset=y - end - if w then - local r2l=kk[6] - local wx=w-x - if r2l then - if wx~=0 then - insert_node_before(head,n,newkern(wx)) - end - if x~=0 then - insert_node_after (head,n,newkern(x)) - end - else - if x~=0 then - insert_node_before(head,n,newkern(x)) - end - if wx~=0 then - insert_node_after(head,n,newkern(wx)) - end - end - else - if 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 ['otfl-luat-att']={ - version=math.pi/42, - comment="companion to luaotfload.lua", - author="Philipp Gesang", - copyright="Luaotfload Development Team", - license="GNU GPL v2" -} -function attributes.private(name) - local attr="otfl@"..name - local number=luatexbase.attributes[attr] - if not number then - number=luatexbase.new_attribute(attr) - end - return number -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 utf=unicode.utf8 -local next,format,match,lower,gsub=next,string.format,string.match,string.lower,string.gsub -local concat,sortedkeys,utfbyte,serialize=table.concat,table.sortedkeys,utf.byte,table.serialize -local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end) -local trace_scaling=false trackers.register("fonts.scaling",function(v) trace_scaling=v end) -fonts=fonts or {} -fonts.tfm=fonts.tfm or {} -fonts.ids=fonts.ids or {} -local tfm=fonts.tfm -fonts.loaded=fonts.loaded or {} -fonts.dontembed=fonts.dontembed or {} -fonts.triggers=fonts.triggers or {} -fonts.initializers=fonts.initializers or {} -fonts.initializers.common=fonts.initializers.common or {} -local fontdata=fonts.ids -local disc=node.id('disc') -local glyph=node.id('glyph') -local set_attribute=node.set_attribute -tfm.resolve_vf=true -tfm.share_base_kerns=false -tfm.mathactions={} -tfm.fontname_mode="fullpath" -tfm.enhance=tfm.enhance or function() end -fonts.formats.tfm="type1" -function tfm.read_from_tfm(specification) - local fname,tfmdata=specification.filename or "",nil - if fname~="" then - if trace_defining then - logs.report("define font","loading tfm file %s at size %s",fname,specification.size) - end - tfmdata=font.read_tfm(fname,specification.size) - if tfmdata then - tfmdata.descriptions=tfmdata.descriptions or {} - if tfm.resolve_vf then - fonts.logger.save(tfmdata,file.extname(fname),specification) - fname=resolvers.findbinfile(specification.name,'ovf') - if fname and fname~="" then - local vfdata=font.read_vf(fname,specification.size) - if vfdata then - local chars=tfmdata.characters - for k,v in next,vfdata.characters do - chars[k].commands=v.commands - end - tfmdata.type='virtual' - tfmdata.fonts=vfdata.fonts - end - end - end - tfm.enhance(tfmdata,specification) - end - elseif trace_defining then - logs.report("define font","loading tfm with name %s fails",specification.name) - end - return tfmdata -end -local factors={ - pt=65536.0, - bp=65781.8, -} -function tfm.setfactor(f) - tfm.factor=factors[f or 'pt'] or factors.pt -end -tfm.setfactor() -function tfm.scaled(scaledpoints,designsize) - if scaledpoints<0 then - if designsize then - if designsize>tfm.factor then - return (- scaledpoints/1000)*designsize - else - return (- scaledpoints/1000)*designsize*tfm.factor - end - else - return (- scaledpoints/1000)*10*tfm.factor - end - else - return scaledpoints - end -end -function tfm.get_virtual_id(tfmdata) - if not tfmdata.fonts then - tfmdata.type="virtual" - tfmdata.fonts={ { id=0 } } - return 1 - else - tfmdata.fonts[#tfmdata.fonts+1]={ id=0 } - return #tfmdata.fonts - end -end -function tfm.check_virtual_id(tfmdata,id) - if tfmdata and tfmdata.type=="virtual" then - if not tfmdata.fonts or #tfmdata.fonts==0 then - tfmdata.type,tfmdata.fonts="real",nil - else - local vfonts=tfmdata.fonts - for f=1,#vfonts do - local fnt=vfonts[f] - if fnt.id and fnt.id==0 then - fnt.id=id - end - end - end - end -end -fonts.trace_scaling=false -local charactercache={} -function tfm.calculate_scale(tfmtable,scaledpoints,relativeid) - if scaledpoints<0 then - scaledpoints=(- scaledpoints/1000)*tfmtable.designsize - end - local units=tfmtable.units or 1000 - local delta=scaledpoints/units - return scaledpoints,delta,units -end -function tfm.do_scale(tfmtable,scaledpoints,relativeid) - local t={} - local scaledpoints,delta,units=tfm.calculate_scale(tfmtable,scaledpoints,relativeid) - t.units_per_em=units or 1000 - local hdelta,vdelta=delta,delta - for k,v in next,tfmtable do - if type(v)=="table" then - else - t[k]=v - end - end - local extend_factor=tfmtable.extend_factor or 0 - if extend_factor~=0 and extend_factor~=1 then - hdelta=hdelta*extend_factor - t.extend=extend_factor*1000 - else - t.extend=1000 - end - local slant_factor=tfmtable.slant_factor or 0 - if slant_factor~=0 then - t.slant=slant_factor*1000 - else - t.slant=0 - end - local isvirtual=tfmtable.type=="virtual" or tfmtable.virtualized - local hasmath=(tfmtable.math_parameters~=nil and next(tfmtable.math_parameters)~=nil) or (tfmtable.MathConstants~=nil and next(tfmtable.MathConstants)~=nil) - local nodemode=tfmtable.mode=="node" - local hasquality=tfmtable.auto_expand or tfmtable.auto_protrude - local hasitalic=tfmtable.has_italic - t.parameters={} - t.characters={} - t.MathConstants={} - local descriptions=tfmtable.descriptions or {} - t.unicodes=tfmtable.unicodes - t.indices=tfmtable.indices - t.marks=tfmtable.marks -t.goodies=tfmtable.goodies -t.colorscheme=tfmtable.colorscheme - t.descriptions=descriptions - if tfmtable.fonts then - t.fonts=table.fastcopy(tfmtable.fonts) - end - local tp=t.parameters - local mp=t.math_parameters - local tfmp=tfmtable.parameters - tp.slant=(tfmp.slant or tfmp[1] or 0) - tp.space=(tfmp.space or tfmp[2] or 0)*hdelta - tp.space_stretch=(tfmp.space_stretch or tfmp[3] or 0)*hdelta - tp.space_shrink=(tfmp.space_shrink or tfmp[4] or 0)*hdelta - tp.x_height=(tfmp.x_height or tfmp[5] or 0)*vdelta - tp.quad=(tfmp.quad or tfmp[6] or 0)*hdelta - tp.extra_space=(tfmp.extra_space or tfmp[7] or 0)*hdelta - local protrusionfactor=(tp.quad~=0 and 1000/tp.quad) or 0 - local tc=t.characters - local characters=tfmtable.characters - local nameneeded=not tfmtable.shared.otfdata - local changed=tfmtable.changed or {} - local ischanged=changed and next(changed) - local indices=tfmtable.indices - local luatex=tfmtable.luatex - local tounicode=luatex and luatex.tounicode - local defaultwidth=luatex and luatex.defaultwidth or 0 - local defaultheight=luatex and luatex.defaultheight or 0 - local defaultdepth=luatex and luatex.defaultdepth or 0 - local scaledwidth=defaultwidth*hdelta - local scaledheight=defaultheight*vdelta - local scaleddepth=defaultdepth*vdelta - local stackmath=tfmtable.ignore_stack_math~=true - local private=fonts.private - local sharedkerns={} - for k,v in next,characters do - local chr,description,index - if ischanged then - local c=changed[k] - if c then - description=descriptions[c] or v - v=characters[c] or v - index=(indices and indices[c]) or c - else - description=descriptions[k] or v - index=(indices and indices[k]) or k - end - else - description=descriptions[k] or v - index=(indices and indices[k]) or k - end - local width=description.width - local height=description.height - local depth=description.depth - if width then width=hdelta*width else width=scaledwidth end - if height then height=vdelta*height else height=scaledheight end - if depth and depth~=0 then - depth=delta*depth - if nameneeded then - chr={ - name=description.name, - index=index, - height=height, - depth=depth, - width=width, - } - else - chr={ - index=index, - height=height, - depth=depth, - width=width, - } - end - else - if nameneeded then - chr={ - name=description.name, - index=index, - height=height, - width=width, - } - else - chr={ - index=index, - height=height, - width=width, - } - end - end - if tounicode then - local tu=tounicode[index] - if tu then - chr.tounicode=tu - end - end - if hasquality then - local ve=v.expansion_factor - if ve then - chr.expansion_factor=ve*1000 - end - local vl=v.left_protruding - if vl then - chr.left_protruding=protrusionfactor*width*vl - end - local vr=v.right_protruding - if vr then - chr.right_protruding=protrusionfactor*width*vr - end - end - if hasitalic then - local vi=description.italic or v.italic - if vi and vi~=0 then - chr.italic=vi*hdelta - end - end - if hasmath then - local vn=v.next - if vn then - chr.next=vn - else - local vv=v.vert_variants - if vv then - local t={} - for i=1,#vv do - local vvi=vv[i] - t[i]={ - ["start"]=(vvi["start"] or 0)*vdelta, - ["end"]=(vvi["end"] or 0)*vdelta, - ["advance"]=(vvi["advance"] or 0)*vdelta, - ["extender"]=vvi["extender"], - ["glyph"]=vvi["glyph"], - } - end - chr.vert_variants=t - else - local hv=v.horiz_variants - if hv then - local t={} - for i=1,#hv do - local hvi=hv[i] - t[i]={ - ["start"]=(hvi["start"] or 0)*hdelta, - ["end"]=(hvi["end"] or 0)*hdelta, - ["advance"]=(hvi["advance"] or 0)*hdelta, - ["extender"]=hvi["extender"], - ["glyph"]=hvi["glyph"], - } - end - chr.horiz_variants=t - end - end - end - local vt=description.top_accent - if vt then - chr.top_accent=vdelta*vt - end - if stackmath then - local mk=v.mathkerns - if mk then - local kerns={} - local v=mk.top_right if v then local k={} for i=1,#v do local vi=v[i] - k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern } - end kerns.top_right=k end - local v=mk.top_left if v then local k={} for i=1,#v do local vi=v[i] - k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern } - end kerns.top_left=k end - local v=mk.bottom_left if v then local k={} for i=1,#v do local vi=v[i] - k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern } - end kerns.bottom_left=k end - local v=mk.bottom_right if v then local k={} for i=1,#v do local vi=v[i] - k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern } - end kerns.bottom_right=k end - chr.mathkern=kerns - end - end - end - if not nodemode then - local vk=v.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=v.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=v.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[#tt+1]={ key,ivc[2]*hdelta } - elseif key=="down" then - tt[#tt+1]={ key,ivc[2]*vdelta } - elseif key=="rule" then - tt[#tt+1]={ key,ivc[2]*vdelta,ivc[3]*hdelta } - else - tt[#tt+1]=ivc - end - end - chr.commands=tt - else - chr.commands=vc - end - end - end - tc[k]=chr - end - t.size=scaledpoints - t.factor=delta - t.hfactor=hdelta - t.vfactor=vdelta - if t.fonts then - t.fonts=table.fastcopy(t.fonts) - end - if hasmath then - local ma=tfm.mathactions - for i=1,#ma do - ma[i](t,tfmtable,delta,hdelta,vdelta) - end - end - local tpx=tp.x_height - if hasmath then - if not tp[13] then tp[13]=.86*tpx end - if not tp[14] then tp[14]=.86*tpx end - if not tp[15] then tp[15]=.86*tpx end - if not tp[16] then tp[16]=.48*tpx end - if not tp[17] then tp[17]=.48*tpx end - if not tp[22] then tp[22]=0 end - if t.MathConstants then t.MathConstants.AccentBaseHeight=nil end - end - t.tounicode=1 - t.cidinfo=tfmtable.cidinfo - if hasmath then - if trace_defining then - logs.report("define font","math enabled for: name '%s', fullname: '%s', filename: '%s'",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename") - end - else - if trace_defining then - logs.report("define font","math disabled for: name '%s', fullname: '%s', filename: '%s'",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename") - end - t.nomath,t.MathConstants=true,nil - end - if not t.psname then - t.psname=t.fontname or (t.fullname and fonts.names.cleanname(t.fullname)) - end - if trace_defining then - logs.report("define font","used for accesing subfont: '%s'",t.psname or "nopsname") - logs.report("define font","used for subsetting: '%s'",t.fontname or "nofontname") - end - return t,delta -end -tfm.auto_cleanup=true -local lastfont=nil -function tfm.cleanup_table(tfmdata) - if tfm.auto_cleanup then - if tfmdata.type=='virtual' or tfmdata.virtualized then - for k,v in next,tfmdata.characters do - if v.commands then v.commands=nil end - end - else - end - end -end -function tfm.cleanup(tfmdata) -end -function tfm.scale(tfmtable,scaledpoints,relativeid) - local t,factor=tfm.do_scale(tfmtable,scaledpoints,relativeid) - t.factor=factor - t.ascender=factor*(tfmtable.ascender or 0) - t.descender=factor*(tfmtable.descender or 0) - t.shared=tfmtable.shared or {} - t.unique=table.fastcopy(tfmtable.unique or {}) - tfm.cleanup(t) - return t -end -fonts.analyzers=fonts.analyzers or {} -fonts.analyzers.aux=fonts.analyzers.aux or {} -fonts.analyzers.methods=fonts.analyzers.methods or {} -fonts.analyzers.initializers=fonts.analyzers.initializers or {} -local state=attributes.private('state') -function fonts.analyzers.aux.setstate(head,font) - local tfmdata=fontdata[font] - local characters=tfmdata.characters - local descriptions=tfmdata.descriptions - local first,last,current,n,done=nil,nil,head,0,false - while current do - local id=current.id - if id==glyph and current.font==font then - local d=descriptions[current.char] - if d then - if d.class=="mark" then - done=true - set_attribute(current,state,5) - elseif n==0 then - first,last,n=current,current,1 - set_attribute(current,state,1) - else - last,n=current,n+1 - set_attribute(current,state,2) - end - else - if first and first==last then - set_attribute(last,state,4) - elseif last then - set_attribute(last,state,3) - end - first,last,n=nil,nil,0 - end - elseif id==disc then - set_attribute(current,state,2) - last=current - else - if first and first==last then - set_attribute(last,state,4) - elseif last then - set_attribute(last,state,3) - end - first,last,n=nil,nil,0 - end - current=current.next - end - if first and first==last then - set_attribute(last,state,4) - elseif last then - set_attribute(last,state,3) - end - return head,done -end -function tfm.replacements(tfm,value) - tfm.characters[0x0027]=tfm.characters[0x2019] -end -function tfm.checked_filename(metadata,whatever) - local foundfilename=metadata.foundfilename - if not foundfilename then - local askedfilename=metadata.filename or "" - if askedfilename~="" then - foundfilename=resolvers.findbinfile(askedfilename,"") or "" - if foundfilename=="" then - logs.report("fonts","source file '%s' is not found",askedfilename) - foundfilename=resolvers.findbinfile(file.basename(askedfilename),"") or "" - if foundfilename~="" then - logs.report("fonts","using source file '%s' (cache mismatch)",foundfilename) - end - end - elseif whatever then - logs.report("fonts","no source file for '%s'",whatever) - foundfilename="" - end - metadata.foundfilename=foundfilename - end - return foundfilename -end -statistics.register("fonts load time",function() - return statistics.elapsedseconds(fonts) -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 lpegmatch=lpeg.match -local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end) -fonts=fonts or {} -fonts.cid=fonts.cid or {} -fonts.cid.map=fonts.cid.map or {} -fonts.cid.max=fonts.cid.max or 10 -local number=lpeg.C(lpeg.R("09","af","AF")^1) -local space=lpeg.S(" \n\r\t") -local spaces=space^0 -local period=lpeg.P(".") -local periods=period*period -local name=lpeg.P("/")*lpeg.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=lpeg.P { "start", - start=number*spaces*number*lpeg.V("series"), - series=(spaces*(lpeg.V("one")+lpeg.V("range")+lpeg.V("named")) )^1, - one=(number*spaces*number)/do_one, - range=(number*periods*number*spaces*number)/do_range, - named=(number*spaces*name)/do_name -} -function fonts.cid.load(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 - } - else - return nil - end -end -local template="%s-%s-%s.cidmap" -local function locate(registry,ordering,supplement) - local filename=format(template,registry,ordering,supplement) - local hashname=lower(filename) - local cidmap=fonts.cid.map[hashname] - if not cidmap then - if trace_loading then - logs.report("load otf","checking cidmap, registry: %s, ordering: %s, supplement: %s, filename: %s",registry,ordering,supplement,filename) - end - local fullname=resolvers.find_file(filename,'cid') or "" - if fullname~="" then - cidmap=fonts.cid.load(fullname) - if cidmap then - if trace_loading then - logs.report("load otf","using cidmap file %s",filename) - end - fonts.cid.map[hashname]=cidmap - cidmap.usedname=file.basename(filename) - return cidmap - end - end - end - return cidmap -end -function fonts.cid.getmap(registry,ordering,supplement) - local supplement=tonumber(supplement) - if trace_loading then - logs.report("load otf","needed cidmap, registry: %s, ordering: %s, supplement: %s",registry,ordering,supplement) - end - local cidmap=locate(registry,ordering,supplement) - if not cidmap then - local cidnum=nil - if supplement<fonts.cid.max then - for supplement=supplement+1,fonts.cid.max do - local c=locate(registry,ordering,supplement) - if c then - cidmap,cidnum=c,supplement - break - end - end - end - if not cidmap and supplement>0 then - for supplement=supplement-1,0,-1 do - local c=locate(registry,ordering,supplement) - if c then - cidmap,cidnum=c,supplement - break - end - end - end - if cidmap and cidnum>0 then - for s=0,cidnum-1 do - filename=format(template,registry,ordering,s) - if not fonts.cid.map[filename] then - fonts.cid.map[filename]=cidmap - end - end - end - end - return cidmap -end - -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-otf.lua (tables)", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -local type,next,tonumber,tostring=type,next,tonumber,tostring -local gsub,lower=string.gsub,string.lower -fonts=fonts or {} -fonts.otf=fonts.otf or {} -local otf=fonts.otf -otf.tables=otf.tables or {} -otf.meanings=otf.meanings or {} -otf.tables.scripts={ - ['dflt']='Default', - ['arab']='Arabic', - ['armn']='Armenian', - ['bali']='Balinese', - ['beng']='Bengali', - ['bopo']='Bopomofo', - ['brai']='Braille', - ['bugi']='Buginese', - ['buhd']='Buhid', - ['byzm']='Byzantine Music', - ['cans']='Canadian Syllabics', - ['cher']='Cherokee', - ['copt']='Coptic', - ['cprt']='Cypriot Syllabary', - ['cyrl']='Cyrillic', - ['deva']='Devanagari', - ['dsrt']='Deseret', - ['ethi']='Ethiopic', - ['geor']='Georgian', - ['glag']='Glagolitic', - ['goth']='Gothic', - ['grek']='Greek', - ['gujr']='Gujarati', - ['guru']='Gurmukhi', - ['hang']='Hangul', - ['hani']='CJK Ideographic', - ['hano']='Hanunoo', - ['hebr']='Hebrew', - ['ital']='Old Italic', - ['jamo']='Hangul Jamo', - ['java']='Javanese', - ['kana']='Hiragana and Katakana', - ['khar']='Kharosthi', - ['khmr']='Khmer', - ['knda']='Kannada', - ['lao' ]='Lao', - ['latn']='Latin', - ['limb']='Limbu', - ['linb']='Linear B', - ['math']='Mathematical Alphanumeric Symbols', - ['mlym']='Malayalam', - ['mong']='Mongolian', - ['musc']='Musical Symbols', - ['mymr']='Myanmar', - ['nko' ]="N'ko", - ['ogam']='Ogham', - ['orya']='Oriya', - ['osma']='Osmanya', - ['phag']='Phags-pa', - ['phnx']='Phoenician', - ['runr']='Runic', - ['shaw']='Shavian', - ['sinh']='Sinhala', - ['sylo']='Syloti Nagri', - ['syrc']='Syriac', - ['tagb']='Tagbanwa', - ['tale']='Tai Le', - ['talu']='Tai Lu', - ['taml']='Tamil', - ['telu']='Telugu', - ['tfng']='Tifinagh', - ['tglg']='Tagalog', - ['thaa']='Thaana', - ['thai']='Thai', - ['tibt']='Tibetan', - ['ugar']='Ugaritic Cuneiform', - ['xpeo']='Old Persian Cuneiform', - ['xsux']='Sumero-Akkadian Cuneiform', - ['yi' ]='Yi', -} -otf.tables.languages={ - ['dflt']='Default', - ['aba']='Abaza', - ['abk']='Abkhazian', - ['ady']='Adyghe', - ['afk']='Afrikaans', - ['afr']='Afar', - ['agw']='Agaw', - ['als']='Alsatian', - ['alt']='Altai', - ['amh']='Amharic', - ['ara']='Arabic', - ['ari']='Aari', - ['ark']='Arakanese', - ['asm']='Assamese', - ['ath']='Athapaskan', - ['avr']='Avar', - ['awa']='Awadhi', - ['aym']='Aymara', - ['aze']='Azeri', - ['bad']='Badaga', - ['bag']='Baghelkhandi', - ['bal']='Balkar', - ['bau']='Baule', - ['bbr']='Berber', - ['bch']='Bench', - ['bcr']='Bible Cree', - ['bel']='Belarussian', - ['bem']='Bemba', - ['ben']='Bengali', - ['bgr']='Bulgarian', - ['bhi']='Bhili', - ['bho']='Bhojpuri', - ['bik']='Bikol', - ['bil']='Bilen', - ['bkf']='Blackfoot', - ['bli']='Balochi', - ['bln']='Balante', - ['blt']='Balti', - ['bmb']='Bambara', - ['bml']='Bamileke', - ['bos']='Bosnian', - ['bre']='Breton', - ['brh']='Brahui', - ['bri']='Braj Bhasha', - ['brm']='Burmese', - ['bsh']='Bashkir', - ['bti']='Beti', - ['cat']='Catalan', - ['ceb']='Cebuano', - ['che']='Chechen', - ['chg']='Chaha Gurage', - ['chh']='Chattisgarhi', - ['chi']='Chichewa', - ['chk']='Chukchi', - ['chp']='Chipewyan', - ['chr']='Cherokee', - ['chu']='Chuvash', - ['cmr']='Comorian', - ['cop']='Coptic', - ['cos']='Corsican', - ['cre']='Cree', - ['crr']='Carrier', - ['crt']='Crimean Tatar', - ['csl']='Church Slavonic', - ['csy']='Czech', - ['dan']='Danish', - ['dar']='Dargwa', - ['dcr']='Woods Cree', - ['deu']='German', - ['dgr']='Dogri', - ['div']='Divehi', - ['djr']='Djerma', - ['dng']='Dangme', - ['dnk']='Dinka', - ['dri']='Dari', - ['dun']='Dungan', - ['dzn']='Dzongkha', - ['ebi']='Ebira', - ['ecr']='Eastern Cree', - ['edo']='Edo', - ['efi']='Efik', - ['ell']='Greek', - ['eng']='English', - ['erz']='Erzya', - ['esp']='Spanish', - ['eti']='Estonian', - ['euq']='Basque', - ['evk']='Evenki', - ['evn']='Even', - ['ewe']='Ewe', - ['fan']='French Antillean', - ['far']='Farsi', - ['fin']='Finnish', - ['fji']='Fijian', - ['fle']='Flemish', - ['fne']='Forest Nenets', - ['fon']='Fon', - ['fos']='Faroese', - ['fra']='French', - ['fri']='Frisian', - ['frl']='Friulian', - ['fta']='Futa', - ['ful']='Fulani', - ['gad']='Ga', - ['gae']='Gaelic', - ['gag']='Gagauz', - ['gal']='Galician', - ['gar']='Garshuni', - ['gaw']='Garhwali', - ['gez']="Ge'ez", - ['gil']='Gilyak', - ['gmz']='Gumuz', - ['gon']='Gondi', - ['grn']='Greenlandic', - ['gro']='Garo', - ['gua']='Guarani', - ['guj']='Gujarati', - ['hai']='Haitian', - ['hal']='Halam', - ['har']='Harauti', - ['hau']='Hausa', - ['haw']='Hawaiin', - ['hbn']='Hammer-Banna', - ['hil']='Hiligaynon', - ['hin']='Hindi', - ['hma']='High Mari', - ['hnd']='Hindko', - ['ho']='Ho', - ['hri']='Harari', - ['hrv']='Croatian', - ['hun']='Hungarian', - ['hye']='Armenian', - ['ibo']='Igbo', - ['ijo']='Ijo', - ['ilo']='Ilokano', - ['ind']='Indonesian', - ['ing']='Ingush', - ['inu']='Inuktitut', - ['iri']='Irish', - ['irt']='Irish Traditional', - ['isl']='Icelandic', - ['ism']='Inari Sami', - ['ita']='Italian', - ['iwr']='Hebrew', - ['jan']='Japanese', - ['jav']='Javanese', - ['jii']='Yiddish', - ['jud']='Judezmo', - ['jul']='Jula', - ['kab']='Kabardian', - ['kac']='Kachchi', - ['kal']='Kalenjin', - ['kan']='Kannada', - ['kar']='Karachay', - ['kat']='Georgian', - ['kaz']='Kazakh', - ['keb']='Kebena', - ['kge']='Khutsuri Georgian', - ['kha']='Khakass', - ['khk']='Khanty-Kazim', - ['khm']='Khmer', - ['khs']='Khanty-Shurishkar', - ['khv']='Khanty-Vakhi', - ['khw']='Khowar', - ['kik']='Kikuyu', - ['kir']='Kirghiz', - ['kis']='Kisii', - ['kkn']='Kokni', - ['klm']='Kalmyk', - ['kmb']='Kamba', - ['kmn']='Kumaoni', - ['kmo']='Komo', - ['kms']='Komso', - ['knr']='Kanuri', - ['kod']='Kodagu', - ['koh']='Korean Old Hangul', - ['kok']='Konkani', - ['kon']='Kikongo', - ['kop']='Komi-Permyak', - ['kor']='Korean', - ['koz']='Komi-Zyrian', - ['kpl']='Kpelle', - ['kri']='Krio', - ['krk']='Karakalpak', - ['krl']='Karelian', - ['krm']='Karaim', - ['krn']='Karen', - ['krt']='Koorete', - ['ksh']='Kashmiri', - ['ksi']='Khasi', - ['ksm']='Kildin Sami', - ['kui']='Kui', - ['kul']='Kulvi', - ['kum']='Kumyk', - ['kur']='Kurdish', - ['kuu']='Kurukh', - ['kuy']='Kuy', - ['kyk']='Koryak', - ['lad']='Ladin', - ['lah']='Lahuli', - ['lak']='Lak', - ['lam']='Lambani', - ['lao']='Lao', - ['lat']='Latin', - ['laz']='Laz', - ['lcr']='L-Cree', - ['ldk']='Ladakhi', - ['lez']='Lezgi', - ['lin']='Lingala', - ['lma']='Low Mari', - ['lmb']='Limbu', - ['lmw']='Lomwe', - ['lsb']='Lower Sorbian', - ['lsm']='Lule Sami', - ['lth']='Lithuanian', - ['ltz']='Luxembourgish', - ['lub']='Luba', - ['lug']='Luganda', - ['luh']='Luhya', - ['luo']='Luo', - ['lvi']='Latvian', - ['maj']='Majang', - ['mak']='Makua', - ['mal']='Malayalam Traditional', - ['man']='Mansi', - ['map']='Mapudungun', - ['mar']='Marathi', - ['maw']='Marwari', - ['mbn']='Mbundu', - ['mch']='Manchu', - ['mcr']='Moose Cree', - ['mde']='Mende', - ['men']="Me'en", - ['miz']='Mizo', - ['mkd']='Macedonian', - ['mle']='Male', - ['mlg']='Malagasy', - ['mln']='Malinke', - ['mlr']='Malayalam Reformed', - ['mly']='Malay', - ['mnd']='Mandinka', - ['mng']='Mongolian', - ['mni']='Manipuri', - ['mnk']='Maninka', - ['mnx']='Manx Gaelic', - ['moh']='Mohawk', - ['mok']='Moksha', - ['mol']='Moldavian', - ['mon']='Mon', - ['mor']='Moroccan', - ['mri']='Maori', - ['mth']='Maithili', - ['mts']='Maltese', - ['mun']='Mundari', - ['nag']='Naga-Assamese', - ['nan']='Nanai', - ['nas']='Naskapi', - ['ncr']='N-Cree', - ['ndb']='Ndebele', - ['ndg']='Ndonga', - ['nep']='Nepali', - ['new']='Newari', - ['ngr']='Nagari', - ['nhc']='Norway House Cree', - ['nis']='Nisi', - ['niu']='Niuean', - ['nkl']='Nkole', - ['nko']="N'ko", - ['nld']='Dutch', - ['nog']='Nogai', - ['nor']='Norwegian', - ['nsm']='Northern Sami', - ['nta']='Northern Tai', - ['nto']='Esperanto', - ['nyn']='Nynorsk', - ['oci']='Occitan', - ['ocr']='Oji-Cree', - ['ojb']='Ojibway', - ['ori']='Oriya', - ['oro']='Oromo', - ['oss']='Ossetian', - ['paa']='Palestinian Aramaic', - ['pal']='Pali', - ['pan']='Punjabi', - ['pap']='Palpa', - ['pas']='Pashto', - ['pgr']='Polytonic Greek', - ['pil']='Pilipino', - ['plg']='Palaung', - ['plk']='Polish', - ['pro']='Provencal', - ['ptg']='Portuguese', - ['qin']='Chin', - ['raj']='Rajasthani', - ['rbu']='Russian Buriat', - ['rcr']='R-Cree', - ['ria']='Riang', - ['rms']='Rhaeto-Romanic', - ['rom']='Romanian', - ['roy']='Romany', - ['rsy']='Rusyn', - ['rua']='Ruanda', - ['rus']='Russian', - ['sad']='Sadri', - ['san']='Sanskrit', - ['sat']='Santali', - ['say']='Sayisi', - ['sek']='Sekota', - ['sel']='Selkup', - ['sgo']='Sango', - ['shn']='Shan', - ['sib']='Sibe', - ['sid']='Sidamo', - ['sig']='Silte Gurage', - ['sks']='Skolt Sami', - ['sky']='Slovak', - ['sla']='Slavey', - ['slv']='Slovenian', - ['sml']='Somali', - ['smo']='Samoan', - ['sna']='Sena', - ['snd']='Sindhi', - ['snh']='Sinhalese', - ['snk']='Soninke', - ['sog']='Sodo Gurage', - ['sot']='Sotho', - ['sqi']='Albanian', - ['srb']='Serbian', - ['srk']='Saraiki', - ['srr']='Serer', - ['ssl']='South Slavey', - ['ssm']='Southern Sami', - ['sur']='Suri', - ['sva']='Svan', - ['sve']='Swedish', - ['swa']='Swadaya Aramaic', - ['swk']='Swahili', - ['swz']='Swazi', - ['sxt']='Sutu', - ['syr']='Syriac', - ['tab']='Tabasaran', - ['taj']='Tajiki', - ['tam']='Tamil', - ['tat']='Tatar', - ['tcr']='TH-Cree', - ['tel']='Telugu', - ['tgn']='Tongan', - ['tgr']='Tigre', - ['tgy']='Tigrinya', - ['tha']='Thai', - ['tht']='Tahitian', - ['tib']='Tibetan', - ['tkm']='Turkmen', - ['tmn']='Temne', - ['tna']='Tswana', - ['tne']='Tundra Nenets', - ['tng']='Tonga', - ['tod']='Todo', - ['trk']='Turkish', - ['tsg']='Tsonga', - ['tua']='Turoyo Aramaic', - ['tul']='Tulu', - ['tuv']='Tuvin', - ['twi']='Twi', - ['udm']='Udmurt', - ['ukr']='Ukrainian', - ['urd']='Urdu', - ['usb']='Upper Sorbian', - ['uyg']='Uyghur', - ['uzb']='Uzbek', - ['ven']='Venda', - ['vit']='Vietnamese', - ['wa' ]='Wa', - ['wag']='Wagdi', - ['wcr']='West-Cree', - ['wel']='Welsh', - ['wlf']='Wolof', - ['xbd']='Tai Lue', - ['xhs']='Xhosa', - ['yak']='Yakut', - ['yba']='Yoruba', - ['ycr']='Y-Cree', - ['yic']='Yi Classic', - ['yim']='Yi Modern', - ['zhh']='Chinese Hong Kong', - ['zhp']='Chinese Phonetic', - ['zhs']='Chinese Simplified', - ['zht']='Chinese Traditional', - ['znd']='Zande', - ['zul']='Zulu' -} -otf.tables.features={ - ['aalt']='Access All Alternates', - ['abvf']='Above-Base Forms', - ['abvm']='Above-Base Mark Positioning', - ['abvs']='Above-Base Substitutions', - ['afrc']='Alternative Fractions', - ['akhn']='Akhands', - ['blwf']='Below-Base Forms', - ['blwm']='Below-Base Mark Positioning', - ['blws']='Below-Base Substitutions', - ['c2pc']='Petite Capitals From Capitals', - ['c2sc']='Small Capitals From Capitals', - ['calt']='Contextual Alternates', - ['case']='Case-Sensitive Forms', - ['ccmp']='Glyph Composition/Decomposition', - ['cjct']='Conjunct Forms', - ['clig']='Contextual Ligatures', - ['cpsp']='Capital Spacing', - ['cswh']='Contextual Swash', - ['curs']='Cursive Positioning', - ['dflt']='Default Processing', - ['dist']='Distances', - ['dlig']='Discretionary Ligatures', - ['dnom']='Denominators', - ['dtls']='Dotless Forms', - ['expt']='Expert Forms', - ['falt']='Final glyph Alternates', - ['fin2']='Terminal Forms #2', - ['fin3']='Terminal Forms #3', - ['fina']='Terminal Forms', - ['flac']='Flattened Accents Over Capitals', - ['frac']='Fractions', - ['fwid']='Full Width', - ['half']='Half Forms', - ['haln']='Halant Forms', - ['halt']='Alternate Half Width', - ['hist']='Historical Forms', - ['hkna']='Horizontal Kana Alternates', - ['hlig']='Historical Ligatures', - ['hngl']='Hangul', - ['hojo']='Hojo Kanji Forms', - ['hwid']='Half Width', - ['init']='Initial Forms', - ['isol']='Isolated Forms', - ['ital']='Italics', - ['jalt']='Justification Alternatives', - ['jp04']='JIS2004 Forms', - ['jp78']='JIS78 Forms', - ['jp83']='JIS83 Forms', - ['jp90']='JIS90 Forms', - ['kern']='Kerning', - ['lfbd']='Left Bounds', - ['liga']='Standard Ligatures', - ['ljmo']='Leading Jamo Forms', - ['lnum']='Lining Figures', - ['locl']='Localized Forms', - ['mark']='Mark Positioning', - ['med2']='Medial Forms #2', - ['medi']='Medial Forms', - ['mgrk']='Mathematical Greek', - ['mkmk']='Mark to Mark Positioning', - ['mset']='Mark Positioning via Substitution', - ['nalt']='Alternate Annotation Forms', - ['nlck']='NLC Kanji Forms', - ['nukt']='Nukta Forms', - ['numr']='Numerators', - ['onum']='Old Style Figures', - ['opbd']='Optical Bounds', - ['ordn']='Ordinals', - ['ornm']='Ornaments', - ['palt']='Proportional Alternate Width', - ['pcap']='Petite Capitals', - ['pnum']='Proportional Figures', - ['pref']='Pre-base Forms', - ['pres']='Pre-base Substitutions', - ['pstf']='Post-base Forms', - ['psts']='Post-base Substitutions', - ['pwid']='Proportional Widths', - ['qwid']='Quarter Widths', - ['rand']='Randomize', - ['rkrf']='Rakar Forms', - ['rlig']='Required Ligatures', - ['rphf']='Reph Form', - ['rtbd']='Right Bounds', - ['rtla']='Right-To-Left Alternates', - ['rtlm']='Right To Left Math', - ['ruby']='Ruby Notation Forms', - ['salt']='Stylistic Alternates', - ['sinf']='Scientific Inferiors', - ['size']='Optical Size', - ['smcp']='Small Capitals', - ['smpl']='Simplified Forms', - ['ss01']='Stylistic Set 1', - ['ss02']='Stylistic Set 2', - ['ss03']='Stylistic Set 3', - ['ss04']='Stylistic Set 4', - ['ss05']='Stylistic Set 5', - ['ss06']='Stylistic Set 6', - ['ss07']='Stylistic Set 7', - ['ss08']='Stylistic Set 8', - ['ss09']='Stylistic Set 9', - ['ss10']='Stylistic Set 10', - ['ss11']='Stylistic Set 11', - ['ss12']='Stylistic Set 12', - ['ss13']='Stylistic Set 13', - ['ss14']='Stylistic Set 14', - ['ss15']='Stylistic Set 15', - ['ss16']='Stylistic Set 16', - ['ss17']='Stylistic Set 17', - ['ss18']='Stylistic Set 18', - ['ss19']='Stylistic Set 19', - ['ss20']='Stylistic Set 20', - ['ssty']='Script Style', - ['subs']='Subscript', - ['sups']='Superscript', - ['swsh']='Swash', - ['titl']='Titling', - ['tjmo']='Trailing Jamo Forms', - ['tnam']='Traditional Name Forms', - ['tnum']='Tabular Figures', - ['trad']='Traditional Forms', - ['twid']='Third Widths', - ['unic']='Unicase', - ['valt']='Alternate Vertical Metrics', - ['vatu']='Vattu Variants', - ['vert']='Vertical Writing', - ['vhal']='Alternate Vertical Half Metrics', - ['vjmo']='Vowel Jamo Forms', - ['vkna']='Vertical Kana Alternates', - ['vkrn']='Vertical Kerning', - ['vpal']='Proportional Alternate Vertical Metrics', - ['vrt2']='Vertical Rotation', - ['zero']='Slashed Zero', - ['trep']='Traditional TeX Replacements', - ['tlig']='Traditional TeX Ligatures', -} -otf.tables.baselines={ - ['hang']='Hanging baseline', - ['icfb']='Ideographic character face bottom edge baseline', - ['icft']='Ideographic character face tope edige baseline', - ['ideo']='Ideographic em-box bottom edge baseline', - ['idtp']='Ideographic em-box top edge baseline', - ['math']='Mathmatical centered baseline', - ['romn']='Roman baseline' -} -function otf.tables.to_tag(id) - return stringformat("%4s",lower(id)) -end -local function resolve(tab,id) - if tab and id then - id=lower(id) - return tab[id] or tab[gsub(id," ","")] or tab['dflt'] or '' - else - return "unknown" - end -end -function otf.meanings.script(id) - return resolve(otf.tables.scripts,id) -end -function otf.meanings.language(id) - return resolve(otf.tables.languages,id) -end -function otf.meanings.feature(id) - return resolve(otf.tables.features,id) -end -function otf.meanings.baseline(id) - return resolve(otf.tables.baselines,id) -end -otf.tables.to_scripts=table.reverse_hash(otf.tables.scripts ) -otf.tables.to_languages=table.reverse_hash(otf.tables.languages) -otf.tables.to_features=table.reverse_hash(otf.tables.features ) -local scripts=otf.tables.scripts -local languages=otf.tables.languages -local features=otf.tables.features -local to_scripts=otf.tables.to_scripts -local to_languages=otf.tables.to_languages -local to_features=otf.tables.to_features -for k,v in next,to_features do - local stripped=gsub(k,"%-"," ") - to_features[stripped]=v - local stripped=gsub(k,"[^a-zA-Z0-9]","") - to_features[stripped]=v -end -for k,v in next,to_features do - to_features[lower(k)]=v -end -otf.meanings.checkers={ - rand=function(v) - return v and "random" - end -} -local checkers=otf.meanings.checkers -function otf.meanings.normalize(features) - local h={} - for k,v in next,features do - k=lower(k) - if k=="language" or k=="lang" then - v=gsub(lower(v),"[^a-z0-9%-]","") - if not languages[v] then - h.language=to_languages[v] or "dflt" - else - h.language=v - end - elseif k=="script" then - v=gsub(lower(v),"[^a-z0-9%-]","") - if not scripts[v] then - h.script=to_scripts[v] or "dflt" - else - h.script=v - end - else - if type(v)=="string" then - local b=v:is_boolean() - if type(b)=="nil" then - v=tonumber(v) or lower(v) - else - v=b - end - end - k=to_features[k] or k - local c=checkers[k] - h[k]=c and c(v) or v - end - end - return h -end - -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 utf=unicode.utf8 -local match,format,find,concat,gsub,lower=string.match,string.format,string.find,table.concat,string.gsub,string.lower -local lpegmatch=lpeg.match -local utfbyte=utf.byte -local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end) -local trace_unimapping=false trackers.register("otf.unimapping",function(v) trace_unimapping=v end) -local ctxcatcodes=tex and tex.ctxcatcodes -fonts=fonts or {} -fonts.map=fonts.map or {} -local function load_lum_table(filename) - local lumname=file.replacesuffix(file.basename(filename),"lum") - local lumfile=resolvers.find_file(lumname,"map") or "" - if lumfile~="" and lfs.isfile(lumfile) then - if trace_loading or trace_unimapping then - logs.report("load otf","enhance: loading %s ",lumfile) - end - lumunic=dofile(lumfile) - return lumunic,lumfile - end -end -local hex=lpeg.R("AF","09") -local hexfour=(hex*hex*hex*hex)/function(s) return tonumber(s,16) end -local hexsix=(hex^1)/function(s) return tonumber(s,16) end -local dec=(lpeg.R("09")^1)/tonumber -local period=lpeg.P(".") -local unicode=lpeg.P("uni")*(hexfour*(period+lpeg.P(-1))*lpeg.Cc(false)+lpeg.Ct(hexfour^1)*lpeg.Cc(true)) -local ucode=lpeg.P("u")*(hexsix*(period+lpeg.P(-1))*lpeg.Cc(false)+lpeg.Ct(hexsix^1)*lpeg.Cc(true)) -local index=lpeg.P("index")*dec*lpeg.Cc(false) -local parser=unicode+ucode+index -local parsers={} -local function make_name_parser(str) - if not str or str=="" then - return parser - else - local p=parsers[str] - if not p then - p=lpeg.P(str)*period*dec*lpeg.Cc(false) - parsers[str]=p - end - return p - end -end -local function tounicode16(unicode) - if unicode<0x10000 then - return format("%04X",unicode) - else - return format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00) - end -end -local function tounicode16sequence(unicodes) - local t={} - for l=1,#unicodes do - local unicode=unicodes[l] - if unicode<0x10000 then - t[l]=format("%04X",unicode) - else - t[l]=format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00) - end - end - return concat(t) -end -fonts.map.load_lum_table=load_lum_table -fonts.map.make_name_parser=make_name_parser -fonts.map.tounicode16=tounicode16 -fonts.map.tounicode16sequence=tounicode16sequence -local separator=lpeg.S("_.") -local other=lpeg.C((1-separator)^1) -local ligsplitter=lpeg.Ct(other*(separator*other)^0) -fonts.map.add_to_unicode=function(data,filename) - local unicodes=data.luatex and data.luatex.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 tounicode,originals,ns,nl,private,unknown={},{},0,0,fonts.private,format("%04X",utfbyte("?")) - data.luatex.tounicode,data.luatex.originals=tounicode,originals - local lumunic,uparser,oparser - if false then - lumunic=load_lum_table(filename) - lumunic=lumunic and lumunic.tounicode - end - local cidinfo,cidnames,cidcodes=data.cidinfo - local usedmap=cidinfo and cidinfo.usedname - usedmap=usedmap and lower(usedmap) - usedmap=usedmap and fonts.cid.map[usedmap] - if usedmap then - oparser=usedmap and make_name_parser(cidinfo.ordering) - cidnames=usedmap.names - cidcodes=usedmap.unicodes - end - uparser=make_name_parser() - local aglmap=fonts.map and fonts.map.agl_to_unicode - for index,glyph in next,data.glyphs do - local name,unic=glyph.name,glyph.unicode or -1 - if unic==-1 or unic>=private or (unic>=0xE000 and unic<=0xF8FF) or unic==0xFFFE or unic==0xFFFF then - local unicode=(lumunic and lumunic[name]) or (aglmap and aglmap[name]) - if unicode then - originals[index],tounicode[index],ns=unicode,tounicode16(unicode),ns+1 - end - if (not unicode) and usedmap then - local foundindex=lpegmatch(oparser,name) - if foundindex then - unicode=cidcodes[foundindex] - if unicode then - originals[index],tounicode[index],ns=unicode,tounicode16(unicode),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],tounicode[index],ns=unicode,tounicode16(unicode),ns+1 - end - end - if not unicode then - local foundcodes,multiple=lpegmatch(uparser,reference) - if foundcodes then - if multiple then - originals[index],tounicode[index],nl,unicode=foundcodes,tounicode16sequence(foundcodes),nl+1,true - else - originals[index],tounicode[index],ns,unicode=foundcodes,tounicode16(foundcodes),ns+1,foundcodes - end - end - end - end - end - end - end - if not unicode then - local split=lpegmatch(ligsplitter,name) - local nplit=(split and #split) or 0 - if nplit==0 then - elseif nplit==1 then - local base=split[1] - unicode=unicodes[base] or (aglmap and aglmap[base]) - if unicode then - if type(unicode)=="table" then - unicode=unicode[1] - end - originals[index],tounicode[index],ns=unicode,tounicode16(unicode),ns+1 - end - else - local t={} - for l=1,nplit do - local base=split[l] - local u=unicodes[base] or (aglmap and aglmap[base]) - if not u then - break - elseif type(u)=="table" then - t[#t+1]=u[1] - else - t[#t+1]=u - end - end - if #t>0 then - originals[index],tounicode[index],nl,unicode=t,tounicode16sequence(t),nl+1,true - end - end - end - if not unicode then - local foundcodes,multiple=lpegmatch(uparser,name) - if foundcodes then - if multiple then - originals[index],tounicode[index],nl,unicode=foundcodes,tounicode16sequence(foundcodes),nl+1,true - else - originals[index],tounicode[index],ns,unicode=foundcodes,tounicode16(foundcodes),ns+1,foundcodes - end - end - end - if not unicode then - originals[index],tounicode[index]=0xFFFD,"FFFD" - end - end - end - if trace_unimapping then - for index,glyph in table.sortedhash(data.glyphs) do - local toun,name,unic=tounicode[index],glyph.name,glyph.unicode or -1 - if toun then - logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X, tounicode: %s",index,name,unic,toun) - else - logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X",index,name,unic) - end - end - end - if trace_loading and (ns>0 or nl>0) then - logs.report("load otf","enhance: %s tounicode entries added (%s ligatures)",nl+ns,ns) - end -end - -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 utf=unicode.utf8 -local concat,utfbyte=table.concat,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 getn=table.getn -local lpegmatch=lpeg.match -local trace_private=false trackers.register("otf.private",function(v) trace_private=v end) -local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end) -local trace_features=false trackers.register("otf.features",function(v) trace_features=v end) -local trace_dynamics=false trackers.register("otf.dynamics",function(v) trace_dynamics=v end) -local trace_sequences=false trackers.register("otf.sequences",function(v) trace_sequences=v end) -local trace_math=false trackers.register("otf.math",function(v) trace_math=v end) -local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end) -fonts=fonts or {} -fonts.otf=fonts.otf or {} -fonts.tfm=fonts.tfm or {} -local otf=fonts.otf -local tfm=fonts.tfm -local fontdata=fonts.ids -otf.tables=otf.tables or {} -otf.meanings=otf.meanings or {} -otf.tables.features=otf.tables.features or {} -otf.tables.languages=otf.tables.languages or {} -otf.tables.scripts=otf.tables.scripts or {} -otf.features=otf.features or {} -otf.features.list=otf.features.list or {} -otf.features.default=otf.features.default or {} -otf.enhancers=otf.enhancers or {} -otf.glists={ "gsub","gpos" } -otf.version=2.653 -otf.pack=true -otf.syncspace=true -otf.notdef=false -otf.cache=containers.define("fonts","otf",otf.version,true) -otf.cleanup_aat=false -local wildcard="*" -local default="dflt" -otf.tables.global_fields=table.tohash { - "lookups", - "glyphs", - "subfonts", - "luatex", - "pfminfo", - "cidinfo", - "tables", - "names", - "unicodes", - "names", - "anchor_classes", - "kern_classes", - "gpos", - "gsub" -} -otf.tables.valid_fields={ - "anchor_classes", - "ascent", - "cache_version", - "cidinfo", - "copyright", - "creationtime", - "descent", - "design_range_bottom", - "design_range_top", - "design_size", - "encodingchanged", - "extrema_bound", - "familyname", - "fontname", - "fontstyle_id", - "fontstyle_name", - "fullname", - "glyphs", - "hasvmetrics", - "head_optimized_for_cleartype", - "horiz_base", - "issans", - "isserif", - "italicangle", - "kerns", - "lookups", - "macstyle", - "modificationtime", - "onlybitmaps", - "origname", - "os2_version", - "pfminfo", - "private", - "serifcheck", - "sfd_version", - "strokedfont", - "strokewidth", - "subfonts", - "table_version", - "ttf_tables", - "uni_interp", - "uniqueid", - "units_per_em", - "upos", - "use_typo_metrics", - "uwidth", - "validation_state", - "verbose", - "version", - "vert_base", - "weight", - "weight_width_slope_only", - "xuid", -} -local function load_featurefile(ff,featurefile) - if featurefile then - featurefile=resolvers.find_file(file.addsuffix(featurefile,'fea'),'fea') - if featurefile and featurefile~="" then - if trace_loading then - logs.report("load otf","featurefile: %s",featurefile) - end - fontloader.apply_featurefile(ff,featurefile) - end - end -end -function otf.enhance(name,data,filename,verbose) - local enhancer=otf.enhancers[name] - if enhancer then - if (verbose~=nil and verbose) or trace_loading then - logs.report("load otf","enhance: %s (%s)",name,filename) - end - enhancer(data,filename) - end -end -local enhancers={ - "patch bugs", - "merge cid fonts","prepare unicode","cleanup ttf tables","compact glyphs","reverse coverage", - "cleanup aat","enrich with features","add some missing characters", - "reorganize mark classes", - "reorganize kerns", - "flatten glyph lookups","flatten anchor tables","flatten feature tables", - "simplify glyph lookups", - "prepare luatex tables", - "analyse features","rehash features", - "analyse anchors","analyse marks","analyse unicodes","analyse subtables", - "check italic correction","check math", - "share widths", - "strip not needed data", - "migrate metadata", - "check math parameters", -} -function otf.load(filename,format,sub,featurefile) - local name=file.basename(file.removesuffix(filename)) - local attr=lfs.attributes(filename) - local size,time=attr.size or 0,attr.modification or 0 - if featurefile then - local fattr=lfs.attributes(featurefile) - local fsize,ftime=fattr and fattr.size or 0,fattr and fattr.modification or 0 - name=name.."@"..file.removesuffix(file.basename(featurefile))..ftime..fsize - end - if sub=="" then sub=false end - local hash=name - if sub then - hash=hash.."-"..sub - end - hash=containers.cleanname(hash) - local data=containers.read(otf.cache,hash) - if not data or data.verbose~=fonts.verbose or data.size~=size or data.time~=time then - logs.report("load otf","loading: %s (hash: %s)",filename,hash) - local ff,messages - if sub then - ff,messages=fontloader.open(filename,sub) - else - ff,messages=fontloader.open(filename) - end - if trace_loading and messages and #messages>0 then - if type(messages)=="string" then - logs.report("load otf","warning: %s",messages) - else - for m=1,#messages do - logs.report("load otf","warning: %s",tostring(messages[m])) - end - end - else - logs.report("load otf","font loaded okay") - end - if ff then - load_featurefile(ff,featurefile) - data=fontloader.to_table(ff) - fontloader.close(ff) - if data then - logs.report("load otf","file size: %s",size) - logs.report("load otf","enhancing ...") - for e=1,#enhancers do - otf.enhance(enhancers[e],data,filename) - io.flush() - end - if otf.pack and not fonts.verbose then - otf.enhance("pack",data,filename) - end - data.size=size - data.time=time - data.verbose=fonts.verbose - logs.report("load otf","saving in cache: %s",filename) - data=containers.write(otf.cache,hash,data) - collectgarbage("collect") - data=containers.read(otf.cache,hash) - collectgarbage("collect") - else - logs.report("load otf","loading failed (table conversion error)") - end - else - logs.report("load otf","loading failed (file read error)") - end - end - if data then - if trace_defining then - logs.report("define font","loading from cache: %s",hash) - end - otf.enhance("unpack",data,filename,false) - otf.add_dimensions(data) - if trace_sequences then - otf.show_feature_order(data,filename) - end - end - return data -end -function otf.add_dimensions(data) - if data then - local force=otf.notdef - local luatex=data.luatex - local defaultwidth=luatex.defaultwidth or 0 - local defaultheight=luatex.defaultheight or 0 - local defaultdepth=luatex.defaultdepth or 0 - for _,d in next,data.glyphs do - local bb,wd=d.boundingbox,d.width - if not wd then - d.width=defaultwidth - elseif wd~=0 and d.class=="mark" then - d.width=-wd - end - if force and not d.name then - d.name=".notdef" - end - if bb then - local ht,dp=bb[4],-bb[2] - if ht==0 or ht<0 then - else - d.height=ht - end - if dp==0 or dp<0 then - else - d.depth=dp - end - end - end - end -end -function otf.show_feature_order(otfdata,filename) - local sequences=otfdata.luatex.sequences - if sequences and #sequences>0 then - if trace_loading then - logs.report("otf check","font %s has %s sequences",filename,#sequences) - logs.report("otf check"," ") - end - for nos=1,#sequences do - local sequence=sequences[nos] - local typ=sequence.type or "no-type" - local name=sequence.name or "no-name" - local subtables=sequence.subtables or { "no-subtables" } - local features=sequence.features - if trace_loading then - logs.report("otf check","%3i %-15s %-20s [%s]",nos,name,typ,concat(subtables,",")) - end - if features then - for feature,scripts in next,features do - local tt={} - for script,languages in next,scripts do - local ttt={} - for language,_ in next,languages do - ttt[#ttt+1]=language - end - tt[#tt+1]=format("[%s: %s]",script,concat(ttt," ")) - end - if trace_loading then - logs.report("otf check"," %s: %s",feature,concat(tt," ")) - end - end - end - end - if trace_loading then - logs.report("otf check","\n") - end - elseif trace_loading then - logs.report("otf check","font %s has no sequences",filename) - end -end -otf.enhancers["reorganize mark classes"]=function(data,filename) - if data.mark_classes then - local unicodes=data.luatex.unicodes - local reverse={} - for name,class in next,data.mark_classes do - local t={} - for s in gmatch(class,"[^ ]+") do - local us=unicodes[s] - if type(us)=="table" then - for u=1,#us do - t[us[u]]=true - end - else - t[us]=true - end - end - reverse[name]=t - end - data.luatex.markclasses=reverse - data.mark_classes=nil - end -end -otf.enhancers["prepare luatex tables"]=function(data,filename) - data.luatex=data.luatex or {} - local luatex=data.luatex - luatex.filename=filename - luatex.version=otf.version - luatex.creator="context mkiv" -end -otf.enhancers["cleanup aat"]=function(data,filename) - if otf.cleanup_aat then - end -end -local function analyze_features(g,features) - if g then - local t,done={},{} - for k=1,#g do - local f=features or g[k].features - if f then - for k=1,#f do - local tag=f[k].tag - if not done[tag] then - t[#t+1]=tag - done[tag]=true - end - end - end - end - if #t>0 then - return t - end - end - return nil -end -otf.enhancers["analyse features"]=function(data,filename) -end -otf.enhancers["rehash features"]=function(data,filename) - local features={} - data.luatex.features=features - for k,what in next,otf.glists do - local dw=data[what] - if dw then - local f={} - features[what]=f - for i=1,#dw do - local d=dw[i] - local dfeatures=d.features - if dfeatures then - for i=1,#dfeatures do - local df=dfeatures[i] - local tag=strip(lower(df.tag)) - local ft=f[tag] if not ft then ft={} f[tag]=ft end - local dscripts=df.scripts - for script,languages in next,dscripts do - script=strip(lower(script)) - local fts=ft[script] if not fts then fts={} ft[script]=fts end - for i=1,#languages do - fts[strip(lower(languages[i]))]=true - end - end - end - end - end - end - end -end -otf.enhancers["analyse anchors"]=function(data,filename) - local classes=data.anchor_classes - local luatex=data.luatex - local anchor_to_lookup,lookup_to_anchor={},{} - luatex.anchor_to_lookup,luatex.lookup_to_anchor=anchor_to_lookup,lookup_to_anchor - if classes then - for c=1,#classes do - local class=classes[c] - local anchor=class.name - local lookups=class.lookup - if type(lookups)~="table" then - lookups={ lookups } - end - local a=anchor_to_lookup[anchor] - if not a then a={} anchor_to_lookup[anchor]=a end - for l=1,#lookups do - local lookup=lookups[l] - local l=lookup_to_anchor[lookup] - if not l then l={} lookup_to_anchor[lookup]=l end - l[anchor]=true - a[lookup]=true - end - end - end -end -otf.enhancers["analyse marks"]=function(data,filename) - local glyphs=data.glyphs - local marks={} - data.luatex.marks=marks - for unicode,index in next,data.luatex.indices do - local glyph=glyphs[index] - if glyph.class=="mark" then - marks[unicode]=true - end - end -end -otf.enhancers["analyse unicodes"]=fonts.map.add_to_unicode -otf.enhancers["analyse subtables"]=function(data,filename) - data.luatex=data.luatex or {} - local luatex=data.luatex - local sequences={} - local lookups={} - luatex.sequences=sequences - luatex.lookups=lookups - for _,g in next,{ data.gsub,data.gpos } do - for k=1,#g do - local gk=g[k] - local typ=gk.type - if typ=="gsub_contextchain" or typ=="gpos_contextchain" then - gk.chain=1 - elseif typ=="gsub_reversecontextchain" or typ=="gpos_reversecontextchain" then - gk.chain=-1 - else - gk.chain=0 - end - local features=gk.features - if features then - sequences[#sequences+1]=gk - local t={} - for f=1,#features do - local feature=features[f] - local hash={} - for s,languages in next,feature.scripts do - s=lower(s) - local h=hash[s] - if not h then h={} hash[s]=h end - for l=1,#languages do - h[strip(lower(languages[l]))]=true - end - end - t[feature.tag]=hash - end - gk.features=t - else - lookups[gk.name]=gk - gk.name=nil - end - local subtables=gk.subtables - if subtables then - local t={} - for s=1,#subtables do - local subtable=subtables[s] - local name=subtable.name - t[#t+1]=name - end - gk.subtables=t - end - local flags=gk.flags - if flags then - gk.flags={ - (flags.ignorecombiningmarks and "mark") or false, - (flags.ignoreligatures and "ligature") or false, - (flags.ignorebaseglyphs and "base") or false, - flags.r2l or false, - } - if flags.mark_class then - gk.markclass=luatex.markclasses[flags.mark_class] - end - end - end - end -end -otf.enhancers["merge cid fonts"]=function(data,filename) - if data.subfonts then - if data.glyphs and next(data.glyphs) then - logs.report("load otf","replacing existing glyph table due to subfonts") - end - local cidinfo=data.cidinfo - local verbose=fonts.verbose - if cidinfo.registry then - local cidmap,cidname=fonts.cid.getmap(cidinfo.registry,cidinfo.ordering,cidinfo.supplement) - if cidmap then - cidinfo.usedname=cidmap.usedname - local glyphs,uni_to_int,int_to_uni,nofnames,nofunicodes={},{},{},0,0 - local unicodes,names=cidmap.unicodes,cidmap.names - for n,subfont in next,data.subfonts do - for index,g in next,subfont.glyphs do - if not next(g) then - else - local unicode,name=unicodes[index],names[index] - g.cidindex=n - g.boundingbox=g.boundingbox - g.name=g.name or name or "unknown" - if unicode then - uni_to_int[unicode]=index - int_to_uni[index]=unicode - nofunicodes=nofunicodes+1 - g.unicode=unicode - elseif name then - nofnames=nofnames+1 - g.unicode=-1 - end - glyphs[index]=g - end - end - subfont.glyphs=nil - end - if trace_loading then - logs.report("load otf","cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes,nofnames,nofunicodes+nofnames) - end - data.glyphs=glyphs - data.map=data.map or {} - data.map.map=uni_to_int - data.map.backmap=int_to_uni - elseif trace_loading then - logs.report("load otf","unable to remap cid font, missing cid file for %s",filename) - end - elseif trace_loading then - logs.report("load otf","font %s has no glyphs",filename) - end - end -end -otf.enhancers["prepare unicode"]=function(data,filename) - local luatex=data.luatex - if not luatex then luatex={} data.luatex=luatex end - local indices,unicodes,multiples,internals={},{},{},{} - local glyphs=data.glyphs - local mapmap=data.map - if not mapmap then - logs.report("load otf","no map in %s",filename) - mapmap={} - data.map={ map=mapmap } - elseif not mapmap.map then - logs.report("load otf","no unicode map in %s",filename) - mapmap={} - data.map.map=mapmap - else - mapmap=mapmap.map - end - local criterium=fonts.private - local private=fonts.private - for index,glyph in next,glyphs do - if index>0 then - local name=glyph.name - if name then - local unicode=glyph.unicode - if unicode==-1 or unicode>=criterium then - glyph.unicode=private - indices[private]=index - unicodes[name]=private - internals[index]=true - if trace_private then - logs.report("load otf","enhance: glyph %s at index U+%04X is moved to private unicode slot U+%04X",name,index,private) - end - private=private+1 - else - indices[unicode]=index - unicodes[name]=unicode - end - end - end - end - for unicode,index in next,mapmap do - if not internals[index] then - local name=glyphs[index].name - if name then - local un=unicodes[name] - if not un then - unicodes[name]=unicode - elseif type(un)=="number" then - if un~=unicode then - multiples[#multiples+1]=name - unicodes[name]={ un,unicode } - indices[unicode]=index - end - else - local ok=false - for u=1,#un do - if un[u]==unicode then - ok=true - break - end - end - if not ok then - multiples[#multiples+1]=name - un[#un+1]=unicode - indices[unicode]=index - end - end - end - end - end - if trace_loading then - if #multiples>0 then - logs.report("load otf","%s glyph are reused: %s",#multiples,concat(multiples," ")) - else - logs.report("load otf","no glyph are reused") - end - end - luatex.indices=indices - luatex.unicodes=unicodes - luatex.private=private -end -otf.enhancers["cleanup ttf tables"]=function(data,filename) - local ttf_tables=data.ttf_tables - if ttf_tables then - for k=1,#ttf_tables do - if ttf_tables[k].data then ttf_tables[k].data="deleted" end - end - end - data.ttf_tab_saved=nil -end -otf.enhancers["compact glyphs"]=function(data,filename) - table.compact(data.glyphs) - if data.subfonts then - for _,subfont in next,data.subfonts do - table.compact(subfont.glyphs) - end - end -end -otf.enhancers["reverse coverage"]=function(data,filename) - if data.lookups then - for _,v in next,data.lookups do - if v.rules then - for _,vv in next,v.rules do - local c=vv.coverage - if c and c.before then - c.before=table.reverse(c.before) - end - end - end - end - end -end -otf.enhancers["check italic correction"]=function(data,filename) - local glyphs=data.glyphs - local ok=false - for index,glyph in next,glyphs do - local ic=glyph.italic_correction - if ic then - if ic~=0 then - glyph.italic=ic - end - glyph.italic_correction=nil - ok=true - end - end - otf.tables.valid_fields[#otf.tables.valid_fields+1]="has_italic" - data.has_italic=true -end -otf.enhancers["check math"]=function(data,filename) - if data.math then - local glyphs=data.glyphs - local unicodes=data.luatex.unicodes - for index,glyph in next,glyphs do - local mk=glyph.mathkern - local hv=glyph.horiz_variants - local vv=glyph.vert_variants - if mk or hv or vv then - local math={} - glyph.math=math - if mk then - for k,v in next,mk do - if not next(v) then - mk[k]=nil - end - end - math.kerns=mk - glyph.mathkern=nil - end - if hv then - math.horiz_variants=hv.variants - local p=hv.parts - if p and #p>0 then - for i=1,#p do - local pi=p[i] - pi.glyph=unicodes[pi.component] or 0 - end - math.horiz_parts=p - end - local ic=hv.italic_correction - if ic and ic~=0 then - math.horiz_italic_correction=ic - end - glyph.horiz_variants=nil - end - if vv then - local uc=unicodes[index] - math.vert_variants=vv.variants - local p=vv.parts - if p and #p>0 then - for i=1,#p do - local pi=p[i] - pi.glyph=unicodes[pi.component] or 0 - end - math.vert_parts=p - end - local ic=vv.italic_correction - if ic and ic~=0 then - math.vert_italic_correction=ic - end - glyph.vert_variants=nil - end - local ic=glyph.italic_correction - if ic then - if ic~=0 then - math.italic_correction=ic - end - glyph.italic_correction=nil - end - end - end - end -end -otf.enhancers["share widths"]=function(data,filename) - local glyphs=data.glyphs - local widths={} - for index,glyph in next,glyphs do - local width=glyph.width - widths[width]=(widths[width] or 0)+1 - end - local wd,most=0,1 - for k,v in next,widths do - if v>most then - wd,most=k,v - end - end - if most>1000 then - if trace_loading then - logs.report("load otf","most common width: %s (%s times), sharing (cjk font)",wd,most) - end - for k,v in next,glyphs do - if v.width==wd then - v.width=nil - end - end - data.luatex.defaultwidth=wd - end -end -otf.enhancers["reorganize kerns"]=function(data,filename) - local glyphs,mapmap,unicodes=data.glyphs,data.luatex.indices,data.luatex.unicodes - local mkdone=false - local function do_it(lookup,first_unicode,kerns) - local glyph=glyphs[mapmap[first_unicode]] - if glyph then - local mykerns=glyph.mykerns - if not mykerns then - mykerns={} - glyph.mykerns=mykerns - end - local lookupkerns=mykerns[lookup] - if not lookupkerns then - lookupkerns={} - mykerns[lookup]=lookupkerns - end - for second_unicode,kern in next,kerns do - lookupkerns[second_unicode]=kern - end - elseif trace_loading then - logs.report("load otf","no glyph data for U+%04X",first_unicode) - end - end - for index,glyph in next,glyphs do - if glyph.kerns then - local mykerns={} - for k,v in next,glyph.kerns do - local vc,vo,vl=v.char,v.off,v.lookup - if vc and vo and vl then - local uvc=unicodes[vc] - if not uvc then - if trace_loading then - logs.report("load otf","problems with unicode %s of kern %s at glyph %s",vc,k,index) - end - else - if type(vl)~="table" then - vl={ vl } - end - for l=1,#vl do - local vll=vl[l] - local mkl=mykerns[vll] - if not mkl then - mkl={} - mykerns[vll]=mkl - end - if type(uvc)=="table" then - for u=1,#uvc do - mkl[uvc[u]]=vo - end - else - mkl[uvc]=vo - end - end - end - end - end - glyph.mykerns=mykerns - glyph.kerns=nil - mkdone=true - end - end - if trace_loading and mkdone then - logs.report("load otf","replacing 'kerns' tables by 'mykerns' tables") - end - if data.kerns then - if trace_loading then - logs.report("load otf","removing global 'kern' table") - end - data.kerns=nil - end - local dgpos=data.gpos - if dgpos then - local separator=lpeg.P(" ") - local other=((1-separator)^0)/unicodes - local splitter=lpeg.Ct(other*(separator*other)^0) - for gp=1,#dgpos do - local gpos=dgpos[gp] - local subtables=gpos.subtables - if subtables then - for s=1,#subtables do - local subtable=subtables[s] - local kernclass=subtable.kernclass - if kernclass then - local split={} - for k=1,#kernclass do - local kcl=kernclass[k] - local firsts,seconds,offsets,lookups=kcl.firsts,kcl.seconds,kcl.offsets,kcl.lookup - if type(lookups)~="table" then - lookups={ lookups } - end - local maxfirsts,maxseconds=#firsts,#seconds - for _,s in next,firsts do - split[s]=split[s] or lpegmatch(splitter,s) - end - for _,s in next,seconds do - split[s]=split[s] or lpegmatch(splitter,s) - end - for l=1,#lookups do - local lookup=lookups[l] - for fk=1,#firsts do - local fv=firsts[fk] - local splt=split[fv] - if splt then - local kerns,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 - local second_unicode=splt[i] - if tonumber(second_unicode) then - kerns[second_unicode]=offset - else for s=1,#second_unicode do - kerns[second_unicode[s]]=offset - end end - end - end - end - end - for i=1,#splt do - local first_unicode=splt[i] - if tonumber(first_unicode) then - do_it(lookup,first_unicode,kerns) - else for f=1,#first_unicode do - do_it(lookup,first_unicode[f],kerns) - end end - end - end - end - end - end - subtable.comment="The kernclass table is merged into mykerns in the indexed glyph tables." - subtable.kernclass={} - end - end - end - end - end -end -otf.enhancers["strip not needed data"]=function(data,filename) - local verbose=fonts.verbose - local int_to_uni=data.luatex.unicodes - for k,v in next,data.glyphs do - local d=v.dependents - if d then v.dependents=nil end - local a=v.altuni - if a then v.altuni=nil end - if verbose then - local code=int_to_uni[k] - if code then - local vu=v.unicode - if not vu then - v.unicode=code - elseif type(vu)=="table" then - if vu[#vu]==code then - else - vu[#vu+1]=code - end - elseif vu~=code then - v.unicode={ vu,code } - end - end - else - v.unicode=nil - v.index=nil - end - end - data.luatex.comment="Glyph tables have their original index. When present, mykern tables are indexed by unicode." - data.map=nil - data.names=nil - data.glyphcnt=nil - data.glyphmax=nil - if true then - data.gpos=nil - data.gsub=nil - data.anchor_classes=nil - end -end -otf.enhancers["migrate metadata"]=function(data,filename) - local global_fields=otf.tables.global_fields - local metadata={} - for k,v in next,data do - if not global_fields[k] then - metadata[k]=v - data[k]=nil - end - end - data.metadata=metadata - local pfminfo=data.pfminfo - metadata.isfixedpitch=metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose["proportion"]=="Monospaced") - metadata.charwidth=pfminfo and pfminfo.avgwidth -end -local private_math_parameters={ - "FractionDelimiterSize", - "FractionDelimiterDisplayStyleSize", -} -otf.enhancers["check math parameters"]=function(data,filename) - local mathdata=data.metadata.math - if mathdata then - for m=1,#private_math_parameters do - local pmp=private_math_parameters[m] - if not mathdata[pmp] then - if trace_loading then - logs.report("load otf","setting math parameter '%s' to 0",pmp) - end - mathdata[pmp]=0 - end - end - end -end -otf.enhancers["flatten glyph lookups"]=function(data,filename) - for k,v in next,data.glyphs do - local lookups=v.lookups - if lookups then - for kk,vv in next,lookups do - for kkk=1,#vv do - local vvv=vv[kkk] - local s=vvv.specification - if s then - local t=vvv.type - if t=="ligature" then - vv[kkk]={ "ligature",s.components,s.char } - elseif t=="alternate" then - vv[kkk]={ "alternate",s.components } - elseif t=="substitution" then - vv[kkk]={ "substitution",s.variant } - elseif t=="multiple" then - vv[kkk]={ "multiple",s.components } - elseif t=="position" then - vv[kkk]={ "position",{ s.x or 0,s.y or 0,s.h or 0,s.v or 0 } } - elseif t=="pair" then - local one,two,paired=s.offsets[1],s.offsets[2],s.paired or "" - if one then - if two then - vv[kkk]={ "pair",paired,{ one.x or 0,one.y or 0,one.h or 0,one.v or 0 },{ two.x or 0,two.y or 0,two.h or 0,two.v or 0 } } - else - vv[kkk]={ "pair",paired,{ one.x or 0,one.y or 0,one.h or 0,one.v or 0 } } - end - else - if two then - vv[kkk]={ "pair",paired,{},{ two.x or 0,two.y or 0,two.h or 0,two.v or 0} } - else - vv[kkk]={ "pair",paired } - end - end - else - if trace_loading then - logs.report("load otf","flattening needed, report to context list") - end - for a,b in next,s do - if trace_loading and vvv[a] then - logs.report("load otf","flattening conflict, report to context list") - end - vvv[a]=b - end - vvv.specification=nil - end - end - end - end - end - end -end -otf.enhancers["simplify glyph lookups"]=function(data,filename) - for k,v in next,data.glyphs do - local lookups=v.lookups - if lookups then - local slookups,mlookups - for kk,vv in next,lookups do - if #vv==1 then - if not slookups then - slookups={} - v.slookups=slookups - end - slookups[kk]=vv[1] - else - if not mlookups then - mlookups={} - v.mlookups=mlookups - end - mlookups[kk]=vv - end - end - v.lookups=nil - end - end -end -otf.enhancers["flatten anchor tables"]=function(data,filename) - for k,v in next,data.glyphs do - if v.anchors then - for kk,vv in next,v.anchors do - for kkk,vvv in next,vv do - if vvv.x or vvv.y then - vv[kkk]={ vvv.x or 0,vvv.y or 0 } - else - for kkkk=1,#vvv do - local vvvv=vvv[kkkk] - vvv[kkkk]={ vvvv.x or 0,vvvv.y or 0 } - end - end - end - end - end - end -end -otf.enhancers["flatten feature tables"]=function(data,filename) - for _,tag in next,otf.glists do - if data[tag] then - if trace_loading then - logs.report("load otf","flattening %s table",tag) - end - for k,v in next,data[tag] do - local features=v.features - if features then - for kk=1,#features do - local vv=features[kk] - local t={} - local scripts=vv.scripts - for kkk=1,#scripts do - local vvv=scripts[kkk] - t[vvv.script]=vvv.langs - end - vv.scripts=t - end - end - end - end - end -end -otf.enhancers.patches=otf.enhancers.patches or {} -otf.enhancers["patch bugs"]=function(data,filename) - local basename=file.basename(lower(filename)) - for pattern,action in next,otf.enhancers.patches do - if find(basename,pattern) then - action(data,filename) - end - end -end -fonts.otf.enhancers["enrich with features"]=function(data,filename) -end -function otf.features.register(name,default) - otf.features.list[#otf.features.list+1]=name - otf.features.default[name]=default -end -function otf.set_features(tfmdata,features) - local processes={} - if features and next(features) then - local lists={ - fonts.triggers, - fonts.processors, - fonts.manipulators, - } - local mode=tfmdata.mode or fonts.mode - local initializers=fonts.initializers - local fi=initializers[mode] - if fi then - local fiotf=fi.otf - if fiotf then - local done={} - for l=1,4 do - local list=lists[l] - if list then - for i=1,#list do - local f=list[i] - local value=features[f] - if value and fiotf[f] then - if not done[f] then - if trace_features then - logs.report("define otf","initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown',tfmdata.fullname or 'unknown') - end - fiotf[f](tfmdata,value) - mode=tfmdata.mode or fonts.mode - local im=initializers[mode] - if im then - fiotf=initializers[mode].otf - end - done[f]=true - end - end - end - end - end - end - end - local fm=fonts.methods[mode] - if fm then - local fmotf=fm.otf - if fmotf then - for l=1,4 do - local list=lists[l] - if list then - for i=1,#list do - local f=list[i] - if fmotf[f] then - if trace_features then - logs.report("define otf","installing feature handler %s for mode %s for font %s",f,mode or 'unknown',tfmdata.fullname or 'unknown') - end - processes[#processes+1]=fmotf[f] - end - end - end - end - end - else - end - end - return processes,features -end -function otf.otf_to_tfm(specification) - local name=specification.name - local sub=specification.sub - local filename=specification.filename - local format=specification.format - local features=specification.features.normal - local cache_id=specification.hash - local tfmdata=containers.read(tfm.cache,cache_id) - if not tfmdata then - local otfdata=otf.load(filename,format,sub,features and features.featurefile) - if otfdata and next(otfdata) then - otfdata.shared=otfdata.shared or { - featuredata={}, - anchorhash={}, - initialized=false, - } - tfmdata=otf.copy_to_tfm(otfdata,cache_id) - if tfmdata and next(tfmdata) then - tfmdata.unique=tfmdata.unique or {} - tfmdata.shared=tfmdata.shared or {} - local shared=tfmdata.shared - shared.otfdata=otfdata - shared.features=features - shared.dynamics={} - shared.processes={} - shared.set_dynamics=otf.set_dynamics - tfmdata.luatex=otfdata.luatex - tfmdata.indices=otfdata.luatex.indices - tfmdata.unicodes=otfdata.luatex.unicodes - tfmdata.marks=otfdata.luatex.marks - tfmdata.originals=otfdata.luatex.originals - tfmdata.changed={} - tfmdata.has_italic=otfdata.metadata.has_italic - if not tfmdata.language then tfmdata.language='dflt' end - if not tfmdata.script then tfmdata.script='dflt' end - shared.processes,shared.features=otf.set_features(tfmdata,fonts.define.check(features,otf.features.default)) - end - end - containers.write(tfm.cache,cache_id,tfmdata) - end - return tfmdata -end -fonts.formats.dfont="truetype" -fonts.formats.ttc="truetype" -fonts.formats.ttf="truetype" -fonts.formats.otf="opentype" -function otf.copy_to_tfm(data,cache_id) - if data then - local glyphs,pfminfo,metadata=data.glyphs or {},data.pfminfo or {},data.metadata or {} - local luatex=data.luatex - local unicodes=luatex.unicodes - local indices=luatex.indices - local characters,parameters,math_parameters,descriptions={},{},{},{} - local designsize=metadata.designsize or metadata.design_size or 100 - if designsize==0 then - designsize=100 - end - local spaceunits,spacer=500,"space" - for u,i in next,indices do - characters[u]={} - descriptions[u]=glyphs[i] - end - if metadata.math then - for name,value in next,metadata.math do - math_parameters[name]=value - end - for u,char in next,characters do - local d=descriptions[u] - local m=d.math - if m then - local variants,parts,c,uc=m.horiz_variants,m.horiz_parts,char,u - if variants then - for n in gmatch(variants,"[^ ]+") do - local un=unicodes[n] - if un and uc~=un then - c.next=un - c=characters[un] - uc=un - end - end - c.horiz_variants=parts - elseif parts then - c.horiz_variants=parts - end - local variants,parts,c,uc=m.vert_variants,m.vert_parts,char,u - if variants then - for n in gmatch(variants,"[^ ]+") do - local un=unicodes[n] - if un and uc~=un then - c.next=un - c=characters[un] - uc=un - end - end - c.vert_variants=parts - elseif parts then - c.vert_variants=parts - end - local italic_correction=m.vert_italic_correction - if italic_correction then - c.vert_italic_correction=italic_correction - end - local kerns=m.kerns - if kerns then - char.mathkerns=kerns - end - end - end - end - local endash,emdash,space=0x20,0x2014,"space" - if metadata.isfixedpitch then - if descriptions[endash] then - spaceunits,spacer=descriptions[endash].width,"space" - end - if not spaceunits and descriptions[emdash] then - spaceunits,spacer=descriptions[emdash].width,"emdash" - end - if not spaceunits and metadata.charwidth then - spaceunits,spacer=metadata.charwidth,"charwidth" - end - else - if descriptions[endash] then - spaceunits,spacer=descriptions[endash].width,"space" - end - if not spaceunits and descriptions[emdash] then - spaceunits,spacer=descriptions[emdash].width/2,"emdash/2" - end - if not spaceunits and metadata.charwidth then - spaceunits,spacer=metadata.charwidth,"charwidth" - end - end - spaceunits=tonumber(spaceunits) or tfm.units/2 - local filename=fonts.tfm.checked_filename(luatex) - local fontname=metadata.fontname - local fullname=metadata.fullname or fontname - local cidinfo=data.cidinfo - local units=metadata.units_per_em or 1000 - cidinfo.registry=cidinfo and cidinfo.registry or "" - 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 - local italicangle=metadata.italicangle - if italicangle then - parameters.slant=parameters.slant-math.round(math.tan(italicangle*math.pi/180)) - end - if metadata.isfixedpitch then - parameters.space_stretch=0 - parameters.space_shrink=0 - elseif otf.syncspace then - parameters.space_stretch=spaceunits/2 - parameters.space_shrink=spaceunits/3 - end - parameters.extra_space=parameters.space_shrink - if pfminfo.os2_xheight and pfminfo.os2_xheight>0 then - parameters.x_height=pfminfo.os2_xheight - else - local x=0x78 - if x then - local x=descriptions[x] - if x then - parameters.x_height=x.height - end - end - end - return { - characters=characters, - parameters=parameters, - math_parameters=math_parameters, - descriptions=descriptions, - indices=indices, - unicodes=unicodes, - type="real", - direction=0, - boundarychar_label=0, - boundarychar=65536, - designsize=(designsize/10)*65536, - spacer="500 units", - encodingbytes=2, - filename=filename, - fontname=fontname, - fullname=fullname, - psname=fontname or fullname, - name=filename or fullname, - units=units, - format=fonts.fontformat(filename,"opentype"), - cidinfo=cidinfo, - ascender=abs(metadata.ascent or 0), - descender=abs(metadata.descent or 0), - spacer=spacer, - italicangle=italicangle, - } - else - return nil - end -end -otf.features.register('mathsize') -function tfm.read_from_open_type(specification) - local tfmtable=otf.otf_to_tfm(specification) - if tfmtable then - local otfdata=tfmtable.shared.otfdata - tfmtable.name=specification.name - tfmtable.sub=specification.sub - local s=specification.size - local m=otfdata.metadata.math - if m then - local f=specification.features - if f then - local f=f.normal - if f and f.mathsize then - local mathsize=specification.mathsize or 0 - if mathsize==2 then - local p=m.ScriptPercentScaleDown - if p then - local ps=p*specification.textsize/100 - if trace_math then - logs.report("define font","asked script size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100) - end - s=ps - end - elseif mathsize==3 then - local p=m.ScriptScriptPercentScaleDown - if p then - local ps=p*specification.textsize/100 - if trace_math then - logs.report("define font","asked scriptscript size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100) - end - s=ps - end - end - end - end - end - tfmtable=tfm.scale(tfmtable,s,specification.relativeid) - if tfm.fontname_mode=="specification" then - local specname=specification.specification - if specname then - tfmtable.name=specname - if trace_defining then - logs.report("define font","overloaded fontname: '%s'",specname) - end - end - end - fonts.logger.save(tfmtable,file.extname(specification.filename),specification) - end - return tfmtable -end -function otf.collect_lookups(otfdata,kind,script,language) - local sequences=otfdata.luatex.sequences - if sequences then - local featuremap,featurelist={},{} - for s=1,#sequences do - local sequence=sequences[s] - local features=sequence.features - features=features and features[kind] - features=features and (features[script] or features[default] or features[wildcard]) - features=features and (features[language] or features[default] or features[wildcard]) - if features then - local subtables=sequence.subtables - if subtables then - for s=1,#subtables do - local ss=subtables[s] - if not featuremap[s] then - featuremap[ss]=true - featurelist[#featurelist+1]=ss - end - end - end - end - end - if #featurelist>0 then - return featuremap,featurelist - end - end - return nil,nil -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-otd']={ - version=1.001, - comment="companion to font-ini.mkiv", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -local trace_dynamics=false trackers.register("otf.dynamics",function(v) trace_dynamics=v end) -fonts=fonts or {} -fonts.otf=fonts.otf or {} -local otf=fonts.otf -local fontdata=fonts.ids -otf.features=otf.features or {} -otf.features.default=otf.features.default or {} -local context_setups=fonts.define.specify.context_setups -local context_numbers=fonts.define.specify.context_numbers -local a_to_script={} otf.a_to_script=a_to_script -local a_to_language={} otf.a_to_language=a_to_language -function otf.set_dynamics(font,dynamics,attribute) - local features=context_setups[context_numbers[attribute]] - if features then - local script=features.script or 'dflt' - local language=features.language or 'dflt' - local ds=dynamics[script] - if not ds then - ds={} - dynamics[script]=ds - end - local dsl=ds[language] - if not dsl then - dsl={} - ds[language]=dsl - end - local dsla=dsl[attribute] - if dsla then - return dsla - else - local tfmdata=fontdata[font] - a_to_script [attribute]=script - a_to_language[attribute]=language - local saved={ - script=tfmdata.script, - language=tfmdata.language, - mode=tfmdata.mode, - features=tfmdata.shared.features - } - tfmdata.mode="node" - tfmdata.language=language - tfmdata.script=script - tfmdata.shared.features={} - local set=fonts.define.check(features,otf.features.default) - dsla=otf.set_features(tfmdata,set) - if trace_dynamics then - logs.report("otf define","setting dynamics %s: attribute %s, script %s, language %s, set: %s",context_numbers[attribute],attribute,script,language,table.sequenced(set)) - end - tfmdata.script=saved.script - tfmdata.language=saved.language - tfmdata.mode=saved.mode - tfmdata.shared.features=saved.features - dynamics[script][language][attribute]=dsla - return dsla - end - end - return nil -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 otf=fonts.otf -otf.default_language='latn' -otf.default_script='dflt' -local languages=otf.tables.languages -local scripts=otf.tables.scripts -function otf.features.language(tfmdata,value) - if value then - value=lower(value) - if languages[value] then - tfmdata.language=value - end - end -end -function otf.features.script(tfmdata,value) - if value then - value=lower(value) - if scripts[value] then - tfmdata.script=value - end - end -end -function otf.features.mode(tfmdata,value) - if value then - tfmdata.mode=lower(value) - end -end -fonts.initializers.base.otf.language=otf.features.language -fonts.initializers.base.otf.script=otf.features.script -fonts.initializers.base.otf.mode=otf.features.mode -fonts.initializers.base.otf.method=otf.features.mode -fonts.initializers.node.otf.language=otf.features.language -fonts.initializers.node.otf.script=otf.features.script -fonts.initializers.node.otf.mode=otf.features.mode -fonts.initializers.node.otf.method=otf.features.mode -otf.features.register("features",true) -table.insert(fonts.processors,"features") - -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 otf=fonts.otf -local tfm=fonts.tfm -local trace_baseinit=false trackers.register("otf.baseinit",function(v) trace_baseinit=v end) -local trace_singles=false trackers.register("otf.singles",function(v) trace_singles=v end) -local trace_multiples=false trackers.register("otf.multiples",function(v) trace_multiples=v end) -local trace_alternatives=false trackers.register("otf.alternatives",function(v) trace_alternatives=v end) -local trace_ligatures=false trackers.register("otf.ligatures",function(v) trace_ligatures=v end) -local trace_kerns=false trackers.register("otf.kerns",function(v) trace_kerns=v end) -local trace_preparing=false trackers.register("otf.preparing",function(v) trace_preparing=v end) -local wildcard="*" -local default="dflt" -local split_at_space=lpeg.Ct(lpeg.splitat(" ")) -local pcache,fcache={},{} -local function gref(descriptions,n) - if type(n)=="number" then - local name=descriptions[n].name - if name then - return format("U+%04X (%s)",n,name) - else - return format("U+%04X") - end - elseif n then - local num,nam={},{} - for i=1,#n do - local ni=n[i] - num[i]=format("U+%04X",ni) - nam[i]=descriptions[ni].name or "?" - end - return format("%s (%s)",concat(num," "),concat(nam," ")) - else - return "?" - end -end -local function cref(kind,lookupname) - if lookupname then - return format("feature %s, lookup %s",kind,lookupname) - else - return format("feature %s",kind) - end -end -local function resolve_ligatures(tfmdata,ligatures,kind) - kind=kind or "unknown" - local unicodes=tfmdata.unicodes - local characters=tfmdata.characters - local descriptions=tfmdata.descriptions - local changed=tfmdata.changed - local done={} - while true do - local ok=false - for k,v in next,ligatures do - local lig=v[1] - if not done[lig] then - local ligs=lpegmatch(split_at_space,lig) - if #ligs==2 then - local uc=v[2] - local c,f,s=characters[uc],ligs[1],ligs[2] - local uft,ust=unicodes[f] or 0,unicodes[s] or 0 - if not uft or not ust then - logs.report("define otf","%s: unicode problem with base ligature %s = %s + %s",cref(kind),gref(descriptions,uc),gref(descriptions,uft),gref(descriptions,ust)) - else - if type(uft)=="number" then uft={ uft } end - if type(ust)=="number" then ust={ ust } end - for ufi=1,#uft do - local uf=uft[ufi] - for usi=1,#ust do - local us=ust[usi] - if changed[uf] or changed[us] then - if trace_baseinit and trace_ligatures then - logs.report("define otf","%s: base ligature %s + %s ignored",cref(kind),gref(descriptions,uf),gref(descriptions,us)) - end - else - local first,second=characters[uf],us - if first and second then - local t=first.ligatures - if not t then - t={} - first.ligatures=t - end - if type(uc)=="number" then - t[second]={ type=0,char=uc } - else - t[second]={ type=0,char=uc[1] } - end - if trace_baseinit and trace_ligatures then - logs.report("define otf","%s: base ligature %s + %s => %s",cref(kind),gref(descriptions,uf),gref(descriptions,us),gref(descriptions,uc)) - end - end - end - end - end - end - ok,done[lig]=true,descriptions[uc].name - end - end - end - if ok then - for d,n in next,done do - local pattern=pcache[d] if not pattern then pattern="^("..d..") " pcache[d]=pattern end - local fnc=fcache[n] if not fnc then fnc=function() return n.." " end fcache[n]=fnc end - for k,v in next,ligatures do - v[1]=gsub(v[1],pattern,fnc) - end - end - else - break - end - end -end -local splitter=lpeg.splitat(" ") -local function prepare_base_substitutions(tfmdata,kind,value) - if value then - local otfdata=tfmdata.shared.otfdata - local validlookups,lookuplist=otf.collect_lookups(otfdata,kind,tfmdata.script,tfmdata.language) - if validlookups then - local ligatures={} - local unicodes=tfmdata.unicodes - local indices=tfmdata.indices - local characters=tfmdata.characters - local descriptions=tfmdata.descriptions - local changed=tfmdata.changed - local actions={ - substitution=function(p,lookup,k,glyph,unicode) - local pv=p[2] - if pv then - local upv=unicodes[pv] - if upv then - if type(upv)=="table" then - upv=upv[1] - end - if characters[upv] then - if trace_baseinit and trace_singles then - logs.report("define otf","%s: base substitution %s => %s",cref(kind,lookup),gref(descriptions,k),gref(descriptions,upv)) - end - changed[k]=upv - end - end - end - end, - alternate=function(p,lookup,k,glyph,unicode) - local pc=p[2] - if pc then - if value==1 then - pc=lpegmatch(splitter,pc) - elseif value==2 then - local a,b=lpegmatch(splitter,pc) - pc=b or a - else - pc={ lpegmatch(splitter,pc) } - pc=pc[value] or pc[#pc] - end - if pc then - local upc=unicodes[pc] - if upc then - if type(upc)=="table" then - upc=upc[1] - end - if characters[upc] then - if trace_baseinit and trace_alternatives then - logs.report("define otf","%s: base alternate %s %s => %s",cref(kind,lookup),tostring(value),gref(descriptions,k),gref(descriptions,upc)) - end - changed[k]=upc - end - end - end - end - end, - ligature=function(p,lookup,k,glyph,unicode) - local pc=p[2] - if pc then - if trace_baseinit and trace_ligatures then - local upc={ lpegmatch(splitter,pc) } - for i=1,#upc do upc[i]=unicodes[upc[i]] end - logs.report("define otf","%s: base ligature %s => %s",cref(kind,lookup),gref(descriptions,upc),gref(descriptions,k)) - end - ligatures[#ligatures+1]={ pc,k } - end - end, - } - for k,c in next,characters do - local glyph=descriptions[k] - local lookups=glyph.slookups - if lookups then - for l=1,#lookuplist do - local lookup=lookuplist[l] - local p=lookups[lookup] - if p then - local a=actions[p[1]] - if a then - a(p,lookup,k,glyph,unicode) - end - end - end - end - local lookups=glyph.mlookups - if lookups then - for l=1,#lookuplist do - local lookup=lookuplist[l] - local ps=lookups[lookup] - if ps then - for i=1,#ps do - local p=ps[i] - local a=actions[p[1]] - if a then - a(p,lookup,k,glyph,unicode) - end - end - end - end - end - end - resolve_ligatures(tfmdata,ligatures,kind) - end - else - tfmdata.ligatures=tfmdata.ligatures or {} - end -end -local function prepare_base_kerns(tfmdata,kind,value) - if value then - local otfdata=tfmdata.shared.otfdata - local validlookups,lookuplist=otf.collect_lookups(otfdata,kind,tfmdata.script,tfmdata.language) - if validlookups then - local unicodes=tfmdata.unicodes - local indices=tfmdata.indices - local characters=tfmdata.characters - local descriptions=tfmdata.descriptions - local sharedkerns={} - for u,chr in next,characters do - local d=descriptions[u] - if d then - local dk=d.mykerns - if dk then - local s=sharedkerns[dk] - if s==false then - elseif s then - chr.kerns=s - else - local t,done=chr.kerns or {},false - for l=1,#lookuplist do - local lookup=lookuplist[l] - local kerns=dk[lookup] - if kerns then - for k,v in next,kerns do - if v~=0 and not t[k] then - t[k],done=v,true - if trace_baseinit and trace_kerns then - logs.report("define otf","%s: base kern %s + %s => %s",cref(kind,lookup),gref(descriptions,u),gref(descriptions,k),v) - end - end - end - end - end - if done then - sharedkerns[dk]=t - chr.kerns=t - else - sharedkerns[dk]=false - end - end - end - end - end - end - end -end -local supported_gsub={ - 'liga','dlig','rlig','hlig', - 'pnum','onum','tnum','lnum', - 'zero', - 'smcp','cpsp','c2sc','ornm','aalt', - 'hwid','fwid', - 'ssty','rtlm', -} -local supported_gpos={ - 'kern' -} -function otf.features.register_base_substitution(tag) - supported_gsub[#supported_gsub+1]=tag -end -function otf.features.register_base_kern(tag) - supported_gsub[#supported_gpos+1]=tag -end -local basehash,basehashes={},1 -function fonts.initializers.base.otf.features(tfmdata,value) - if true then - local t=trace_preparing and os.clock() - local features=tfmdata.shared.features - if features then - local h={} - for f=1,#supported_gsub do - local feature=supported_gsub[f] - local value=features[feature] - prepare_base_substitutions(tfmdata,feature,value) - if value then - h[#h+1]=feature.."="..tostring(value) - end - end - for f=1,#supported_gpos do - local feature=supported_gpos[f] - local value=features[feature] - prepare_base_kerns(tfmdata,feature,features[feature]) - if value then - h[#h+1]=feature.."="..tostring(value) - end - end - local hash=concat(h," ") - local base=basehash[hash] - if not base then - basehashes=basehashes+1 - base=basehashes - basehash[hash]=base - end - tfmdata.fullname=tfmdata.fullname.."-"..base - end - if trace_preparing then - logs.report("otf define","preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?") - end - end -end - -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 format,gmatch,gsub,find,match,lower,strip=string.format,string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip -local type,next,tonumber,tostring=type,next,tonumber,tostring -local lpegmatch=lpeg.match -local otf=fonts.otf -local tfm=fonts.tfm -local trace_lookups=false trackers.register("otf.lookups",function(v) trace_lookups=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_contexts=false trackers.register("otf.contexts",function(v) trace_contexts=v end) -local trace_marks=false trackers.register("otf.marks",function(v) trace_marks=v end) -local trace_kerns=false trackers.register("otf.kerns",function(v) trace_kerns=v end) -local trace_cursive=false trackers.register("otf.cursive",function(v) trace_cursive=v end) -local trace_preparing=false trackers.register("otf.preparing",function(v) trace_preparing=v end) -local trace_bugs=false trackers.register("otf.bugs",function(v) trace_bugs=v end) -local trace_details=false trackers.register("otf.details",function(v) trace_details=v end) -local trace_applied=false trackers.register("otf.applied",function(v) trace_applied=v end) -local trace_steps=false trackers.register("otf.steps",function(v) trace_steps=v end) -local trace_skips=false trackers.register("otf.skips",function(v) trace_skips=v end) -local trace_directions=false trackers.register("otf.directions",function(v) trace_directions=v end) -trackers.register("otf.verbose_chain",function(v) otf.setcontextchain(v and "verbose") end) -trackers.register("otf.normal_chain",function(v) otf.setcontextchain(v and "normal") end) -trackers.register("otf.replacements","otf.singles,otf.multiples,otf.alternatives,otf.ligatures") -trackers.register("otf.positions","otf.marks,otf.kerns,otf.cursive") -trackers.register("otf.actions","otf.replacements,otf.positions") -trackers.register("otf.injections","nodes.injections") -trackers.register("*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 set_attribute=node.set_attribute -local has_attribute=node.has_attribute -local zwnj=0x200C -local zwj=0x200D -local wildcard="*" -local default="dflt" -local split_at_space=lpeg.splitters[" "] or lpeg.Ct(lpeg.splitat(" ")) -local glyph=node.id('glyph') -local glue=node.id('glue') -local kern=node.id('kern') -local disc=node.id('disc') -local whatsit=node.id('whatsit') -local state=attributes.private('state') -local markbase=attributes.private('markbase') -local markmark=attributes.private('markmark') -local markdone=attributes.private('markdone') -local cursbase=attributes.private('cursbase') -local curscurs=attributes.private('curscurs') -local cursdone=attributes.private('cursdone') -local kernpair=attributes.private('kernpair') -local set_mark=nodes.set_mark -local set_cursive=nodes.set_cursive -local set_kern=nodes.set_kern -local set_pair=nodes.set_pair -local markonce=true -local cursonce=true -local kernonce=true -local fontdata=fonts.ids -otf.features.process={} -local tfmdata=false -local otfdata=false -local characters=false -local descriptions=false -local marks=false -local indices=false -local unicodes=false -local currentfont=false -local lookuptable=false -local anchorlookups=false -local handlers={} -local rlmode=0 -local featurevalue=false -local context_setups=fonts.define.specify.context_setups -local context_numbers=fonts.define.specify.context_numbers -local context_merged=fonts.define.specify.context_merged -local special_attributes={ - init=1, - medi=2, - fina=3, - isol=4 -} -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 - logs.report("otf direct",...) -end -local function logwarning(...) - logs.report("otf direct",...) -end -local function gref(n) - if type(n)=="number" then - local description=descriptions[n] - local name=description and description.name - if name then - return format("U+%04X (%s)",n,name) - else - return format("U+%04X",n) - end - elseif not n then - return "<error in tracing>" - else - local num,nam={},{} - for i=1,#n do - local ni=n[i] - num[#num+1]=format("U+%04X",ni) - local dni=descriptions[ni] - nam[#num]=(dni and dni.name) or "?" - end - return format("%s (%s)",concat(num," "),concat(nam," ")) - end -end -local function cref(kind,chainname,chainlookupname,lookupname,index) - if index then - return format("feature %s, chain %s, sub %s, lookup %s, index %s",kind,chainname,chainlookupname,lookupname,index) - elseif lookupname then - return format("feature %s, chain %s, sub %s, lookup %s",kind,chainname or "?",chainlookupname or "?",lookupname) - elseif chainlookupname then - return format("feature %s, chain %s, sub %s",kind,chainname or "?",chainlookupname) - elseif chainname then - return format("feature %s, chain %s",kind,chainname) - else - return format("feature %s",kind) - end -end -local function pref(kind,lookupname) - return format("feature %s, lookup %s",kind,lookupname) -end -local function markstoligature(kind,lookupname,start,stop,char) - local n=copy_node(start) - local keep=start - local current - current,start=insert_node_after(start,start,n) - local snext=stop.next - current.next=snext - if snext then - snext.prev=current - end - start.prev,stop.next=nil,nil - current.char,current.subtype,current.components=char,2,start - return keep -end -local function toligature(kind,lookupname,start,stop,char,markflag,discfound) - if start~=stop then - if discfound then - local lignode=copy_node(start) - lignode.font,lignode.char,lignode.subtype=start.font,char,2 - local next,prev=stop.next,start.prev - stop.next=nil - lignode=node.do_ligature_n(start,stop,lignode) - prev.next=lignode - if next then - next.prev=lignode - end - lignode.next,lignode.prev=next,prev - start=lignode - else - local deletemarks=markflag~="mark" - local n=copy_node(start) - local current - current,start=insert_node_after(start,start,n) - local snext=stop.next - current.next=snext - if snext then - snext.prev=current - end - start.prev,stop.next=nil,nil - current.char,current.subtype,current.components=char,2,start - local head=current - if deletemarks then - if trace_marks then - while start do - if marks[start.char] then - logwarning("%s: remove mark %s",pref(kind,lookupname),gref(start.char)) - end - start=start.next - end - end - else - local i=0 - while start do - if marks[start.char] then - set_attribute(start,markdone,i) - if trace_marks then - logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) - end - head,current=insert_node_after(head,current,copy_node(start)) - else - i=i+1 - end - start=start.next - end - start=current.next - while start and start.id==glyph do - if marks[start.char] then - set_attribute(start,markdone,i) - if trace_marks then - logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) - end - else - break - end - start=start.next - end - end - return head - end - else - start.char=char - end - return start -end -function handlers.gsub_single(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 start,true -end -local function alternative_glyph(start,alternatives,kind,chainname,chainlookupname,lookupname) - local value,choice,n=featurevalue or tfmdata.shared.features[kind],nil,#alternatives - if value=="random" then - local r=math.random(1,n) - value,choice=format("random, choice %s",r),alternatives[r] - elseif value=="first" then - value,choice=format("first, choice %s",1),alternatives[1] - elseif value=="last" then - value,choice=format("last, choice %s",n),alternatives[n] - else - value=tonumber(value) - if type(value)~="number" then - value,choice="default, choice 1",alternatives[1] - elseif value>n then - value,choice=format("no %s variants, taking %s",value,n),alternatives[n] - elseif value==0 then - value,choice=format("choice %s (no change)",value),start.char - elseif value<1 then - value,choice=format("no %s variants, taking %s",value,1),alternatives[1] - else - value,choice=format("choice %s",value),alternatives[value] - end - end - if not choice then - logwarning("%s: no variant %s for %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(start.char)) - choice,value=start.char,format("no replacement instead of %s",value) - end - return choice,value -end -function handlers.gsub_alternate(start,kind,lookupname,alternative,sequence) - local choice,index=alternative_glyph(start,alternative,kind,lookupname) - if trace_alternatives then - logprocess("%s: replacing %s by alternative %s (%s)",pref(kind,lookupname),gref(start.char),gref(choice),index) - end - start.char=choice - return start,true -end -function handlers.gsub_multiple(start,kind,lookupname,multiple) - if trace_multiples then - logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple)) - end - start.char=multiple[1] - if #multiple>1 then - for k=2,#multiple do - local n=copy_node(start) - n.char=multiple[k] - local sn=start.next - n.next=sn - n.prev=start - if sn then - sn.prev=n - end - start.next=n - start=n - end - end - return start,true -end -function handlers.gsub_ligature(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 and s.subtype<256 then - if s.font==currentfont then - local char=s.char - local lg=ligature[1][char] - if not lg then - break - else - stop=s - ligature=lg - s=s.next - end - else - break - end - else - break - end - end - if stop and ligature[2] then - if trace_ligatures then - local stopchar=stop.char - start=markstoligature(kind,lookupname,start,stop,ligature[2]) - logprocess("%s: replacing %s upto %s by ligature %s",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) - else - start=markstoligature(kind,lookupname,start,stop,ligature[2]) - end - return start,true - end - else - local skipmark=sequence.flags[1] - while s do - local id=s.id - if id==glyph 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[1][char] - if not lg then - break - else - stop=s - ligature=lg - s=s.next - end - end - else - break - end - elseif id==disc then - discfound=true - s=s.next - else - break - end - end - if stop and ligature[2] then - if trace_ligatures then - local stopchar=stop.char - start=toligature(kind,lookupname,start,stop,ligature[2],skipmark,discfound) - logprocess("%s: replacing %s upto %s by ligature %s",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) - else - start=toligature(kind,lookupname,start,stop,ligature[2],skipmark,discfound) - end - return start,true - end - end - return start,false -end -function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence) - local markchar=start.char - if marks[markchar] then - local base=start.prev - if base and base.id==glyph and base.subtype<256 and base.font==currentfont then - local basechar=base.char - if marks[basechar] then - while true do - base=base.prev - if base and base.id==glyph and base.subtype<256 and base.font==currentfont then - basechar=base.char - if not marks[basechar] then - break - end - else - if trace_bugs then - logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) - end - return start,false - end - end - end - local baseanchors=descriptions[basechar] - if baseanchors then - baseanchors=baseanchors.anchors - end - if baseanchors then - local baseanchors=baseanchors['basechar'] - if baseanchors then - local al=anchorlookups[lookupname] - for anchor,ba in next,baseanchors do - if al[anchor] then - local ma=markanchors[anchor] - if ma then - local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma) - if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)", - pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) - end - return start,true - end - end - end - if trace_bugs then - logwarning("%s, no matching anchors for mark %s and base %s",pref(kind,lookupname),gref(markchar),gref(basechar)) - end - end - else - fonts.register_message(currentfont,basechar,"no base anchors") - end - elseif trace_bugs then - logwarning("%s: prev node is no char",pref(kind,lookupname)) - end - elseif trace_bugs then - logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) - end - return start,false -end -function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) - local markchar=start.char - if marks[markchar] then - local base=start.prev - local index=1 - if base and base.id==glyph and base.subtype<256 and base.font==currentfont then - local basechar=base.char - if marks[basechar] then - index=index+1 - while true do - base=base.prev - if base and base.id==glyph and base.subtype<256 and base.font==currentfont then - basechar=base.char - if marks[basechar] then - index=index+1 - else - break - end - else - if trace_bugs then - logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) - end - return start,false - end - end - end - local i=has_attribute(start,markdone) - if i then index=i end - local baseanchors=descriptions[basechar] - if baseanchors then - baseanchors=baseanchors.anchors - if baseanchors then - local baseanchors=baseanchors['baselig'] - if baseanchors then - local al=anchorlookups[lookupname] - for anchor,ba in next,baseanchors do - if al[anchor] then - local ma=markanchors[anchor] - if ma then - ba=ba[index] - if ba then - local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma,index) - if trace_marks then - logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)", - pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) - end - return start,true - end - end - end - end - if trace_bugs then - logwarning("%s: no matching anchors for mark %s and baselig %s",pref(kind,lookupname),gref(markchar),gref(basechar)) - end - end - end - else - fonts.register_message(currentfont,basechar,"no base anchors") - end - elseif trace_bugs then - logwarning("%s: prev node is no char",pref(kind,lookupname)) - end - elseif trace_bugs then - logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) - end - return start,false -end -function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) - local markchar=start.char - if marks[markchar] then - local base=start.prev - if base and base.id==glyph and base.subtype<256 and base.font==currentfont 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=set_mark(start,base,tfmdata.factor,rlmode,ba,ma) - if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", - pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) - end - return start,true - end - end - end - if trace_bugs then - logwarning("%s: no matching anchors for mark %s and basemark %s",pref(kind,lookupname),gref(markchar),gref(basechar)) - end - end - end - else - fonts.register_message(currentfont,basechar,"no base anchors") - end - elseif trace_bugs then - logwarning("%s: prev node is no mark",pref(kind,lookupname)) - end - elseif trace_bugs then - logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) - end - return start,false -end -function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence) - local alreadydone=cursonce and has_attribute(start,cursbase) - if not alreadydone then - local done=false - local startchar=start.char - if marks[startchar] then - if trace_cursive then - logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) - end - else - local nxt=start.next - while not done and nxt and nxt.id==glyph and nxt.subtype<256 and nxt.font==currentfont do - local nextchar=nxt.char - if marks[nextchar] then - nxt=nxt.next - else - local entryanchors=descriptions[nextchar] - if entryanchors then - entryanchors=entryanchors.anchors - if entryanchors then - entryanchors=entryanchors['centry'] - if entryanchors then - local al=anchorlookups[lookupname] - for anchor,entry in next,entryanchors do - if al[anchor] then - local exit=exitanchors[anchor] - if exit then - local dx,dy,bound=set_cursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) - if trace_cursive then - logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) - end - done=true - break - end - end - end - end - end - else - fonts.register_message(currentfont,startchar,"no entry anchors") - end - break - end - end - end - return start,done - else - if trace_cursive and trace_details then - logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) - end - return start,false - end -end -function handlers.gpos_single(start,kind,lookupname,kerns,sequence) - local startchar=start.char - local dx,dy,w,h=set_pair(start,tfmdata.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) - if trace_kerns then - logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) - end - return start,false -end -function handlers.gpos_pair(start,kind,lookupname,kerns,sequence) - local snext=start.next - if not snext then - return start,false - else - local prev,done=start,false - local factor=tfmdata.factor - while snext and snext.id==glyph and snext.subtype<256 and snext.font==currentfont do - local nextchar=snext.char -local krn=kerns[nextchar] - if not krn and marks[nextchar] then - prev=snext - snext=snext.next - else - local krn=kerns[nextchar] - if not krn then - elseif type(krn)=="table" then - if krn[1]=="pair" then - local a,b=krn[3],krn[4] - if a and #a>0 then - local startchar=start.char - local x,y,w,h=set_pair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) - if trace_kerns then - logprocess("%s: shifting first of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) - end - end - if b and #b>0 then - local startchar=start.char - local x,y,w,h=set_pair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) - if trace_kerns then - logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) - end - end - else - logs.report("%s: check this out (old kern stuff)",pref(kind,lookupname)) - local a,b=krn[3],krn[7] - if a and a~=0 then - local k=set_kern(snext,factor,rlmode,a) - if trace_kerns then - logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) - end - end - if b and b~=0 then - logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor) - end - end - done=true - elseif krn~=0 then - local k=set_kern(snext,factor,rlmode,krn) - if trace_kerns then - logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) - end - done=true - end - break - end - end - return start,done - end -end -local chainmores={} -local chainprocs={} -local function logprocess(...) - if trace_steps then - registermessage(...) - end - logs.report("otf subchain",...) -end -local function logwarning(...) - logs.report("otf subchain",...) -end -function chainmores.chainsub(start,stop,kind,chainname,currentcontext,cache,lookuplist,chainlookupname,n) - logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) - return start,false -end -function chainmores.gsub_multiple(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) - logprocess("%s: gsub_multiple not yet supported",cref(kind,chainname,chainlookupname)) - return start,false -end -function chainmores.gsub_alternate(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) - logprocess("%s: gsub_alternate not yet supported",cref(kind,chainname,chainlookupname)) - return start,false -end -local function logprocess(...) - if trace_steps then - registermessage(...) - end - logs.report("otf chain",...) -end -local function logwarning(...) - logs.report("otf chain",...) -end -function chainprocs.chainsub(start,stop,kind,chainname,currentcontext,cache,lookuplist,chainlookupname) - logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) - return start,false -end -function chainprocs.reversesub(start,stop,kind,chainname,currentcontext,cache,replacements) - local char=start.char - local replacement=replacements[char] - if replacement then - if trace_singles then - logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement)) - end - start.char=replacement - return start,true - else - return start,false - end -end -local function delete_till_stop(start,stop,ignoremarks) - if start~=stop then - local done=false - while not done do - done=start==stop - delete_node(start,start.next) - end - end -end -function chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex) - if not chainindex then - delete_till_stop(start,stop) - end - local current=start - local subtables=currentlookup.subtables - while current do - if current.id==glyph then - local currentchar=current.char - local lookupname=subtables[1] - local replacement=cache.gsub_single[lookupname] - if not replacement then - if trace_bugs then - logwarning("%s: no single hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) - end - else - replacement=replacement[currentchar] - if not replacement then - if trace_bugs then - logwarning("%s: no single for %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar)) - end - else - if trace_singles then - logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement)) - end - current.char=replacement - end - end - return start,true - elseif current==stop then - break - else - current=current.next - end - end - return start,false -end -chainmores.gsub_single=chainprocs.gsub_single -function chainprocs.gsub_multiple(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) - delete_till_stop(start,stop) - local startchar=start.char - local subtables=currentlookup.subtables - local lookupname=subtables[1] - local replacements=cache.gsub_multiple[lookupname] - if not replacements then - if trace_bugs then - logwarning("%s: no multiple hits",cref(kind,chainname,chainlookupname,lookupname)) - end - else - replacements=replacements[startchar] - if not replacements then - if trace_bugs then - logwarning("%s: no multiple for %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar)) - end - else - if trace_multiples then - logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements)) - end - local sn=start.next - for k=1,#replacements do - if k==1 then - start.char=replacements[k] - else - local n=copy_node(start) - n.char=replacements[k] - n.next,n.prev=sn,start - if sn then - sn.prev=n - end - start.next,start=n,n - end - end - return start,true - end - end - return start,false -end -function chainprocs.gsub_alternate(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) - delete_till_stop(start,stop) - local current=start - local subtables=currentlookup.subtables - while current do - if current.id==glyph then - local currentchar=current.char - local lookupname=subtables[1] - local alternatives=cache.gsub_alternate[lookupname] - if not alternatives then - if trace_bugs then - logwarning("%s: no alternative hits",cref(kind,chainname,chainlookupname,lookupname)) - end - else - alternatives=alternatives[currentchar] - if not alternatives then - if trace_bugs then - logwarning("%s: no alternative for %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar)) - end - else - local choice,index=alternative_glyph(current,alternatives,kind,chainname,chainlookupname,lookupname) - current.char=choice - if trace_alternatives then - logprocess("%s: replacing single %s by alternative %s (%s)",cref(kind,chainname,chainlookupname,lookupname),index,gref(currentchar),gref(choice),index) - end - end - end - return start,true - elseif current==stop then - break - else - current=current.next - end - end - return start,false -end -function chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex) - local startchar=start.char - local subtables=currentlookup.subtables - local lookupname=subtables[1] - local ligatures=cache.gsub_ligature[lookupname] - if not ligatures then - if trace_bugs then - logwarning("%s: no ligature hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) - end - else - ligatures=ligatures[startchar] - if not ligatures then - if trace_bugs then - logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) - end - else - local s,discfound,last,nofreplacements=start.next,false,stop,0 - while s do - local id=s.id - if id==disc then - s=s.next - discfound=true - else - local schar=s.char - if marks[schar] then - s=s.next - else - local lg=ligatures[1][schar] - if not lg then - break - else - ligatures,last,nofreplacements=lg,s,nofreplacements+1 - if s==stop then - break - else - s=s.next - end - end - end - end - end - local l2=ligatures[2] - if l2 then - if chainindex then - stop=last - end - if trace_ligatures then - if start==stop then - logprocess("%s: replacing character %s by ligature %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2)) - else - logprocess("%s: replacing character %s upto %s by ligature %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2)) - end - end - start=toligature(kind,lookupname,start,stop,l2,currentlookup.flags[1],discfound) - return start,true,nofreplacements - elseif trace_bugs then - if start==stop then - logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) - else - logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char)) - end - end - end - end - return start,false,0 -end -chainmores.gsub_ligature=chainprocs.gsub_ligature -function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) - local markchar=start.char - if marks[markchar] then - local subtables=currentlookup.subtables - local lookupname=subtables[1] - local markanchors=cache.gpos_mark2base[lookupname] - if markanchors then - markanchors=markanchors[markchar] - end - if markanchors then - local base=start.prev - if base and base.id==glyph and base.subtype<256 and base.font==currentfont then - local basechar=base.char - if marks[basechar] then - while true do - base=base.prev - if base and base.id==glyph and base.subtype<256 and base.font==currentfont then - basechar=base.char - if not marks[basechar] then - break - end - else - if trace_bugs then - logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) - end - return start,false - end - end - end - local baseanchors=descriptions[basechar].anchors - if baseanchors then - local baseanchors=baseanchors['basechar'] - if baseanchors then - local al=anchorlookups[lookupname] - for anchor,ba in next,baseanchors do - if al[anchor] then - local ma=markanchors[anchor] - if ma then - local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma) - if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)", - cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) - end - return start,true - end - end - end - if trace_bugs then - logwarning("%s, no matching anchors for mark %s and base %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) - end - end - end - elseif trace_bugs then - logwarning("%s: prev node is no char",cref(kind,chainname,chainlookupname,lookupname)) - end - elseif trace_bugs then - logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) - end - elseif trace_bugs then - logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) - end - return start,false -end -function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) - local markchar=start.char - if marks[markchar] then - local subtables=currentlookup.subtables - local lookupname=subtables[1] - local markanchors=cache.gpos_mark2ligature[lookupname] - if markanchors then - markanchors=markanchors[markchar] - end - if markanchors then - local base=start.prev - local index=1 - if base and base.id==glyph and base.subtype<256 and base.font==currentfont then - local basechar=base.char - if marks[basechar] then - index=index+1 - while true do - base=base.prev - if base and base.id==glyph and base.subtype<256 and base.font==currentfont then - basechar=base.char - if marks[basechar] then - index=index+1 - else - break - end - else - if trace_bugs then - logwarning("%s: no base for mark %s",cref(kind,chainname,chainlookupname,lookupname),markchar) - end - return start,false - end - end - end - local i=has_attribute(start,markdone) - if i then index=i end - local baseanchors=descriptions[basechar].anchors - if baseanchors then - local baseanchors=baseanchors['baselig'] - if baseanchors then - local al=anchorlookups[lookupname] - for anchor,ba in next,baseanchors do - if al[anchor] then - local ma=markanchors[anchor] - if ma then - ba=ba[index] - if ba then - local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma,index) - if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)", - cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) - end - return start,true - end - end - end - end - if trace_bugs then - logwarning("%s: no matching anchors for mark %s and baselig %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) - end - end - end - elseif trace_bugs then - logwarning("feature %s, lookup %s: prev node is no char",kind,lookupname) - end - elseif trace_bugs then - logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) - end - elseif trace_bugs then - logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) - end - return start,false -end -function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) - local markchar=start.char - if marks[markchar] then - local subtables=currentlookup.subtables - local lookupname=subtables[1] - local markanchors=cache.gpos_mark2mark[lookupname] - if markanchors then - markanchors=markanchors[markchar] - end - if markanchors then - local base=start.prev - if base and base.id==glyph and base.subtype<256 and base.font==currentfont 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=set_mark(start,base,tfmdata.factor,rlmode,ba,ma) - if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", - cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) - end - return start,true - end - end - end - if trace_bugs then - logwarning("%s: no matching anchors for mark %s and basemark %s",gref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) - end - end - end - elseif trace_bugs then - logwarning("%s: prev node is no mark",cref(kind,chainname,chainlookupname,lookupname)) - end - elseif trace_bugs then - logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) - end - elseif trace_bugs then - logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) - end - return start,false -end -function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) - local alreadydone=cursonce and has_attribute(start,cursbase) - if not alreadydone then - local startchar=start.char - local subtables=currentlookup.subtables - local lookupname=subtables[1] - local exitanchors=cache.gpos_cursive[lookupname] - if exitanchors then - exitanchors=exitanchors[startchar] - end - if exitanchors then - local done=false - if marks[startchar] then - if trace_cursive then - logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) - end - else - local nxt=start.next - while not done and nxt and nxt.id==glyph and nxt.subtype<256 and nxt.font==currentfont do - local nextchar=nxt.char - if marks[nextchar] then - nxt=nxt.next - else - local entryanchors=descriptions[nextchar] - if entryanchors then - entryanchors=entryanchors.anchors - if entryanchors then - entryanchors=entryanchors['centry'] - if entryanchors then - local al=anchorlookups[lookupname] - for anchor,entry in next,entryanchors do - if al[anchor] then - local exit=exitanchors[anchor] - if exit then - local dx,dy,bound=set_cursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) - if trace_cursive then - logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) - end - done=true - break - end - end - end - end - end - else - fonts.register_message(currentfont,startchar,"no entry anchors") - end - break - end - end - end - return start,done - else - if trace_cursive and trace_details then - logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) - end - return start,false - end - end - return start,false -end -function chainprocs.gpos_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex,sequence) - local startchar=start.char - local subtables=currentlookup.subtables - local lookupname=subtables[1] - local kerns=cache.gpos_single[lookupname] - if kerns then - kerns=kerns[startchar] - if kerns then - local dx,dy,w,h=set_pair(start,tfmdata.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) - if trace_kerns then - logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) - end - end - end - return start,false -end -function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,cache,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=cache.gpos_pair[lookupname] - if kerns then - kerns=kerns[startchar] - if kerns then - local prev,done=start,false - local factor=tfmdata.factor - while snext and snext.id==glyph and snext.subtype<256 and snext.font==currentfont do - local nextchar=snext.char - local krn=kerns[nextchar] - if not krn and marks[nextchar] then - prev=snext - snext=snext.next - else - if not krn then - elseif type(krn)=="table" then - if krn[1]=="pair" then - local a,b=krn[3],krn[4] - if a and #a>0 then - local startchar=start.char - local x,y,w,h=set_pair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) - if trace_kerns then - logprocess("%s: shifting first of pair %s and %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) - end - end - if b and #b>0 then - local startchar=start.char - local x,y,w,h=set_pair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) - if trace_kerns then - logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) - end - end - else - logs.report("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) - local a,b=krn[3],krn[7] - if a and a~=0 then - local k=set_kern(snext,factor,rlmode,a) - if trace_kerns then - logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) - end - end - if b and b~=0 then - logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor) - end - end - done=true - elseif krn~=0 then - local k=set_kern(snext,factor,rlmode,krn) - if trace_kerns then - logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) - end - done=true - end - break - end - end - return start,done - end - end - end - return start,false -end -local function show_skip(kind,chainname,char,ck,class) - if ck[9] then - logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s (%s=>%s)",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) - else - logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s",cref(kind,chainname),gref(char),class,ck[1],ck[2]) - end -end -local function normal_handle_contextchain(start,kind,chainname,contexts,sequence,cache) - local flags,done=sequence.flags,false - local skipmark,skipligature,skipbase=flags[1],flags[2],flags[3] - local someskip=skipmark or skipligature or skipbase - local markclass=sequence.markclass - local skipped=false - for k=1,#contexts do - local match,current,last=true,start,start - local ck=contexts[k] - local seq=ck[3] - local s=#seq - if s==1 then - match=current.id==glyph and current.subtype<256 and current.font==currentfont and seq[1][current.char] - else - local f,l=ck[4],ck[5] - if f==l then - match=true - else - local n=f+1 - last=last.next - while n<=l do - if last then - local id=last.id - if id==glyph then - if last.subtype<256 and last.font==currentfont then - local char=last.char - local ccd=descriptions[char] - if ccd then - local class=ccd.class - if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then - skipped=true - if trace_skips then - show_skip(kind,chainname,char,ck,class) - end - last=last.next - elseif seq[n][char] then - if n<l then - last=last.next - end - n=n+1 - else - match=false break - end - else - match=false break - end - else - match=false break - end - elseif id==disc then - last=last.next - else - match=false break - end - else - match=false break - 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 then - if prev.subtype<256 and prev.font==currentfont 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 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.next - if current then - local n=l+1 - while n<=s do - if current then - local id=current.id - if id==glyph then - if current.subtype<256 and current.font==currentfont then - local char=current.char - local ccd=descriptions[char] - if ccd then - local class=ccd.class - if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then - skipped=true - if trace_skips then - show_skip(kind,chainname,char,ck,class) - end - elseif seq[n][char] then - n=n+1 - else - match=false break - end - else - match=false break - end - else - match=false break - end - elseif id==disc then - 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 %s (%s=>%s)",cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10]) - else - logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %s",cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype) - end - end - local chainlookups=ck[6] - if chainlookups then - local nofchainlookups=#chainlookups - if nofchainlookups==1 then - local chainlookupname=chainlookups[1] - local chainlookup=lookuptable[chainlookupname] - local cp=chainprocs[chainlookup.type] - if cp then - start,done=cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,nil,sequence) - else - logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) - end - else - local i=1 - repeat -if skipped then - while true do - local char=start.char - local ccd=descriptions[char] - if ccd then - local class=ccd.class - if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then - start=start.next - else - break - end - else - break - end - end -end - local chainlookupname=chainlookups[i] - local chainlookup=lookuptable[chainlookupname] - local cp=chainmores[chainlookup.type] - if cp then - local ok,n - start,ok,n=cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,i,sequence) - if ok then - done=true - i=i+(n or 1) - else - i=i+1 - end - else - logprocess("%s: multiple subchains for %s are not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) - i=i+1 - end - start=start.next - until i>nofchainlookups - end - else - local replacements=ck[7] - if replacements then - start,done=chainprocs.reversesub(start,last,kind,chainname,ck,cache,replacements) - else - done=true - if trace_contexts then - logprocess("%s: skipping match",cref(kind,chainname)) - end - end - end - end - end - return 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 '%s'",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 - logs.report("otf process",...) -end -local function logwarning(...) - logs.report("otf process",...) -end -local function report_missing_cache(typ,lookup) - local f=missing[currentfont] if not f then f={} missing[currentfont]=f end - local t=f[typ] if not t then t={} f[typ]=t end - if not t[lookup] then - t[lookup]=true - logwarning("missing cache for lookup %s of type %s in font %s (%s)",lookup,typ,currentfont,tfmdata.fullname) - end -end -local resolved={} -function fonts.methods.node.otf.features(head,font,attr) - if trace_steps then - checkstep(head) - end - tfmdata=fontdata[font] - local shared=tfmdata.shared - otfdata=shared.otfdata - local luatex=otfdata.luatex - descriptions=tfmdata.descriptions - characters=tfmdata.characters - indices=tfmdata.indices - unicodes=tfmdata.unicodes - marks=tfmdata.marks - anchorlookups=luatex.lookup_to_anchor - currentfont=font - rlmode=0 - local featuredata=otfdata.shared.featuredata - local sequences=luatex.sequences - lookuptable=luatex.lookups - local done=false - local script,language,s_enabled,a_enabled,dyn - local attribute_driven=attr and attr~=0 - if attribute_driven then - local features=context_setups[context_numbers[attr]] - dyn=context_merged[attr] or 0 - language,script=features.language or "dflt",features.script or "dflt" - a_enabled=features - if dyn==2 or dyn==-2 then - s_enabled=shared.features - end - else - language,script=tfmdata.language or "dflt",tfmdata.script or "dflt" - s_enabled=shared.features - dyn=0 - end - local res=resolved[font] if not res then res={} resolved[font]=res end - local rs=res [script] if not rs then rs={} res [script]=rs end - local rl=rs [language] if not rl then rl={} rs [language]=rl end - local ra=rl [attr] if ra==nil then ra={} rl [attr]=ra end - for s=1,#sequences do - local pardir,txtdir,success=0,{},false - local sequence=sequences[s] - local r=ra[s] - if r==nil then - local chain=sequence.chain or 0 - local features=sequence.features - if not features then - r=false - else - local valid,attribute,kind,what=false,false - for k,v in next,features do - local s_e=s_enabled and s_enabled[k] - local a_e=a_enabled and a_enabled[k] - if s_e or a_e then - local l=v[script] or v[wildcard] - if l then - if l[language] then - valid,what=s_e or a_e,language - elseif l[wildcard] then - valid,what=s_e or a_e,wildcard - end - if valid then - kind,attribute=k,special_attributes[k] or false - if a_e and dyn<0 then - valid=false - end - if trace_applied then - local typ,action=match(sequence.type,"(.*)_(.*)") - logs.report("otf node mode", - "%s font: %03i, dynamic: %03i, kind: %s, lookup: %3i, script: %-4s, language: %-4s (%-4s), type: %s, action: %s, name: %s", - (valid and "+") or "-",font,attr or 0,kind,s,script,language,what,typ,action,sequence.name) - end - break - end - end - end - end - if valid then - r={ valid,attribute,chain,kind } - else - r=false - end - end - ra[s]=r - end - featurevalue=r and r[1] - if featurevalue then - local attribute,chain,typ,subtables=r[2],r[3],sequence.type,sequence.subtables - if chain<0 then - local handler=handlers[typ] - local thecache=featuredata[typ] or {} - local start=find_node_tail(head) - while start do - local id=start.id - if id==glyph then - if start.subtype<256 and start.font==font then - local a=has_attribute(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=thecache[lookupname] - if lookupcache then - local lookupmatch=lookupcache[start.char] - if lookupmatch then - start,success=handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,i) - if success then - break - end - end - else - report_missing_cache(typ,lookupname) - end - end - if start then start=start.prev end - else - start=start.prev - end - else - start=start.prev - end - else - start=start.prev - end - end - else - local handler=handlers[typ] - local ns=#subtables - local thecache=featuredata[typ] or {} - local start=head - rlmode=0 - if ns==1 then - local lookupname=subtables[1] - local lookupcache=thecache[lookupname] - if not lookupcache then - report_missing_cache(typ,lookupname) - else - while start do - local id=start.id - if id==glyph then - if start.subtype<256 and start.font==font then - local a=has_attribute(start,0) - if a then - a=(a==attr) and (not attribute or has_attribute(start,state,attribute)) - else - a=not attribute or has_attribute(start,state,attribute) - end - if a then - local lookupmatch=lookupcache[start.char] - if lookupmatch then - local ok - start,ok=handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,1) - if ok then - success=true - end - end - if start then start=start.next end - else - start=start.next - end - else - start=start.next - end - elseif id==whatsit then - local subtype=start.subtype - if subtype==7 then - local dir=start.dir - if dir=="+TRT" or dir=="+TLT" then - insert(txtdir,dir) - elseif dir=="-TRT" or dir=="-TLT" then - remove(txtdir) - end - local d=txtdir[#txtdir] - if d=="+TRT" then - rlmode=-1 - elseif d=="+TLT" then - rlmode=1 - else - rlmode=pardir - end - if trace_directions then - logs.report("fonts","directions after textdir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) - end - elseif subtype==6 then - local dir=start.dir - if dir=="TRT" then - pardir=-1 - elseif dir=="TLT" then - pardir=1 - else - pardir=0 - end - rlmode=pardir - if trace_directions then - logs.report("fonts","directions after pardir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) - end - end - start=start.next - else - start=start.next - end - end - end - else - while start do - local id=start.id - if id==glyph then - if start.subtype<256 and start.font==font then - local a=has_attribute(start,0) - if a then - a=(a==attr) and (not attribute or has_attribute(start,state,attribute)) - else - a=not attribute or has_attribute(start,state,attribute) - end - if a then - for i=1,ns do - local lookupname=subtables[i] - local lookupcache=thecache[lookupname] - if lookupcache then - local lookupmatch=lookupcache[start.char] - if lookupmatch then - local ok - start,ok=handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,i) - if ok then - success=true - break - end - end - else - report_missing_cache(typ,lookupname) - end - end - if start then start=start.next end - else - start=start.next - end - else - start=start.next - end - elseif id==whatsit then - local subtype=start.subtype - if subtype==7 then - local dir=start.dir - if dir=="+TRT" or dir=="+TLT" then - insert(txtdir,dir) - elseif dir=="-TRT" or dir=="-TLT" then - remove(txtdir) - end - local d=txtdir[#txtdir] - if d=="+TRT" then - rlmode=-1 - elseif d=="+TLT" then - rlmode=1 - else - rlmode=pardir - end - if trace_directions then - logs.report("fonts","directions after textdir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) - end - elseif subtype==6 then - local dir=start.dir - if dir=="TRT" then - pardir=-1 - elseif dir=="TLT" then - pardir=1 - else - pardir=0 - end - rlmode=pardir - if trace_directions then - logs.report("fonts","directions after pardir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) - end - end - start=start.next - else - start=start.next - end - end - end - end - if success then - done=true - end - if trace_steps then - registerstep(head) - end - end - end - return head,done -end -otf.features.prepare={} -local function split(replacement,original,cache,unicodes) - local o,t,n={},{},0 - for s in gmatch(original,"[^ ]+") do - local us=unicodes[s] - if type(us)=="number" then - o[#o+1]=us - else - o[#o+1]=us[1] - end - end - for s in gmatch(replacement,"[^ ]+") do - n=n+1 - local us=unicodes[s] - if type(us)=="number" then - t[o[n]]=us - else - t[o[n]]=us[1] - end - end - return t -end -local function uncover(covers,result,cache,unicodes) - for n=1,#covers do - local c=covers[n] - local cc=cache[c] - if not cc then - local t={} - for s in gmatch(c,"[^ ]+") do - local us=unicodes[s] - if type(us)=="number" then - t[us]=true - else - for i=1,#us do - t[us[i]]=true - end - end - end - cache[c]=t - result[#result+1]=t - else - result[#result+1]=cc - end - end -end -local function prepare_lookups(tfmdata) - local otfdata=tfmdata.shared.otfdata - local featuredata=otfdata.shared.featuredata - local anchor_to_lookup=otfdata.luatex.anchor_to_lookup - local lookup_to_anchor=otfdata.luatex.lookup_to_anchor - local multiple=featuredata.gsub_multiple - local alternate=featuredata.gsub_alternate - local single=featuredata.gsub_single - local ligature=featuredata.gsub_ligature - local pair=featuredata.gpos_pair - local position=featuredata.gpos_single - local kerns=featuredata.gpos_pair - local mark=featuredata.gpos_mark2mark - local cursive=featuredata.gpos_cursive - local unicodes=tfmdata.unicodes - local indices=tfmdata.indices - local descriptions=tfmdata.descriptions - local action={ - substitution=function(p,lookup,glyph,unicode) - local old,new=unicode,unicodes[p[2]] - if type(new)=="table" then - new=new[1] - end - local s=single[lookup] - if not s then s={} single[lookup]=s end - s[old]=new - end, - multiple=function (p,lookup,glyph,unicode) - local old,new=unicode,{} - local m=multiple[lookup] - if not m then m={} multiple[lookup]=m end - m[old]=new - for pc in gmatch(p[2],"[^ ]+") do - local upc=unicodes[pc] - if type(upc)=="number" then - new[#new+1]=upc - else - new[#new+1]=upc[1] - end - end - end, - alternate=function(p,lookup,glyph,unicode) - local old,new=unicode,{} - local a=alternate[lookup] - if not a then a={} alternate[lookup]=a end - a[old]=new - for pc in gmatch(p[2],"[^ ]+") do - local upc=unicodes[pc] - if type(upc)=="number" then - new[#new+1]=upc - else - new[#new+1]=upc[1] - end - end - end, - ligature=function (p,lookup,glyph,unicode) - local first=true - local t=ligature[lookup] - if not t then t={} ligature[lookup]=t end - for s in gmatch(p[2],"[^ ]+") do - if first then - local u=unicodes[s] - if not u then - logs.report("define otf","lookup %s: ligature %s => %s ignored due to invalid unicode",lookup,p[2],glyph.name) - break - elseif type(u)=="number" then - if not t[u] then - t[u]={ {} } - end - t=t[u] - else - local tt=t - local tu - for i=1,#u do - local u=u[i] - if i==1 then - if not t[u] then - t[u]={ {} } - end - tu=t[u] - t=tu - else - if not t[u] then - tt[u]=tu - end - end - end - end - first=false - else - s=unicodes[s] - local t1=t[1] - if not t1[s] then - t1[s]={ {} } - end - t=t1[s] - end - end - t[2]=unicode - end, - position=function(p,lookup,glyph,unicode) - local s=position[lookup] - if not s then s={} position[lookup]=s end - s[unicode]=p[2] - end, - pair=function(p,lookup,glyph,unicode) - local s=pair[lookup] - if not s then s={} pair[lookup]=s end - local others=s[unicode] - if not others then others={} s[unicode]=others end - local two=p[2] - local upc=unicodes[two] - if not upc then - for pc in gmatch(two,"[^ ]+") do - local upc=unicodes[pc] - if type(upc)=="number" then - others[upc]=p - else - for i=1,#upc do - others[upc[i]]=p - end - end - end - elseif type(upc)=="number" then - others[upc]=p - else - for i=1,#upc do - others[upc[i]]=p - end - end - end, - } - for unicode,glyph in next,descriptions do - local lookups=glyph.slookups - if lookups then - for lookup,p in next,lookups do - action[p[1]](p,lookup,glyph,unicode) - end - end - local lookups=glyph.mlookups - if lookups then - for lookup,whatever in next,lookups do - for i=1,#whatever do - local p=whatever[i] - action[p[1]](p,lookup,glyph,unicode) - end - end - end - local list=glyph.mykerns - if list then - for lookup,krn in next,list do - local k=kerns[lookup] - if not k then k={} kerns[lookup]=k end - k[unicode]=krn - end - end - local oanchor=glyph.anchors - if oanchor then - for typ,anchors in next,oanchor do - if typ=="mark" then - for name,anchor in next,anchors do - local lookups=anchor_to_lookup[name] - if lookups then - for lookup,_ in next,lookups do - local f=mark[lookup] - if not f then f={} mark[lookup]=f end - f[unicode]=anchors - end - end - end - elseif 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 f=cursive[lookup] - if not f then f={} cursive[lookup]=f end - f[unicode]=anchors - end - end - end - end - end - end - end -end -luatex=luatex or {} -local function prepare_contextchains(tfmdata) - local otfdata=tfmdata.shared.otfdata - local lookups=otfdata.lookups - if lookups then - local featuredata=otfdata.shared.featuredata - local contextchain=featuredata.gsub_contextchain - local reversecontextchain=featuredata.gsub_reversecontextchain - local characters=tfmdata.characters - local unicodes=tfmdata.unicodes - local indices=tfmdata.indices - local cache=luatex.covers - if not cache then - cache={} - luatex.covers=cache - end - for lookupname,lookupdata in next,otfdata.lookups do - local lookuptype=lookupdata.type - if not lookuptype then - logs.report("otf process","missing lookuptype for %s",lookupname) - else - local rules=lookupdata.rules - if rules then - local fmt=lookupdata.format - if fmt=="coverage" then - if lookuptype~="chainsub" and lookuptype~="chainpos" then - logs.report("otf process","unsupported coverage %s for %s",lookuptype,lookupname) - else - local contexts=contextchain[lookupname] - if not contexts then - contexts={} - contextchain[lookupname]=contexts - end - local t={} - for nofrules=1,#rules do - local rule=rules[nofrules] - local coverage=rule.coverage - if coverage and coverage.current then - local current,before,after,sequence=coverage.current,coverage.before,coverage.after,{} - if before then - uncover(before,sequence,cache,unicodes) - end - local start=#sequence+1 - uncover(current,sequence,cache,unicodes) - local stop=#sequence - if after then - uncover(after,sequence,cache,unicodes) - end - if sequence[1] then - t[#t+1]={ nofrules,lookuptype,sequence,start,stop,rule.lookups } - for unic,_ in next,sequence[start] do - local cu=contexts[unic] - if not cu then - contexts[unic]=t - end - end - end - end - end - end - elseif fmt=="reversecoverage" then - if lookuptype~="reversesub" then - logs.report("otf process","unsupported reverse coverage %s for %s",lookuptype,lookupname) - else - local contexts=reversecontextchain[lookupname] - if not contexts then - contexts={} - reversecontextchain[lookupname]=contexts - end - local t={} - for nofrules=1,#rules do - local rule=rules[nofrules] - local reversecoverage=rule.reversecoverage - if reversecoverage and reversecoverage.current then - local current,before,after,replacements,sequence=reversecoverage.current,reversecoverage.before,reversecoverage.after,reversecoverage.replacements,{} - if before then - uncover(before,sequence,cache,unicodes) - end - local start=#sequence+1 - uncover(current,sequence,cache,unicodes) - local stop=#sequence - if after then - uncover(after,sequence,cache,unicodes) - end - if replacements then - replacements=split(replacements,current[1],cache,unicodes) - end - if sequence[1] then - t[#t+1]={ nofrules,lookuptype,sequence,start,stop,rule.lookups,replacements } - for unic,_ in next,sequence[start] do - local cu=contexts[unic] - if not cu then - contexts[unic]=t - end - end - end - end - end - end - elseif fmt=="glyphs" then - if lookuptype~="chainsub" and lookuptype~="chainpos" then - logs.report("otf process","unsupported coverage %s for %s",lookuptype,lookupname) - else - local contexts=contextchain[lookupname] - if not contexts then - contexts={} - contextchain[lookupname]=contexts - end - local t={} - for nofrules=1,#rules do - local rule=rules[nofrules] - local glyphs=rule.glyphs - if glyphs and glyphs.names then - local fore,back,names,sequence=glyphs.fore,glyphs.back,glyphs.names,{} - if fore and fore~="" then - fore=lpegmatch(split_at_space,fore) - uncover(fore,sequence,cache,unicodes) - end - local start=#sequence+1 - names=lpegmatch(split_at_space,names) - uncover(names,sequence,cache,unicodes) - local stop=#sequence - if back and back~="" then - back=lpegmatch(split_at_space,back) - uncover(back,sequence,cache,unicodes) - end - if sequence[1] then - t[#t+1]={ nofrules,lookuptype,sequence,start,stop,rule.lookups } - for unic,_ in next,sequence[start] do - local cu=contexts[unic] - if not cu then - contexts[unic]=t - end - end - end - end - end - end - end - end - end - end - end -end -function fonts.initializers.node.otf.features(tfmdata,value) - if true then - if not tfmdata.shared.otfdata.shared.initialized then - local t=trace_preparing and os.clock() - local otfdata=tfmdata.shared.otfdata - local featuredata=otfdata.shared.featuredata - featuredata.gsub_multiple={} - featuredata.gsub_alternate={} - featuredata.gsub_single={} - featuredata.gsub_ligature={} - featuredata.gsub_contextchain={} - featuredata.gsub_reversecontextchain={} - featuredata.gpos_pair={} - featuredata.gpos_single={} - featuredata.gpos_mark2base={} - featuredata.gpos_mark2ligature=featuredata.gpos_mark2base - featuredata.gpos_mark2mark=featuredata.gpos_mark2base - featuredata.gpos_cursive={} - featuredata.gpos_contextchain=featuredata.gsub_contextchain - featuredata.gpos_reversecontextchain=featuredata.gsub_reversecontextchain - prepare_contextchains(tfmdata) - prepare_lookups(tfmdata) - otfdata.shared.initialized=true - if trace_preparing then - logs.report("otf process","preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?") - end - end - end -end - -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,tostring,match,format,concat=type,tostring,string.match,string.format,table.concat -if not trackers then trackers={ register=function() end } end -local trace_analyzing=false trackers.register("otf.analyzing",function(v) trace_analyzing=v end) -local trace_cjk=false trackers.register("cjk.injections",function(v) trace_cjk=v end) -trackers.register("cjk.analyzing","otf.analyzing") -fonts=fonts or {} -fonts.analyzers=fonts.analyzers or {} -fonts.analyzers.initializers=fonts.analyzers.initializers or { node={ otf={} } } -fonts.analyzers.methods=fonts.analyzers.methods or { node={ otf={} } } -local otf=fonts.otf -local tfm=fonts.tfm -local initializers=fonts.analyzers.initializers -local methods=fonts.analyzers.methods -local glyph=node.id('glyph') -local glue=node.id('glue') -local penalty=node.id('penalty') -local set_attribute=node.set_attribute -local has_attribute=node.has_attribute -local traverse_id=node.traverse_id -local traverse_node_list=node.traverse -local fontdata=fonts.ids -local state=attributes.private('state') -local fcs=(fonts.color and fonts.color.set) or function() end -local fcr=(fonts.color and fonts.color.reset) or function() end -local a_to_script=otf.a_to_script -local a_to_language=otf.a_to_language -function fonts.initializers.node.otf.analyze(tfmdata,value,attr) - local script,language - if attr and attr>0 then - script,language=a_to_script[attr],a_to_language[attr] - else - script,language=tfmdata.script,tfmdata.language - end - local action=initializers[script] - if action then - if type(action)=="function" then - return action(tfmdata,value) - else - local action=action[language] - if action then - return action(tfmdata,value) - end - end - end - return nil -end -function fonts.methods.node.otf.analyze(head,font,attr) - local tfmdata=fontdata[font] - local script,language - if attr and attr>0 then - script,language=a_to_script[attr],a_to_language[attr] - else - script,language=tfmdata.script,tfmdata.language - end - local action=methods[script] - if action then - if type(action)=="function" then - return action(head,font,attr) - else - action=action[language] - if action then - return action(head,font,attr) - end - end - end - return head,false -end -otf.features.register("analyze",true) -table.insert(fonts.triggers,"analyze") -fonts.analyzers.methods.latn=fonts.analyzers.aux.setstate -local zwnj=0x200C -local zwj=0x200D -local isol={ - [0x0600]=true,[0x0601]=true,[0x0602]=true,[0x0603]=true, - [0x0608]=true,[0x060B]=true,[0x0621]=true,[0x0674]=true, - [0x06DD]=true,[zwnj]=true, -} -local isol_fina={ - [0x0622]=true,[0x0623]=true,[0x0624]=true,[0x0625]=true, - [0x0627]=true,[0x0629]=true,[0x062F]=true,[0x0630]=true, - [0x0631]=true,[0x0632]=true,[0x0648]=true,[0x0671]=true, - [0x0672]=true,[0x0673]=true,[0x0675]=true,[0x0676]=true, - [0x0677]=true,[0x0688]=true,[0x0689]=true,[0x068A]=true, - [0x068B]=true,[0x068C]=true,[0x068D]=true,[0x068E]=true, - [0x068F]=true,[0x0690]=true,[0x0691]=true,[0x0692]=true, - [0x0693]=true,[0x0694]=true,[0x0695]=true,[0x0696]=true, - [0x0697]=true,[0x0698]=true,[0x0699]=true,[0x06C0]=true, - [0x06C3]=true,[0x06C4]=true,[0x06C5]=true,[0x06C6]=true, - [0x06C7]=true,[0x06C8]=true,[0x06C9]=true,[0x06CA]=true, - [0x06CB]=true,[0x06CD]=true,[0x06CF]=true,[0x06D2]=true, - [0x06D3]=true,[0x06D5]=true,[0x06EE]=true,[0x06EF]=true, - [0x0759]=true,[0x075A]=true,[0x075B]=true,[0x076B]=true, - [0x076C]=true,[0x0771]=true,[0x0773]=true,[0x0774]=true, - [0x0778]=true,[0x0779]=true,[0xFEF5]=true,[0xFEF7]=true, - [0xFEF9]=true,[0xFEFB]=true, -} -local isol_fina_medi_init={ - [0x0626]=true,[0x0628]=true,[0x062A]=true,[0x062B]=true, - [0x062C]=true,[0x062D]=true,[0x062E]=true,[0x0633]=true, - [0x0634]=true,[0x0635]=true,[0x0636]=true,[0x0637]=true, - [0x0638]=true,[0x0639]=true,[0x063A]=true,[0x063B]=true, - [0x063C]=true,[0x063D]=true,[0x063E]=true,[0x063F]=true, - [0x0640]=true,[0x0641]=true,[0x0642]=true,[0x0643]=true, - [0x0644]=true,[0x0645]=true,[0x0646]=true,[0x0647]=true, - [0x0649]=true,[0x064A]=true,[0x066E]=true,[0x066F]=true, - [0x0678]=true,[0x0679]=true,[0x067A]=true,[0x067B]=true, - [0x067C]=true,[0x067D]=true,[0x067E]=true,[0x067F]=true, - [0x0680]=true,[0x0681]=true,[0x0682]=true,[0x0683]=true, - [0x0684]=true,[0x0685]=true,[0x0686]=true,[0x0687]=true, - [0x069A]=true,[0x069B]=true,[0x069C]=true,[0x069D]=true, - [0x069E]=true,[0x069F]=true,[0x06A0]=true,[0x06A1]=true, - [0x06A2]=true,[0x06A3]=true,[0x06A4]=true,[0x06A5]=true, - [0x06A6]=true,[0x06A7]=true,[0x06A8]=true,[0x06A9]=true, - [0x06AA]=true,[0x06AB]=true,[0x06AC]=true,[0x06AD]=true, - [0x06AE]=true,[0x06AF]=true,[0x06B0]=true,[0x06B1]=true, - [0x06B2]=true,[0x06B3]=true,[0x06B4]=true,[0x06B5]=true, - [0x06B6]=true,[0x06B7]=true,[0x06B8]=true,[0x06B9]=true, - [0x06BA]=true,[0x06BB]=true,[0x06BC]=true,[0x06BD]=true, - [0x06BE]=true,[0x06BF]=true,[0x06C1]=true,[0x06C2]=true, - [0x06CC]=true,[0x06CE]=true,[0x06D0]=true,[0x06D1]=true, - [0x06FA]=true,[0x06FB]=true,[0x06FC]=true,[0x06FF]=true, - [0x0750]=true,[0x0751]=true,[0x0752]=true,[0x0753]=true, - [0x0754]=true,[0x0755]=true,[0x0756]=true,[0x0757]=true, - [0x0758]=true,[0x075C]=true,[0x075D]=true,[0x075E]=true, - [0x075F]=true,[0x0760]=true,[0x0761]=true,[0x0762]=true, - [0x0763]=true,[0x0764]=true,[0x0765]=true,[0x0766]=true, - [0x0767]=true,[0x0768]=true,[0x0769]=true,[0x076A]=true, - [0x076D]=true,[0x076E]=true,[0x076F]=true,[0x0770]=true, - [0x0772]=true,[0x0775]=true,[0x0776]=true,[0x0777]=true, - [0x077A]=true,[0x077B]=true,[0x077C]=true,[0x077D]=true, - [0x077E]=true,[0x077F]=true,[zwj]=true, -} -local arab_warned={} -local function warning(current,what) - local char=current.char - if not arab_warned[char] then - log.report("analyze","arab: character %s (U+%04X) has no %s class",char,char,what) - arab_warned[char]=true - end -end -function fonts.analyzers.methods.nocolor(head,font,attr) - for n in traverse_node_list(head,glyph) do - if not font or n.font==font then - fcr(n) - end - end - return head,true -end -local function finish(first,last) - if last then - if first==last then - local fc=first.char - if isol_fina_medi_init[fc] or isol_fina[fc] then - set_attribute(first,state,4) - if trace_analyzing then fcs(first,"font:isol") end - else - warning(first,"isol") - set_attribute(first,state,0) - if trace_analyzing then fcr(first) end - end - else - local lc=last.char - if isol_fina_medi_init[lc] or isol_fina[lc] then - set_attribute(last,state,3) - if trace_analyzing then fcs(last,"font:fina") end - else - warning(last,"fina") - set_attribute(last,state,0) - if trace_analyzing then fcr(last) end - end - end - first,last=nil,nil - elseif first then - local fc=first.char - if isol_fina_medi_init[fc] or isol_fina[fc] then - set_attribute(first,state,4) - if trace_analyzing then fcs(first,"font:isol") end - else - warning(first,"isol") - set_attribute(first,state,0) - if trace_analyzing then fcr(first) end - end - first=nil - end - return first,last -end -function fonts.analyzers.methods.arab(head,font,attr) - local tfmdata=fontdata[font] - local marks=tfmdata.marks - local first,last,current,done=nil,nil,head,false - while current do - if current.id==glyph and current.subtype<256 and current.font==font and not has_attribute(current,state) then - done=true - local char=current.char - if marks[char] then - set_attribute(current,state,5) - if trace_analyzing then fcs(current,"font:mark") end - elseif isol[char] then - first,last=finish(first,last) - set_attribute(current,state,4) - if trace_analyzing then fcs(current,"font:isol") end - first,last=nil,nil - elseif not first then - if isol_fina_medi_init[char] then - set_attribute(current,state,1) - if trace_analyzing then fcs(current,"font:init") end - first,last=first or current,current - elseif isol_fina[char] then - set_attribute(current,state,4) - if trace_analyzing then fcs(current,"font:isol") end - first,last=nil,nil - else - first,last=finish(first,last) - end - elseif isol_fina_medi_init[char] then - first,last=first or current,current - set_attribute(current,state,2) - if trace_analyzing then fcs(current,"font:medi") end - elseif isol_fina[char] then - if not has_attribute(last,state,1) then - set_attribute(last,state,2) - if trace_analyzing then fcs(last,"font:medi") end - end - set_attribute(current,state,3) - if trace_analyzing then fcs(current,"font:fina") end - first,last=nil,nil - elseif char>=0x0600 and char<=0x06FF then - if trace_analyzing then fcs(current,"font:rest") end - first,last=finish(first,last) - else - first,last=finish(first,last) - end - else - first,last=finish(first,last) - end - current=current.next - end - first,last=finish(first,last) - return head,done -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-otc']={ - version=1.001, - comment="companion to font-otf.lua (context)", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -local format,insert=string.format,table.insert -local type,next=type,next -local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end) -local otf=fonts.otf -local tfm=fonts.tfm -local extra_lists={ - tlig={ - { - endash="hyphen hyphen", - emdash="hyphen hyphen hyphen", - quotedblleft="quoteleft quoteleft", - quotedblright="quoteright quoteright", - quotedblleft="grave grave", - quotedblright="quotesingle quotesingle", - quotedblbase="comma comma", - exclamdown="exclam grave", - questiondown="question grave", - guillemotleft="less less", - guillemotright="greater greater", - }, - }, - trep={ - { - [0x0022]=0x201D, - [0x0027]=0x2019, - [0x0060]=0x2018, - }, - }, - anum={ - { - [0x0030]=0x0660, - [0x0031]=0x0661, - [0x0032]=0x0662, - [0x0033]=0x0663, - [0x0034]=0x0664, - [0x0035]=0x0665, - [0x0036]=0x0666, - [0x0037]=0x0667, - [0x0038]=0x0668, - [0x0039]=0x0669, - }, - { - [0x0030]=0x06F0, - [0x0031]=0x06F1, - [0x0032]=0x06F2, - [0x0033]=0x06F3, - [0x0034]=0x06F4, - [0x0035]=0x06F5, - [0x0036]=0x06F6, - [0x0037]=0x06F7, - [0x0038]=0x06F8, - [0x0039]=0x06F9, - }, - }, -} -local extra_features={ - tlig={ - { - features={ { scripts={ { script="*",langs={ "*" },} },tag="tlig",comment="added bij mkiv" },}, - name="ctx_tlig_1", - subtables={ { name="ctx_tlig_1_s" } }, - type="gsub_ligature", - flags={}, - }, - }, - trep={ - { - features={ { scripts={ { script="*",langs={ "*" },} },tag="trep",comment="added bij mkiv" },}, - name="ctx_trep_1", - subtables={ { name="ctx_trep_1_s" } }, - type="gsub_single", - flags={}, - }, - }, - anum={ - { - features={ { scripts={ { script="arab",langs={ "dflt","ARA" },} },tag="anum",comment="added bij mkiv" },}, - name="ctx_anum_1", - subtables={ { name="ctx_anum_1_s" } }, - type="gsub_single", - flags={}, - }, - { - features={ { scripts={ { script="arab",langs={ "FAR" },} },tag="anum",comment="added bij mkiv" },}, - name="ctx_anum_2", - subtables={ { name="ctx_anum_2_s" } }, - type="gsub_single", - flags={}, - }, - }, -} -fonts.otf.enhancers["add some missing characters"]=function(data,filename) -end -fonts.otf.enhancers["enrich with features"]=function(data,filename) - local used={} - for i=1,#otf.glists do - local g=data[otf.glists[i]] - if g then - for i=1,#g do - local f=g[i].features - if f then - for i=1,#f do - local t=f[i].tag - if t then used[t]=true end - end - end - end - end - end - local glyphs=data.glyphs - local indices=data.map.map - data.gsub=data.gsub or {} - for kind,specifications in next,extra_features do - if not used[kind] then - local done=0 - for s=1,#specifications do - local added=false - local specification=specifications[s] - local list=extra_lists[kind][s] - local name=specification.name.."_s" - if specification.type=="gsub_ligature" then - for unicode,index in next,indices do - local glyph=glyphs[index] - local ligature=list[glyph.name] - if ligature then - local o=glyph.lookups or {} - o[name]={ - { - ["type"]="ligature", - ["specification"]={ - char=glyph.name, - components=ligature, - } - } - } - glyph.lookups,done,added=o,done+1,true - end - end - elseif specification.type=="gsub_single" then - for unicode,index in next,indices do - local glyph=glyphs[index] - local r=list[unicode] - if r then - local replacement=indices[r] - if replacement and glyphs[replacement] then - local o=glyph.lookups or {} - o[name]={ - { - ["type"]="substitution", - ["specification"]={ - variant=glyphs[replacement].name, - } - } - } - glyph.lookups,done,added=o,done+1,true - end - end - end - end - if added then - insert(data.gsub,s,table.fastcopy(specification)) - end - end - if done>0 then - if trace_loading then - logs.report("load otf","enhance: registering %s feature (%s glyphs affected)",kind,done) - end - end - end - end -end -otf.tables.features['tlig']='TeX Ligatures' -otf.tables.features['trep']='TeX Replacements' -otf.tables.features['anum']='Arabic Digits' -otf.features.register_base_substitution('tlig') -otf.features.register_base_substitution('trep') -otf.features.register_base_substitution('anum') -fonts.initializers.base.otf.equaldigits=fonts.initializers.common.equaldigits -fonts.initializers.node.otf.equaldigits=fonts.initializers.common.equaldigits -fonts.initializers.base.otf.lineheight=fonts.initializers.common.lineheight -fonts.initializers.node.otf.lineheight=fonts.initializers.common.lineheight -fonts.initializers.base.otf.compose=fonts.initializers.common.compose -fonts.initializers.node.otf.compose=fonts.initializers.common.compose - -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,concat,gmatch,match,find,lower=string.format,table.concat,string.gmatch,string.match,string.find,string.lower -local tostring,next=tostring,next -local lpegmatch=lpeg.match -local trace_defining=false trackers .register("fonts.defining",function(v) trace_defining=v end) -local directive_embedall=false directives.register("fonts.embedall",function(v) directive_embedall=v end) -trackers.register("fonts.loading","fonts.defining","otf.loading","afm.loading","tfm.loading") -trackers.register("fonts.all","fonts.*","otf.*","afm.*","tfm.*") -fonts=fonts or {} -fonts.define=fonts.define or {} -fonts.tfm=fonts.tfm or {} -fonts.ids=fonts.ids or {} -fonts.vf=fonts.vf or {} -fonts.used=fonts.used or {} -local tfm=fonts.tfm -local vf=fonts.vf -local define=fonts.define -tfm.version=1.01 -tfm.cache=containers.define("fonts","tfm",tfm.version,false) -define.method="afm or tfm" -define.specify=fonts.define.specify or {} -define.methods=fonts.define.methods or {} -tfm.fonts=tfm.fonts or {} -tfm.readers=tfm.readers or {} -tfm.internalized=tfm.internalized or {} -tfm.readers.sequence={ 'otf','ttf','afm','tfm' } -tfm.auto_afm=true -local readers=tfm.readers -local sequence=readers.sequence -fonts.version=1.05 -fonts.cache=containers.define("fonts","def",fonts.version,false) -local splitter,specifiers=nil,"" -local P,C,S,Cc=lpeg.P,lpeg.C,lpeg.S,lpeg.Cc -local left=P("(") -local right=P(")") -local colon=P(":") -local space=P(" ") -define.defaultlookup="file" -local prefixpattern=P(false) -function define.add_specifier(symbol) - specifiers=specifiers..symbol - local method=S(specifiers) - local lookup=C(prefixpattern)*colon - local sub=left*C(P(1-left-right-method)^1)*right - local specification=C(method)*C(P(1)^1) - local name=C((1-sub-specification)^1) - splitter=P((lookup+Cc(""))*name*(sub+Cc(""))*(specification+Cc(""))) -end -function define.add_lookup(str,default) - prefixpattern=prefixpattern+P(str) -end -define.add_lookup("file") -define.add_lookup("name") -define.add_lookup("spec") -function define.get_specification(str) - return lpegmatch(splitter,str) -end -function define.register_split(symbol,action) - define.add_specifier(symbol) - define.specify[symbol]=action -end -function define.makespecification(specification,lookup,name,sub,method,detail,size) - size=size or 655360 - if trace_defining then - logs.report("define font","%s -> lookup: %s, name: %s, sub: %s, method: %s, detail: %s", - specification,(lookup~="" and lookup) or "[file]",(name~="" and name) or "-", - (sub~="" and sub) or "-",(method~="" and method) or "-",(detail~="" and detail) or "-") - end - if not lookup or lookup=="" then - lookup=define.defaultlookup - end - local t={ - lookup=lookup, - specification=specification, - size=size, - name=name, - sub=sub, - method=method, - detail=detail, - resolved="", - forced="", - features={}, - } - return t -end -function define.analyze(specification,size) - local lookup,name,sub,method,detail=define.get_specification(specification or "") - return define.makespecification(specification,lookup,name,sub,method,detail,size) -end -local sortedhashkeys=table.sortedhashkeys -function tfm.hash_features(specification) - local features=specification.features - if features then - local t={} - local normal=features.normal - if normal and next(normal) then - local f=sortedhashkeys(normal) - for i=1,#f do - local v=f[i] - if v~="number" and v~="features" then - t[#t+1]=v..'='..tostring(normal[v]) - end - end - end - local vtf=features.vtf - if vtf and next(vtf) then - local f=sortedhashkeys(vtf) - for i=1,#f do - local v=f[i] - t[#t+1]=v..'='..tostring(vtf[v]) - end - end - if #t>0 then - return concat(t,"+") - end - end - return "unknown" -end -fonts.designsizes={} -function tfm.hash_instance(specification,force) - local hash,size,fallbacks=specification.hash,specification.size,specification.fallbacks - if force or not hash then - hash=tfm.hash_features(specification) - specification.hash=hash - end - if size<1000 and fonts.designsizes[hash] then - size=math.round(tfm.scaled(size,fonts.designsizes[hash])) - specification.size=size - end - if fallbacks then - return hash..' @ '..tostring(size)..' @ '..fallbacks - else - return hash..' @ '..tostring(size) - end -end -define.resolvers=resolvers -function define.resolvers.file(specification) - local suffix=file.suffix(specification.name) - if fonts.formats[suffix] then - specification.forced=suffix - specification.name=file.removesuffix(specification.name) - end -end -function define.resolvers.name(specification) - local resolve=fonts.names.resolve - if resolve then - local resolved,sub=fonts.names.resolve(specification) - specification.resolved,specification.sub=resolved,sub - if resolved then - local suffix=file.suffix(resolved) - if fonts.formats[suffix] then - specification.forced=suffix - specification.name=file.removesuffix(resolved) - else - specification.name=resolved - end - end - else - define.resolvers.file(specification) - end -end -function define.resolvers.spec(specification) - local resolvespec=fonts.names.resolvespec - if resolvespec then - specification.resolved,specification.sub=fonts.names.resolvespec(specification) - if specification.resolved then - specification.forced=file.extname(specification.resolved) - specification.name=file.removesuffix(specification.resolved) - end - else - define.resolvers.name(specification) - end -end -function define.resolve(specification) - if not specification.resolved or specification.resolved=="" then - local r=define.resolvers[specification.lookup] - if r then - r(specification) - end - end - if specification.forced=="" then - specification.forced=nil - else - specification.forced=specification.forced - end - specification.hash=lower(specification.name..' @ '..tfm.hash_features(specification)) - if specification.sub and specification.sub~="" then - specification.hash=specification.sub..' @ '..specification.hash - end - return specification -end -function tfm.read(specification) - local hash=tfm.hash_instance(specification) - local tfmtable=tfm.fonts[hash] - if not tfmtable then - local forced=specification.forced or "" - if forced~="" then - tfmtable=readers[lower(forced)](specification) - if not tfmtable then - logs.report("define font","forced type %s of %s not found",forced,specification.name) - end - else - for s=1,#sequence do - local reader=sequence[s] - if readers[reader] then - if trace_defining then - logs.report("define font","trying (reader sequence driven) type %s for %s with file %s",reader,specification.name,specification.filename or "unknown") - end - tfmtable=readers[reader](specification) - if tfmtable then - break - else - specification.filename=nil - end - end - end - end - if tfmtable then - if directive_embedall then - tfmtable.embedding="full" - elseif tfmtable.filename and fonts.dontembed[tfmtable.filename] then - tfmtable.embedding="no" - else - tfmtable.embedding="subset" - end - tfm.fonts[hash]=tfmtable - fonts.designsizes[specification.hash]=tfmtable.designsize - end - end - if not tfmtable then - logs.report("define font","font with name %s is not found",specification.name) - end - return tfmtable -end -function tfm.read_and_define(name,size) - local specification=define.analyze(name,size) - local method=specification.method - if method and define.specify[method] then - specification=define.specify[method](specification) - end - specification=define.resolve(specification) - local hash=tfm.hash_instance(specification) - local id=define.registered(hash) - if not id then - local fontdata=tfm.read(specification) - if fontdata then - fontdata.hash=hash - id=font.define(fontdata) - define.register(fontdata,id) - tfm.cleanup_table(fontdata) - else - id=0 - end - end - return fonts.ids[id],id -end -local function check_tfm(specification,fullname) - local foundname=resolvers.findbinfile(fullname,'tfm') or "" - if foundname=="" then - foundname=resolvers.findbinfile(fullname,'ofm') or "" - end - if foundname~="" then - specification.filename,specification.format=foundname,"ofm" - return tfm.read_from_tfm(specification) - end -end -local function check_afm(specification,fullname) - local foundname=resolvers.findbinfile(fullname,'afm') or "" - if foundname=="" and tfm.auto_afm then - local encoding,shortname=match(fullname,"^(.-)%-(.*)$") - if encoding and shortname and fonts.enc.known[encoding] then - shortname=resolvers.findbinfile(shortname,'afm') or "" - if shortname~="" then - foundname=shortname - if trace_loading then - logs.report("load afm","stripping encoding prefix from filename %s",afmname) - end - end - end - end - if foundname~="" then - specification.filename,specification.format=foundname,"afm" - return tfm.read_from_afm(specification) - end -end -function readers.tfm(specification) - local fullname,tfmtable=specification.filename or "",nil - if fullname=="" then - local forced=specification.forced or "" - if forced~="" then - tfmtable=check_tfm(specification,specification.name.."."..forced) - end - if not tfmtable then - tfmtable=check_tfm(specification,specification.name) - end - else - tfmtable=check_tfm(specification,fullname) - end - return tfmtable -end -function readers.afm(specification,method) - local fullname,tfmtable=specification.filename or "",nil - if fullname=="" then - local forced=specification.forced or "" - if forced~="" then - tfmtable=check_afm(specification,specification.name.."."..forced) - end - if not tfmtable then - method=method or define.method or "afm or tfm" - if method=="tfm" then - tfmtable=check_tfm(specification,specification.name) - elseif method=="afm" then - tfmtable=check_afm(specification,specification.name) - elseif method=="tfm or afm" then - tfmtable=check_tfm(specification,specification.name) or check_afm(specification,specification.name) - else - tfmtable=check_afm(specification,specification.name) or check_tfm(specification,specification.name) - end - end - else - tfmtable=check_afm(specification,fullname) - end - return tfmtable -end -local function check_otf(forced,specification,suffix,what) - local name=specification.name - if forced then - name=file.addsuffix(name,suffix,true) - end - local fullname,tfmtable=resolvers.findbinfile(name,suffix) or "",nil - if fullname=="" then - local fb=fonts.names.old_to_new[name] - if fb then - fullname=resolvers.findbinfile(fb,suffix) or "" - end - end - if fullname=="" then - local fb=fonts.names.new_to_old[name] - if fb then - fullname=resolvers.findbinfile(fb,suffix) or "" - end - end - if fullname~="" then - specification.filename,specification.format=fullname,what - tfmtable=tfm.read_from_open_type(specification) - end - return tfmtable -end -function readers.opentype(specification,suffix,what) - local forced=specification.forced or "" - if forced=="otf" then - return check_otf(true,specification,forced,"opentype") - elseif forced=="ttf" or forced=="ttc" or forced=="dfont" then - return check_otf(true,specification,forced,"truetype") - else - return check_otf(false,specification,suffix,what) - end -end -function readers.otf (specification) return readers.opentype(specification,"otf","opentype") end -function readers.ttf (specification) return readers.opentype(specification,"ttf","truetype") end -function readers.ttc (specification) return readers.opentype(specification,"ttf","truetype") end -function readers.dfont(specification) return readers.opentype(specification,"ttf","truetype") end -function define.check(features,defaults) - local done=false - if features and next(features) then - for k,v in next,defaults do - if features[k]==nil then - features[k],done=v,true - end - end - else - features,done=table.fastcopy(defaults),true - end - return features,done -end -define.last=nil -function define.register(fontdata,id) - if fontdata and id then - local hash=fontdata.hash - if not tfm.internalized[hash] then - if trace_defining then - logs.report("define font","loading at 2 id %s, hash: %s",id or "?",hash or "?") - end - fonts.identifiers[id]=fontdata - fonts.characters [id]=fontdata.characters - fonts.quads [id]=fontdata.parameters.quad - tfm.internalized[hash]=id - end - end -end -function define.registered(hash) - local id=tfm.internalized[hash] - return id,id and fonts.ids[id] -end -local cache_them=false -function tfm.make(specification) - local fvm=define.methods[specification.features.vtf.preset] - if fvm then - return fvm(specification) - else - return nil - end -end -function define.read(specification,size,id) - statistics.starttiming(fonts) - if type(specification)=="string" then - specification=define.analyze(specification,size) - end - local method=specification.method - if method and define.specify[method] then - specification=define.specify[method](specification) - end - specification=define.resolve(specification) - local hash=tfm.hash_instance(specification) - if cache_them then - local fontdata=containers.read(fonts.cache,hash) - end - local fontdata=define.registered(hash) - if not fontdata then - if specification.features.vtf and specification.features.vtf.preset then - fontdata=tfm.make(specification) - else - fontdata=tfm.read(specification) - if fontdata then - tfm.check_virtual_id(fontdata) - end - end - if cache_them then - fontdata=containers.write(fonts.cache,hash,fontdata) - end - if fontdata then - fontdata.hash=hash - fontdata.cache="no" - if id then - define.register(fontdata,id) - end - end - end - define.last=fontdata or id - if not fontdata then - logs.report("define font","unknown font %s, loading aborted",specification.name) - elseif trace_defining and type(fontdata)=="table" then - logs.report("define font","using %s font with id %s, name:%s size:%s bytes:%s encoding:%s fullname:%s filename:%s", - fontdata.type or "unknown", - id or "?", - fontdata.name or "?", - fontdata.size or "default", - fontdata.encodingbytes or "?", - fontdata.encodingname or "unicode", - fontdata.fullname or "?", - file.basename(fontdata.filename or "?")) - end - statistics.stoptiming(fonts) - return fontdata -end -function vf.find(name) - name=file.removesuffix(file.basename(name)) - if tfm.resolve_vf then - local format=fonts.logger.format(name) - if format=='tfm' or format=='ofm' then - if trace_defining then - logs.report("define font","locating vf for %s",name) - end - return resolvers.findbinfile(name,"ovf") - else - if trace_defining then - logs.report("define font","vf for %s is already taken care of",name) - end - return nil - end - else - if trace_defining then - logs.report("define font","locating vf for %s",name) - end - return resolvers.findbinfile(name,"ovf") - end -end -callbacks.register('define_font',define.read,"definition of fonts (tfmtable preparation)") -callbacks.register('find_vf_file',vf.find,"locating virtual fonts, insofar needed") - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-xtx']={ - version=1.001, - comment="companion to font-ini.mkiv", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -local texsprint,count=tex.sprint,tex.count -local format,concat,gmatch,match,find,lower=string.format,table.concat,string.gmatch,string.match,string.find,string.lower -local tostring,next=tostring,next -local lpegmatch=lpeg.match -local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end) -local list={} -fonts.define.specify.colonized_default_lookup="file" -local function isstyle(s) - local style=string.lower(s):split("/") - for _,v in ipairs(style) do - if v=="b" then - list.style="bold" - elseif v=="i" then - list.style="italic" - elseif v=="bi" or v=="ib" then - list.style="bolditalic" - elseif v:find("^s=") then - list.optsize=v:split("=")[2] - elseif v=="aat" or v=="icu" or v=="gr" then - logs.report("load font","unsupported font option: %s",v) - elseif not v:is_empty() then - list.style=v:gsub("[^%a%d]","") - end - end -end -fonts=fonts or {} -fonts.otf=fonts.otf or {} -local otf=fonts.otf -otf.tables=otf.tables or {} -otf.tables.defaults={ - dflt={ - "ccmp","locl","rlig","liga","clig", - "kern","mark","mkmk","itlc", - }, - arab={ - "ccmp","locl","isol","fina","fin2", - "fin3","medi","med2","init","rlig", - "calt","liga","cswh","mset","curs", - "kern","mark","mkmk", - }, - deva={ - "ccmp","locl","init","nukt","akhn", - "rphf","blwf","half","pstf","vatu", - "pres","blws","abvs","psts","haln", - "calt","blwm","abvm","dist","kern", - "mark","mkmk", - }, - khmr={ - "ccmp","locl","pref","blwf","abvf", - "pstf","pres","blws","abvs","psts", - "clig","calt","blwm","abvm","dist", - "kern","mark","mkmk", - }, - thai={ - "ccmp","locl","liga","kern","mark", - "mkmk", - }, - hang={ - "ccmp","ljmo","vjmo","tjmo", - }, -} -otf.tables.defaults.beng=otf.tables.defaults.deva -otf.tables.defaults.guru=otf.tables.defaults.deva -otf.tables.defaults.gujr=otf.tables.defaults.deva -otf.tables.defaults.orya=otf.tables.defaults.deva -otf.tables.defaults.taml=otf.tables.defaults.deva -otf.tables.defaults.telu=otf.tables.defaults.deva -otf.tables.defaults.knda=otf.tables.defaults.deva -otf.tables.defaults.mlym=otf.tables.defaults.deva -otf.tables.defaults.sinh=otf.tables.defaults.deva -otf.tables.defaults.syrc=otf.tables.defaults.arab -otf.tables.defaults.mong=otf.tables.defaults.arab -otf.tables.defaults.nko=otf.tables.defaults.arab -otf.tables.defaults.tibt=otf.tables.defaults.khmr -otf.tables.defaults.lao=otf.tables.defaults.thai -local function parse_script(script) - if otf.tables.scripts[script] then - local dflt - if otf.tables.defaults[script] then - logs.report("load font","auto-selecting default features for script: %s",script) - dflt=otf.tables.defaults[script] - else - logs.report("load font","auto-selecting default features for script: dflt (was %s)",script) - dflt=otf.tables.defaults["dflt"] - end - for _,v in next,dflt do - list[v]="yes" - end - else - logs.report("load font","unknown script: %s",script) - end -end -local function issome () list.lookup=fonts.define.specify.colonized_default_lookup end -local function isfile () list.lookup='file' end -local function isname () list.lookup='name' end -local function thename(s) list.name=s end -local function issub (v) list.sub=v end -local function iskey (k,v) - if k=="script" then - parse_script(v) - end - list[k]=v -end -local function istrue (s) list[s]=true end -local function isfalse(s) list[s]=false end -local spaces=lpeg.P(" ")^0 -local namespec=(1-lpeg.S("/:("))^0 -local filespec=(lpeg.R("az","AZ")*lpeg.P(":"))^-1*(1-lpeg.S(":("))^1 -local crapspec=spaces*lpeg.P("/")*(((1-lpeg.P(":"))^0)/isstyle)*spaces -local filename=(lpeg.P("file:")/isfile*(filespec/thename))+(lpeg.P("[")*lpeg.P(true)/isfile*(((1-lpeg.P("]"))^0)/thename)*lpeg.P("]")) -local fontname=(lpeg.P("name:")/isname*(namespec/thename))+lpeg.P(true)/issome*(namespec/thename) -local sometext=(lpeg.R("az","AZ","09")+lpeg.S("+-."))^1 -local truevalue=lpeg.P("+")*spaces*(sometext/istrue) -local falsevalue=lpeg.P("-")*spaces*(sometext/isfalse) -local keyvalue=lpeg.P("+")+(lpeg.C(sometext)*spaces*lpeg.P("=")*spaces*lpeg.C(sometext))/iskey -local somevalue=sometext/istrue -local subvalue=lpeg.P("(")*(lpeg.C(lpeg.P(1-lpeg.S("()"))^1)/issub)*lpeg.P(")") -local option=spaces*(keyvalue+falsevalue+truevalue+somevalue)*spaces -local options=lpeg.P(":")*spaces*(lpeg.P(";")^0*option)^0 -local pattern=(filename+fontname)*subvalue^0*crapspec^0*options^0 -local normalize_meanings=fonts.otf.meanings.normalize -function fonts.define.specify.colonized(specification) - list={} - lpegmatch(pattern,specification.specification) - if list.style then - specification.style=list.style - list.style=nil - end - if list.optsize then - specification.optsize=list.optsize - list.optsize=nil - end - if list.name then - if resolvers.find_file(list.name,"tfm") then - list.lookup="file" - list.name=file.addsuffix(list.name,"tfm") - elseif resolvers.find_file(list.name,"ofm") then - list.lookup="file" - list.name=file.addsuffix(list.name,"ofm") - end - 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=normalize_meanings(list) - return specification -end -fonts.define.register_split(":",fonts.define.specify.colonized) - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-dum']={ - version=1.001, - comment="companion to luatex-*.tex", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -fonts=fonts or {} -fonts.otf.pack=false -fonts.tfm.resolve_vf=false -fonts.tfm.fontname_mode="specification" -fonts.tfm.readers=fonts.tfm.readers or {} -fonts.tfm.readers.sequence={ 'otf','ttf','tfm' } -fonts.tfm.readers.afm=nil -fonts.define=fonts.define or {} -fonts.define.specify.colonized_default_lookup="name" -function fonts.define.get_specification(str) - return "",str,"",":",str -end -fonts.logger=fonts.logger or {} -function fonts.logger.save() -end -fonts.names=fonts.names or {} -fonts.names.version=1.001 -fonts.names.basename="luatex-fonts-names.lua" -fonts.names.new_to_old={} -fonts.names.old_to_new={} -local data,loaded=nil,false -local fileformats={ "lua","tex","other text files" } -function fonts.names.resolve(name,sub) - if not loaded then - local basename=fonts.names.basename - if basename and basename~="" then - for i=1,#fileformats do - local format=fileformats[i] - local foundname=resolvers.find_file(basename,format) or "" - if foundname~="" then - data=dofile(foundname) - break - 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 - else - return name,false - end - end -end -fonts.names.resolvespec=fonts.names.resolve -table.insert(fonts.triggers,"itlc") -local function itlc(tfmdata,value) - if value then - local metadata=tfmdata.shared.otfdata.metadata - if metadata then - local italicangle=metadata.italicangle - if italicangle and italicangle~=0 then - local uwidth=(metadata.uwidth or 40)/2 - for unicode,d in next,tfmdata.descriptions do - local it=d.boundingbox[3]-d.width+uwidth - if it~=0 then - d.italic=it - end - end - tfmdata.has_italic=true - end - end - end -end -fonts.initializers.base.otf.itlc=itlc -fonts.initializers.node.otf.itlc=itlc -function fonts.initializers.common.slant(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.slant_factor=value -end -function fonts.initializers.common.extend(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.extend_factor=value -end -table.insert(fonts.triggers,"slant") -table.insert(fonts.triggers,"extend") -fonts.initializers.base.otf.slant=fonts.initializers.common.slant -fonts.initializers.node.otf.slant=fonts.initializers.common.slant -fonts.initializers.base.otf.extend=fonts.initializers.common.extend -fonts.initializers.node.otf.extend=fonts.initializers.common.extend -fonts.protrusions=fonts.protrusions or {} -fonts.protrusions.setups=fonts.protrusions.setups or {} -local setups=fonts.protrusions.setups -local function map_opbd_onto_protrusion(tfmdata,value,opbd) - local characters,descriptions=tfmdata.characters,tfmdata.descriptions - local otfdata=tfmdata.shared.otfdata - local singles=otfdata.shared.featuredata.gpos_single - local script,language=tfmdata.script,tfmdata.language - local done,factor,left,right=false,1,1,1 - local setup=setups[value] - if setup then - factor=setup.factor or 1 - left=setup.left or 1 - right=setup.right or 1 - else - factor=tonumber(value) or 1 - end - if opbd~="right" then - local validlookups,lookuplist=fonts.otf.collect_lookups(otfdata,"lfbd",script,language) - if validlookups then - for i=1,#lookuplist do - local lookup=lookuplist[i] - local data=singles[lookup] - if data then - if trace_protrusion then - logs.report("fonts","set left protrusion using lfbd lookup '%s'",lookup) - end - for k,v in next,data do - local p=- (v[1]/1000)*factor*left - characters[k].left_protruding=p - if trace_protrusion then - logs.report("opbd","lfbd -> %s -> 0x%05X (%s) -> %0.03f (%s)",lookup,k,utfchar(k),p,concat(v," ")) - end - end - done=true - end - end - end - end - if opbd~="left" then - local validlookups,lookuplist=fonts.otf.collect_lookups(otfdata,"rtbd",script,language) - if validlookups then - for i=1,#lookuplist do - local lookup=lookuplist[i] - local data=singles[lookup] - if data then - if trace_protrusion then - logs.report("fonts","set right protrusion using rtbd lookup '%s'",lookup) - end - for k,v in next,data do - local p=(v[1]/1000)*factor*right - characters[k].right_protruding=p - if trace_protrusion then - logs.report("opbd","rtbd -> %s -> 0x%05X (%s) -> %0.03f (%s)",lookup,k,utfchar(k),p,concat(v," ")) - end - end - end - done=true - end - end - end - tfmdata.auto_protrude=done -end -function fonts.initializers.common.protrusion(tfmdata,value) - if value then - local opbd=tfmdata.shared.features.opbd - if opbd then - map_opbd_onto_protrusion(tfmdata,value,opbd) - elseif 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.auto_protrude=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 -end -fonts.expansions=fonts.expansions or {} -fonts.expansions.setups=fonts.expansions.setups or {} -local setups=fonts.expansions.setups -function fonts.initializers.common.expansion(tfmdata,value) - if value then - local setup=setups[value] - if setup then - local stretch,shrink,step,factor=setup.stretch or 0,setup.shrink or 0,setup.step or 0,setup.factor or 1 - tfmdata.stretch,tfmdata.shrink,tfmdata.step,tfmdata.auto_expand=stretch*10,shrink*10,step*10,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 -table.insert(fonts.manipulators,"protrusion") -table.insert(fonts.manipulators,"expansion") -fonts.initializers.base.otf.protrusion=fonts.initializers.common.protrusion -fonts.initializers.node.otf.protrusion=fonts.initializers.common.protrusion -fonts.initializers.base.otf.expansion=fonts.initializers.common.expansion -fonts.initializers.node.otf.expansion=fonts.initializers.common.expansion -function fonts.register_message() -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.otf.meanings=fonts.otf.meanings or {} -fonts.otf.meanings.normalize=fonts.otf.meanings.normalize or function(t) - if t.rand then - t.rand="random" - end -end -function fonts.otf.name_to_slot(name) - local tfmdata=fonts.ids[font.current()] - if tfmdata and tfmdata.shared then - local otfdata=tfmdata.shared.otfdata - local unicode=otfdata.luatex.unicodes[name] - return unicode and (type(unicode)=="number" and unicode or unicode[1]) - end -end -function fonts.otf.char(n) - if type(n)=="string" then - n=fonts.otf.name_to_slot(n) - end - if type(n)=="number" then - tex.sprint("\\char"..n) - end -end -fonts.strippables=table.tohash { - 0x000AD,0x017B4,0x017B5,0x0200B,0x0200C,0x0200D,0x0200E,0x0200F,0x0202A,0x0202B, - 0x0202C,0x0202D,0x0202E,0x02060,0x02061,0x02062,0x02063,0x0206A,0x0206B,0x0206C, - 0x0206D,0x0206E,0x0206F,0x0FEFF,0x1D173,0x1D174,0x1D175,0x1D176,0x1D177,0x1D178, - 0x1D179,0x1D17A,0xE0001,0xE0020,0xE0021,0xE0022,0xE0023,0xE0024,0xE0025,0xE0026, - 0xE0027,0xE0028,0xE0029,0xE002A,0xE002B,0xE002C,0xE002D,0xE002E,0xE002F,0xE0030, - 0xE0031,0xE0032,0xE0033,0xE0034,0xE0035,0xE0036,0xE0037,0xE0038,0xE0039,0xE003A, - 0xE003B,0xE003C,0xE003D,0xE003E,0xE003F,0xE0040,0xE0041,0xE0042,0xE0043,0xE0044, - 0xE0045,0xE0046,0xE0047,0xE0048,0xE0049,0xE004A,0xE004B,0xE004C,0xE004D,0xE004E, - 0xE004F,0xE0050,0xE0051,0xE0052,0xE0053,0xE0054,0xE0055,0xE0056,0xE0057,0xE0058, - 0xE0059,0xE005A,0xE005B,0xE005C,0xE005D,0xE005E,0xE005F,0xE0060,0xE0061,0xE0062, - 0xE0063,0xE0064,0xE0065,0xE0066,0xE0067,0xE0068,0xE0069,0xE006A,0xE006B,0xE006C, - 0xE006D,0xE006E,0xE006F,0xE0070,0xE0071,0xE0072,0xE0073,0xE0074,0xE0075,0xE0076, - 0xE0077,0xE0078,0xE0079,0xE007A,0xE007B,0xE007C,0xE007D,0xE007E,0xE007F, -} - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-clr']={ - version=1.001, - comment="companion to font-otf.lua (font color)", - author="Khaled Hosny and Elie Roux", - copyright="Luaotfload Development Team", - license="GPL" -} -fonts.triggers=fonts.triggers or {} -fonts.initializers=fonts.initializers or {} -fonts.initializers.common=fonts.initializers.common or {} -local initializers,format=fonts.initializers,string.format -table.insert(fonts.triggers,"color") -function initializers.common.color(tfmdata,value) - local sanitized - if value then - value=tostring(value) - if #value==6 or #value==8 then - sanitized=value - elseif #value==7 then - _,_,sanitized=value:find("(......)") - elseif #value>8 then - _,_,sanitized=value:find("(........)") - else - end - end - if sanitized then - tfmdata.color=sanitized - add_color_callback() - end -end -initializers.base.otf.color=initializers.common.color -initializers.node.otf.color=initializers.common.color -local function hex2dec(hex,one) - if one then - return format("%.1g",tonumber(hex,16)/255) - else - return format("%.3g",tonumber(hex,16)/255) - end -end -local res -local function pageresources(a) - local res2 - if not res then - res="/TransGs1<</ca 1/CA 1>>" - end - res2=format("/TransGs%s<</ca %s/CA %s>>",a,a,a) - res=format("%s%s",res,res:find(res2) and "" or res2) -end -local function hex_to_rgba(hex) - local r,g,b,a,push,pop,res3 - if hex then - if #hex==6 then - _,_,r,g,b=hex:find('(..)(..)(..)') - elseif #hex==8 then - _,_,r,g,b,a=hex:find('(..)(..)(..)(..)') - a=hex2dec(a,true) - pageresources(a) - end - else - return nil - end - r=hex2dec(r) - g=hex2dec(g) - b=hex2dec(b) - if a then - push=format('/TransGs%g gs %s %s %s rg',a,r,g,b) - pop='0 g /TransGs1 gs' - else - push=format('%s %s %s rg',r,g,b) - pop='0 g' - end - return push,pop -end -local glyph=node.id('glyph') -local hlist=node.id('hlist') -local vlist=node.id('vlist') -local whatsit=node.id('whatsit') -local pgi=node.id('page_insert') -local sbox=node.id('sub_box') -local function lookup_next_color(head) - for n in node.traverse(head) do - if n.id==glyph then - if fonts.ids[n.font] and fonts.ids[n.font].color then - return fonts.ids[n.font].color - else - return -1 - end - elseif n.id==vlist or n.id==hlist or n.id==sbox then - local r=lookup_next_color(n.list) - if r==-1 then - return -1 - elseif r then - return r - end - elseif n.id==whatsit or n.id==pgi then - return -1 - end - end - return nil -end -local function node_colorize(head,current_color,next_color) - for n in node.traverse(head) do - if n.id==hlist or n.id==vlist or n.id==sbox then - local next_color_in=lookup_next_color(n.next) or next_color - n.list,current_color=node_colorize(n.list,current_color,next_color_in) - elseif n.id==glyph then - local tfmdata=fonts.ids[n.font] - if tfmdata and tfmdata.color then - if tfmdata.color~=current_color then - local pushcolor=hex_to_rgba(tfmdata.color) - local push=node.new(whatsit,8) - push.mode=1 - push.data=pushcolor - head=node.insert_before(head,n,push) - current_color=tfmdata.color - end - local next_color_in=lookup_next_color (n.next) or next_color - if next_color_in~=tfmdata.color then - local _,popcolor=hex_to_rgba(tfmdata.color) - local pop=node.new(whatsit,8) - pop.mode=1 - pop.data=popcolor - head=node.insert_after(head,n,pop) - current_color=nil - end - end - end - end - return head,current_color -end -local function font_colorize(head) - if res then - local r="/ExtGState<<"..res..">>" - tex.pdfpageresources=tex.pdfpageresources:gsub(r,"") - end - local h=node_colorize(head,nil,nil) - if res and res:find("%S") then - local r="/ExtGState<<"..res..">>" - tex.pdfpageresources=tex.pdfpageresources..r - end - return h -end -local color_callback_activated=0 -function add_color_callback() - if color_callback_activated==0 then - luatexbase.add_to_callback("pre_output_filter",font_colorize,"loaotfload.colorize") - color_callback_activated=1 - end -end - -end -- closure |