do --- [luaotfload, fontloader-2017-02-11.lua scope for “data-con” d8982c834ed9acc6193eee23067b9d5d] ---
if not modules then modules={} end modules ['data-con']={
version=1.100,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local format,lower,gsub=string.format,string.lower,string.gsub
local trace_cache=false trackers.register("resolvers.cache",function(v) trace_cache=v end)
local trace_containers=false trackers.register("resolvers.containers",function(v) trace_containers=v end)
local trace_storage=false trackers.register("resolvers.storage",function(v) trace_storage=v end)
containers=containers or {}
local containers=containers
containers.usecache=true
local report_containers=logs.reporter("resolvers","containers")
local allocated={}
local mt={
__index=function(t,k)
if k=="writable" then
local writable=caches.getwritablepath(t.category,t.subcategory) or { "." }
t.writable=writable
return writable
elseif k=="readables" then
local readables=caches.getreadablepaths(t.category,t.subcategory) or { "." }
t.readables=readables
return readables
end
end,
__storage__=true
}
function containers.define(category,subcategory,version,enabled)
if category and subcategory then
local c=allocated[category]
if not c then
c={}
allocated[category]=c
end
local s=c[subcategory]
if not s then
s={
category=category,
subcategory=subcategory,
storage={},
enabled=enabled,
version=version or math.pi,
trace=false,
}
setmetatable(s,mt)
c[subcategory]=s
end
return s
end
end
function containers.is_usable(container,name)
return container.enabled and caches and caches.is_writable(container.writable,name)
end
function containers.is_valid(container,name)
if name and name~="" then
local storage=container.storage[name]
return storage and storage.cache_version==container.version
else
return false
end
end
function containers.read(container,name)
local storage=container.storage
local stored=storage[name]
if not stored and container.enabled and caches and containers.usecache then
stored=caches.loaddata(container.readables,name,container.writable)
if stored and stored.cache_version==container.version then
if trace_cache or trace_containers then
report_containers("action %a, category %a, name %a","load",container.subcategory,name)
end
else
stored=nil
end
storage[name]=stored
elseif stored then
if trace_cache or trace_containers then
report_containers("action %a, category %a, name %a","reuse",container.subcategory,name)
end
end
return stored
end
function containers.write(container,name,data)
if data then
data.cache_version=container.version
if container.enabled and caches then
local unique,shared=data.unique,data.shared
data.unique,data.shared=nil,nil
caches.savedata(container.writable,name,data)
if trace_cache or trace_containers then
report_containers("action %a, category %a, name %a","save",container.subcategory,name)
end
data.unique,data.shared=unique,shared
end
if trace_cache or trace_containers then
report_containers("action %a, category %a, name %a","store",container.subcategory,name)
end
container.storage[name]=data
end
return data
end
function containers.content(container,name)
return container.storage[name]
end
function containers.cleanname(name)
return (gsub(lower(name),"[^%w\128-\255]+","-"))
end
end --- [luaotfload, fontloader-2017-02-11.lua scope for “data-con”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “basics-nod” 9288471b8395bfb683aba0ff3964d950] ---
if not modules then modules={} end modules ['luatex-fonts-nod']={
version=1.001,
comment="companion to luatex-fonts.lua",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
if context then
texio.write_nl("fatal error: this module is not for context")
os.exit()
end
if tex.attribute[0]~=0 then
texio.write_nl("log","!")
texio.write_nl("log","! Attribute 0 is reserved for ConTeXt's font feature management and has to be")
texio.write_nl("log","! set to zero. Also, some attributes in the range 1-255 are used for special")
texio.write_nl("log","! purposes so setting them at the TeX end might break the font handler.")
texio.write_nl("log","!")
tex.attribute[0]=0
end
attributes=attributes or {}
attributes.unsetvalue=-0x7FFFFFFF
local numbers,last={},127
attributes.private=attributes.private or function(name)
local number=numbers[name]
if not number then
if last<255 then
last=last+1
end
number=last
numbers[name]=number
end
return number
end
nodes={}
nodes.pool={}
nodes.handlers={}
local nodecodes={}
local glyphcodes=node.subtypes("glyph")
local disccodes=node.subtypes("disc")
for k,v in next,node.types() do
v=string.gsub(v,"_","")
nodecodes[k]=v
nodecodes[v]=k
end
for i=0,#glyphcodes do
glyphcodes[glyphcodes[i]]=i
end
for i=0,#disccodes do
disccodes[disccodes[i]]=i
end
nodes.nodecodes=nodecodes
nodes.glyphcodes=glyphcodes
nodes.disccodes=disccodes
local flush_node=node.flush_node
local remove_node=node.remove
local new_node=node.new
local traverse_id=node.traverse_id
nodes.handlers.protectglyphs=node.protect_glyphs
nodes.handlers.unprotectglyphs=node.unprotect_glyphs
local math_code=nodecodes.math
local end_of_math=node.end_of_math
function node.end_of_math(n)
if n.id==math_code and n.subtype==1 then
return n
else
return end_of_math(n)
end
end
function nodes.remove(head,current,free_too)
local t=current
head,current=remove_node(head,current)
if t then
if free_too then
flush_node(t)
t=nil
else
t.next,t.prev=nil,nil
end
end
return head,current,t
end
function nodes.delete(head,current)
return nodes.remove(head,current,true)
end
function nodes.pool.kern(k)
local n=new_node("kern",1)
n.kern=k
return n
end
local getfield=node.getfield
local setfield=node.setfield
nodes.getfield=getfield
nodes.setfield=setfield
nodes.getattr=getfield
nodes.setattr=setfield
nodes.tostring=node.tostring or tostring
nodes.copy=node.copy
nodes.copy_node=node.copy
nodes.copy_list=node.copy_list
nodes.delete=node.delete
nodes.dimensions=node.dimensions
nodes.end_of_math=node.end_of_math
nodes.flush_list=node.flush_list
nodes.flush_node=node.flush_node
nodes.flush=node.flush_node
nodes.free=node.free
nodes.insert_after=node.insert_after
nodes.insert_before=node.insert_before
nodes.hpack=node.hpack
nodes.new=node.new
nodes.tail=node.tail
nodes.traverse=node.traverse
nodes.traverse_id=node.traverse_id
nodes.slide=node.slide
nodes.vpack=node.vpack
nodes.first_glyph=node.first_glyph
nodes.has_glyph=node.has_glyph or node.first_glyph
nodes.current_attr=node.current_attr
nodes.has_field=node.has_field
nodes.last_node=node.last_node
nodes.usedlist=node.usedlist
nodes.protrusion_skippable=node.protrusion_skippable
nodes.write=node.write
nodes.has_attribute=node.has_attribute
nodes.set_attribute=node.set_attribute
nodes.unset_attribute=node.unset_attribute
nodes.protect_glyphs=node.protect_glyphs
nodes.unprotect_glyphs=node.unprotect_glyphs
nodes.mlist_to_hlist=node.mlist_to_hlist
local direct=node.direct
local nuts={}
nodes.nuts=nuts
local tonode=direct.tonode
local tonut=direct.todirect
nodes.tonode=tonode
nodes.tonut=tonut
nuts.tonode=tonode
nuts.tonut=tonut
local getfield=direct.getfield
local setfield=direct.setfield
nuts.getfield=getfield
nuts.setfield=setfield
nuts.getnext=direct.getnext
nuts.setnext=direct.setnext
nuts.getprev=direct.getprev
nuts.setprev=direct.setprev
nuts.getboth=direct.getboth
nuts.setboth=direct.setboth
nuts.getid=direct.getid
nuts.getattr=direct.get_attribute or direct.has_attribute or getfield
nuts.setattr=setfield
nuts.getfont=direct.getfont
nuts.setfont=direct.setfont
nuts.getsubtype=direct.getsubtype
nuts.setsubtype=direct.setsubtype or function(n,s) setfield(n,"subtype",s) end
nuts.getchar=direct.getchar
nuts.setchar=direct.setchar
nuts.getdisc=direct.getdisc
nuts.setdisc=direct.setdisc
nuts.setlink=direct.setlink
nuts.getlist=direct.getlist
nuts.setlist=direct.setlist or function(n,l) setfield(n,"list",l) end
nuts.getleader=direct.getleader
nuts.setleader=direct.setleader or function(n,l) setfield(n,"leader",l) end
if not direct.is_glyph then
local getchar=direct.getchar
local getid=direct.getid
local getfont=direct.getfont
local glyph_code=nodes.nodecodes.glyph
function direct.is_glyph(n,f)
local id=getid(n)
if id==glyph_code then
if f and getfont(n)==f then
return getchar(n)
else
return false
end
else
return nil,id
end
end
function direct.is_char(n,f)
local id=getid(n)
if id==glyph_code then
if getsubtype(n)>=256 then
return false
elseif f and getfont(n)==f then
return getchar(n)
else
return false
end
else
return nil,id
end
end
end
nuts.ischar=direct.is_char
nuts.is_char=direct.is_char
nuts.isglyph=direct.is_glyph
nuts.is_glyph=direct.is_glyph
nuts.insert_before=direct.insert_before
nuts.insert_after=direct.insert_after
nuts.delete=direct.delete
nuts.copy=direct.copy
nuts.copy_node=direct.copy
nuts.copy_list=direct.copy_list
nuts.tail=direct.tail
nuts.flush_list=direct.flush_list
nuts.flush_node=direct.flush_node
nuts.flush=direct.flush
nuts.free=direct.free
nuts.remove=direct.remove
nuts.is_node=direct.is_node
nuts.end_of_math=direct.end_of_math
nuts.traverse=direct.traverse
nuts.traverse_id=direct.traverse_id
nuts.traverse_char=direct.traverse_char
nuts.ligaturing=direct.ligaturing
nuts.kerning=direct.kerning
nuts.getprop=nuts.getattr
nuts.setprop=nuts.setattr
local new_nut=direct.new
nuts.new=new_nut
nuts.pool={}
function nuts.pool.kern(k)
local n=new_nut("kern",1)
setfield(n,"kern",k)
return n
end
local propertydata=direct.get_properties_table()
nodes.properties={ data=propertydata }
direct.set_properties_mode(true,true)
function direct.set_properties_mode() end
nuts.getprop=function(n,k)
local p=propertydata[n]
if p then
return p[k]
end
end
nuts.setprop=function(n,k,v)
if v then
local p=propertydata[n]
if p then
p[k]=v
else
propertydata[n]={ [k]=v }
end
end
end
nodes.setprop=nodes.setproperty
nodes.getprop=nodes.getproperty
end --- [luaotfload, fontloader-2017-02-11.lua scope for “basics-nod”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-ini” 10cb9a563a98e06ff79c35a8751e13dc] ---
if not modules then modules={} end modules ['font-ini']={
version=1.001,
comment="companion to font-ini.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local allocate=utilities.storage.allocate
fonts=fonts or {}
local fonts=fonts
fonts.hashes={ identifiers=allocate() }
fonts.tables=fonts.tables or {}
fonts.helpers=fonts.helpers or {}
fonts.tracers=fonts.tracers or {}
fonts.specifiers=fonts.specifiers or {}
fonts.analyzers={}
fonts.readers={}
fonts.definers={ methods={} }
fonts.loggers={ register=function() end }
fontloader.totable=fontloader.to_table
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-ini”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-con” 7575a7b4e6d04816072945e27d7d0b33] ---
if not modules then modules={} end modules ['font-con']={
version=1.001,
comment="companion to font-ini.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local next,tostring,rawget=next,tostring,rawget
local format,match,lower,gsub,find=string.format,string.match,string.lower,string.gsub,string.find
local sort,insert,concat,sortedkeys,serialize,fastcopy=table.sort,table.insert,table.concat,table.sortedkeys,table.serialize,table.fastcopy
local derivetable=table.derive
local ioflush=io.flush
local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end)
local trace_scaling=false trackers.register("fonts.scaling",function(v) trace_scaling=v end)
local report_defining=logs.reporter("fonts","defining")
local fonts=fonts
local constructors=fonts.constructors or {}
fonts.constructors=constructors
local handlers=fonts.handlers or {}
fonts.handlers=handlers
local allocate=utilities.storage.allocate
local setmetatableindex=table.setmetatableindex
constructors.dontembed=allocate()
constructors.autocleanup=true
constructors.namemode="fullpath"
constructors.version=1.01
constructors.cache=containers.define("fonts","constructors",constructors.version,false)
constructors.privateoffset=0xF0000
constructors.cacheintex=true
local designsizes=allocate()
constructors.designsizes=designsizes
local loadedfonts=allocate()
constructors.loadedfonts=loadedfonts
local factors={
pt=65536.0,
bp=65781.8,
}
function constructors.setfactor(f)
constructors.factor=factors[f or 'pt'] or factors.pt
end
constructors.setfactor()
function constructors.scaled(scaledpoints,designsize)
if scaledpoints<0 then
local factor=constructors.factor
if designsize then
if designsize>factor then
return (- scaledpoints/1000)*designsize
else
return (- scaledpoints/1000)*designsize*factor
end
else
return (- scaledpoints/1000)*10*factor
end
else
return scaledpoints
end
end
function constructors.cleanuptable(tfmdata)
if constructors.autocleanup and tfmdata.properties.virtualized then
for k,v in next,tfmdata.characters do
if v.commands then v.commands=nil end
end
end
end
function constructors.calculatescale(tfmdata,scaledpoints)
local parameters=tfmdata.parameters
if scaledpoints<0 then
scaledpoints=(- scaledpoints/1000)*(tfmdata.designsize or parameters.designsize)
end
return scaledpoints,scaledpoints/(parameters.units or 1000)
end
local unscaled={
ScriptPercentScaleDown=true,
ScriptScriptPercentScaleDown=true,
RadicalDegreeBottomRaisePercent=true,
NoLimitSupFactor=true,
NoLimitSubFactor=true,
}
function constructors.assignmathparameters(target,original)
local mathparameters=original.mathparameters
if mathparameters and next(mathparameters) then
local targetparameters=target.parameters
local targetproperties=target.properties
local targetmathparameters={}
local factor=targetproperties.math_is_scaled and 1 or targetparameters.factor
for name,value in next,mathparameters do
if unscaled[name] then
targetmathparameters[name]=value
else
targetmathparameters[name]=value*factor
end
end
if not targetmathparameters.FractionDelimiterSize then
targetmathparameters.FractionDelimiterSize=1.01*targetparameters.size
end
if not mathparameters.FractionDelimiterDisplayStyleSize then
targetmathparameters.FractionDelimiterDisplayStyleSize=2.40*targetparameters.size
end
target.mathparameters=targetmathparameters
end
end
function constructors.beforecopyingcharacters(target,original)
end
function constructors.aftercopyingcharacters(target,original)
end
constructors.sharefonts=false
constructors.nofsharedfonts=0
local sharednames={}
function constructors.trytosharefont(target,tfmdata)
if constructors.sharefonts then
local characters=target.characters
local n=1
local t={ target.psname }
local u=sortedkeys(characters)
for i=1,#u do
local k=u[i]
n=n+1;t[n]=k
n=n+1;t[n]=characters[k].index or k
end
local h=md5.HEX(concat(t," "))
local s=sharednames[h]
if s then
if trace_defining then
report_defining("font %a uses backend resources of font %a",target.fullname,s)
end
target.fullname=s
constructors.nofsharedfonts=constructors.nofsharedfonts+1
target.properties.sharedwith=s
else
sharednames[h]=target.fullname
end
end
end
function constructors.enhanceparameters(parameters)
local xheight=parameters.x_height
local quad=parameters.quad
local space=parameters.space
local stretch=parameters.space_stretch
local shrink=parameters.space_shrink
local extra=parameters.extra_space
local slant=parameters.slant
parameters.xheight=xheight
parameters.spacestretch=stretch
parameters.spaceshrink=shrink
parameters.extraspace=extra
parameters.em=quad
parameters.ex=xheight
parameters.slantperpoint=slant
parameters.spacing={
width=space,
stretch=stretch,
shrink=shrink,
extra=extra,
}
end
local function mathkerns(v,vdelta)
local k={}
for i=1,#v do
local entry=v[i]
local height=entry.height
local kern=entry.kern
k[i]={
height=height and vdelta*height or 0,
kern=kern and vdelta*kern or 0,
}
end
return k
end
local psfake=0
local function fixedpsname(psname,fallback)
local usedname=psname
if psname and psname~="" then
if find(psname," ") then
usedname=gsub(psname,"[%s]+","-")
else
end
elseif not fallback or fallback=="" then
psfake=psfake+1
psname="fakename-"..psfake
else
psname=fallback
usedname=gsub(psname,"[^a-zA-Z0-9]+","-")
end
return usedname,psname~=usedname
end
function constructors.scale(tfmdata,specification)
local target={}
if tonumber(specification) then
specification={ size=specification }
end
target.specification=specification
local scaledpoints=specification.size
local relativeid=specification.relativeid
local properties=tfmdata.properties or {}
local goodies=tfmdata.goodies or {}
local resources=tfmdata.resources or {}
local descriptions=tfmdata.descriptions or {}
local characters=tfmdata.characters or {}
local changed=tfmdata.changed or {}
local shared=tfmdata.shared or {}
local parameters=tfmdata.parameters or {}
local mathparameters=tfmdata.mathparameters or {}
local targetcharacters={}
local targetdescriptions=derivetable(descriptions)
local targetparameters=derivetable(parameters)
local targetproperties=derivetable(properties)
local targetgoodies=goodies
target.characters=targetcharacters
target.descriptions=targetdescriptions
target.parameters=targetparameters
target.properties=targetproperties
target.goodies=targetgoodies
target.shared=shared
target.resources=resources
target.unscaled=tfmdata
local mathsize=tonumber(specification.mathsize) or 0
local textsize=tonumber(specification.textsize) or scaledpoints
local forcedsize=tonumber(parameters.mathsize ) or 0
local extrafactor=tonumber(specification.factor ) or 1
if (mathsize==2 or forcedsize==2) and parameters.scriptpercentage then
scaledpoints=parameters.scriptpercentage*textsize/100
elseif (mathsize==3 or forcedsize==3) and parameters.scriptscriptpercentage then
scaledpoints=parameters.scriptscriptpercentage*textsize/100
elseif forcedsize>1000 then
scaledpoints=forcedsize
end
targetparameters.mathsize=mathsize
targetparameters.textsize=textsize
targetparameters.forcedsize=forcedsize
targetparameters.extrafactor=extrafactor
local tounicode=fonts.mappings.tounicode
local defaultwidth=resources.defaultwidth or 0
local defaultheight=resources.defaultheight or 0
local defaultdepth=resources.defaultdepth or 0
local units=parameters.units or 1000
if target.fonts then
target.fonts=fastcopy(target.fonts)
end
targetproperties.language=properties.language or "dflt"
targetproperties.script=properties.script or "dflt"
targetproperties.mode=properties.mode or "base"
local askedscaledpoints=scaledpoints
local scaledpoints,delta=constructors.calculatescale(tfmdata,scaledpoints,nil,specification)
local hdelta=delta
local vdelta=delta
target.designsize=parameters.designsize
target.units=units
target.units_per_em=units
local direction=properties.direction or tfmdata.direction or 0
target.direction=direction
properties.direction=direction
target.size=scaledpoints
target.encodingbytes=properties.encodingbytes or 1
target.embedding=properties.embedding or "subset"
target.tounicode=1
target.cidinfo=properties.cidinfo
target.format=properties.format
target.cache=constructors.cacheintex and "yes" or "renew"
local fontname=properties.fontname or tfmdata.fontname
local fullname=properties.fullname or tfmdata.fullname
local filename=properties.filename or tfmdata.filename
local psname=properties.psname or tfmdata.psname
local name=properties.name or tfmdata.name
local psname,psfixed=fixedpsname(psname,fontname or fullname or file.nameonly(filename))
target.fontname=fontname
target.fullname=fullname
target.filename=filename
target.psname=psname
target.name=name
properties.fontname=fontname
properties.fullname=fullname
properties.filename=filename
properties.psname=psname
properties.name=name
local expansion=parameters.expansion
if expansion then
target.stretch=expansion.stretch
target.shrink=expansion.shrink
target.step=expansion.step
target.auto_expand=expansion.auto
end
local protrusion=parameters.protrusion
if protrusion then
target.auto_protrude=protrusion.auto
end
local extendfactor=parameters.extendfactor or 0
if extendfactor~=0 and extendfactor~=1 then
hdelta=hdelta*extendfactor
target.extend=extendfactor*1000
else
target.extend=1000
end
local slantfactor=parameters.slantfactor or 0
if slantfactor~=0 then
target.slant=slantfactor*1000
else
target.slant=0
end
targetparameters.factor=delta
targetparameters.hfactor=hdelta
targetparameters.vfactor=vdelta
targetparameters.size=scaledpoints
targetparameters.units=units
targetparameters.scaledpoints=askedscaledpoints
local isvirtual=properties.virtualized or tfmdata.type=="virtual"
local hasquality=target.auto_expand or target.auto_protrude
local hasitalics=properties.hasitalics
local autoitalicamount=properties.autoitalicamount
local stackmath=not properties.nostackmath
local nonames=properties.noglyphnames
local haskerns=properties.haskerns or properties.mode=="base"
local hasligatures=properties.hasligatures or properties.mode=="base"
local realdimensions=properties.realdimensions
local writingmode=properties.writingmode or "horizontal"
local identity=properties.identity or "horizontal"
if changed and not next(changed) then
changed=false
end
target.type=isvirtual and "virtual" or "real"
target.writingmode=writingmode=="vertical" and "vertical" or "horizontal"
target.identity=identity=="vertical" and "vertical" or "horizontal"
target.postprocessors=tfmdata.postprocessors
local targetslant=(parameters.slant or parameters[1] or 0)*factors.pt
local targetspace=(parameters.space or parameters[2] or 0)*hdelta
local targetspace_stretch=(parameters.space_stretch or parameters[3] or 0)*hdelta
local targetspace_shrink=(parameters.space_shrink or parameters[4] or 0)*hdelta
local targetx_height=(parameters.x_height or parameters[5] or 0)*vdelta
local targetquad=(parameters.quad or parameters[6] or 0)*hdelta
local targetextra_space=(parameters.extra_space or parameters[7] or 0)*hdelta
targetparameters.slant=targetslant
targetparameters.space=targetspace
targetparameters.space_stretch=targetspace_stretch
targetparameters.space_shrink=targetspace_shrink
targetparameters.x_height=targetx_height
targetparameters.quad=targetquad
targetparameters.extra_space=targetextra_space
local ascender=parameters.ascender
if ascender then
targetparameters.ascender=delta*ascender
end
local descender=parameters.descender
if descender then
targetparameters.descender=delta*descender
end
constructors.enhanceparameters(targetparameters)
local protrusionfactor=(targetquad~=0 and 1000/targetquad) or 0
local scaledwidth=defaultwidth*hdelta
local scaledheight=defaultheight*vdelta
local scaleddepth=defaultdepth*vdelta
local hasmath=(properties.hasmath or next(mathparameters)) and true
if hasmath then
constructors.assignmathparameters(target,tfmdata)
properties.hasmath=true
target.nomath=false
target.MathConstants=target.mathparameters
else
properties.hasmath=false
target.nomath=true
target.mathparameters=nil
end
if hasmath then
local mathitalics=properties.mathitalics
if mathitalics==false then
if trace_defining then
report_defining("%s italics %s for font %a, fullname %a, filename %a","math",hasitalics and "ignored" or "disabled",name,fullname,filename)
end
hasitalics=false
autoitalicamount=false
end
else
local textitalics=properties.textitalics
if textitalics==false then
if trace_defining then
report_defining("%s italics %s for font %a, fullname %a, filename %a","text",hasitalics and "ignored" or "disabled",name,fullname,filename)
end
hasitalics=false
autoitalicamount=false
end
end
if trace_defining then
report_defining("defining tfm, name %a, fullname %a, filename %a, %spsname %a, hscale %a, vscale %a, math %a, italics %a",
name,fullname,filename,psfixed and "(fixed) " or "",psname,hdelta,vdelta,
hasmath and "enabled" or "disabled",hasitalics and "enabled" or "disabled")
end
constructors.beforecopyingcharacters(target,tfmdata)
local sharedkerns={}
for unicode,character in next,characters do
local chr,description,index
if changed then
local c=changed[unicode]
if c then
description=descriptions[c] or descriptions[unicode] or character
character=characters[c] or character
index=description.index or c
else
description=descriptions[unicode] or character
index=description.index or unicode
end
else
description=descriptions[unicode] or character
index=description.index or unicode
end
local width=description.width
local height=description.height
local depth=description.depth
if realdimensions then
if not height or height==0 then
local bb=description.boundingbox
local ht=bb[4]
if ht~=0 then
height=ht
end
if not depth or depth==0 then
local dp=-bb[2]
if dp~=0 then
depth=dp
end
end
elseif not depth or depth==0 then
local dp=-description.boundingbox[2]
if dp~=0 then
depth=dp
end
end
end
if width then width=hdelta*width else width=scaledwidth end
if height then height=vdelta*height else height=scaledheight end
if depth and depth~=0 then
depth=delta*depth
if nonames then
chr={
index=index,
height=height,
depth=depth,
width=width,
}
else
chr={
name=description.name,
index=index,
height=height,
depth=depth,
width=width,
}
end
else
if nonames then
chr={
index=index,
height=height,
width=width,
}
else
chr={
name=description.name,
index=index,
height=height,
width=width,
}
end
end
local isunicode=description.unicode
if isunicode then
chr.unicode=isunicode
chr.tounicode=tounicode(isunicode)
end
if hasquality then
local ve=character.expansion_factor
if ve then
chr.expansion_factor=ve*1000
end
local vl=character.left_protruding
if vl then
chr.left_protruding=protrusionfactor*width*vl
end
local vr=character.right_protruding
if vr then
chr.right_protruding=protrusionfactor*width*vr
end
end
if hasmath then
local vn=character.next
if vn then
chr.next=vn
else
local vv=character.vert_variants
if vv then
local t={}
for i=1,#vv do
local vvi=vv[i]
t[i]={
["start"]=(vvi["start"] or 0)*vdelta,
["end"]=(vvi["end"] or 0)*vdelta,
["advance"]=(vvi["advance"] or 0)*vdelta,
["extender"]=vvi["extender"],
["glyph"]=vvi["glyph"],
}
end
chr.vert_variants=t
else
local hv=character.horiz_variants
if hv then
local t={}
for i=1,#hv do
local hvi=hv[i]
t[i]={
["start"]=(hvi["start"] or 0)*hdelta,
["end"]=(hvi["end"] or 0)*hdelta,
["advance"]=(hvi["advance"] or 0)*hdelta,
["extender"]=hvi["extender"],
["glyph"]=hvi["glyph"],
}
end
chr.horiz_variants=t
end
end
end
local vi=character.vert_italic
if vi and vi~=0 then
chr.vert_italic=vi*hdelta
end
local va=character.accent
if va then
chr.top_accent=vdelta*va
end
if stackmath then
local mk=character.mathkerns
if mk then
local tr,tl,br,bl=mk.topright,mk.topleft,mk.bottomright,mk.bottomleft
chr.mathkern={
top_right=tr and mathkerns(tr,vdelta) or nil,
top_left=tl and mathkerns(tl,vdelta) or nil,
bottom_right=br and mathkerns(br,vdelta) or nil,
bottom_left=bl and mathkerns(bl,vdelta) or nil,
}
end
end
if hasitalics then
local vi=character.italic
if vi and vi~=0 then
chr.italic=vi*hdelta
end
end
elseif autoitalicamount then
local vi=description.italic
if not vi then
local bb=description.boundingbox
if bb then
local vi=bb[3]-description.width+autoitalicamount
if vi>0 then
chr.italic=vi*hdelta
end
else
end
elseif vi~=0 then
chr.italic=vi*hdelta
end
elseif hasitalics then
local vi=character.italic
if vi and vi~=0 then
chr.italic=vi*hdelta
end
end
if haskerns then
local vk=character.kerns
if vk then
local s=sharedkerns[vk]
if not s then
s={}
for k,v in next,vk do s[k]=v*hdelta end
sharedkerns[vk]=s
end
chr.kerns=s
end
end
if hasligatures then
local vl=character.ligatures
if vl then
if true then
chr.ligatures=vl
else
local tt={}
for i,l in next,vl do
tt[i]=l
end
chr.ligatures=tt
end
end
end
if isvirtual then
local vc=character.commands
if vc then
local ok=false
for i=1,#vc do
local key=vc[i][1]
if key=="right" or key=="down" then
ok=true
break
end
end
if ok then
local tt={}
for i=1,#vc do
local ivc=vc[i]
local key=ivc[1]
if key=="right" then
tt[i]={ key,ivc[2]*hdelta }
elseif key=="down" then
tt[i]={ key,ivc[2]*vdelta }
elseif key=="rule" then
tt[i]={ key,ivc[2]*vdelta,ivc[3]*hdelta }
else
tt[i]=ivc
end
end
chr.commands=tt
else
chr.commands=vc
end
chr.index=nil
end
end
targetcharacters[unicode]=chr
end
properties.setitalics=hasitalics
constructors.aftercopyingcharacters(target,tfmdata)
constructors.trytosharefont(target,tfmdata)
return target
end
function constructors.finalize(tfmdata)
if tfmdata.properties and tfmdata.properties.finalized then
return
end
if not tfmdata.characters then
return nil
end
if not tfmdata.goodies then
tfmdata.goodies={}
end
local parameters=tfmdata.parameters
if not parameters then
return nil
end
if not parameters.expansion then
parameters.expansion={
stretch=tfmdata.stretch or 0,
shrink=tfmdata.shrink or 0,
step=tfmdata.step or 0,
auto=tfmdata.auto_expand or false,
}
end
if not parameters.protrusion then
parameters.protrusion={
auto=auto_protrude
}
end
if not parameters.size then
parameters.size=tfmdata.size
end
if not parameters.extendfactor then
parameters.extendfactor=tfmdata.extend or 0
end
if not parameters.slantfactor then
parameters.slantfactor=tfmdata.slant or 0
end
local designsize=parameters.designsize
if designsize then
parameters.minsize=tfmdata.minsize or designsize
parameters.maxsize=tfmdata.maxsize or designsize
else
designsize=factors.pt*10
parameters.designsize=designsize
parameters.minsize=designsize
parameters.maxsize=designsize
end
parameters.minsize=tfmdata.minsize or parameters.designsize
parameters.maxsize=tfmdata.maxsize or parameters.designsize
if not parameters.units then
parameters.units=tfmdata.units or tfmdata.units_per_em or 1000
end
if not tfmdata.descriptions then
local descriptions={}
setmetatableindex(descriptions,function(t,k) local v={} t[k]=v return v end)
tfmdata.descriptions=descriptions
end
local properties=tfmdata.properties
if not properties then
properties={}
tfmdata.properties=properties
end
if not properties.virtualized then
properties.virtualized=tfmdata.type=="virtual"
end
if not tfmdata.properties then
tfmdata.properties={
fontname=tfmdata.fontname,
filename=tfmdata.filename,
fullname=tfmdata.fullname,
name=tfmdata.name,
psname=tfmdata.psname,
encodingbytes=tfmdata.encodingbytes or 1,
embedding=tfmdata.embedding or "subset",
tounicode=tfmdata.tounicode or 1,
cidinfo=tfmdata.cidinfo or nil,
format=tfmdata.format or "type1",
direction=tfmdata.direction or 0,
writingmode=tfmdata.writingmode or "horizontal",
identity=tfmdata.identity or "horizontal",
}
end
if not tfmdata.resources then
tfmdata.resources={}
end
if not tfmdata.shared then
tfmdata.shared={}
end
if not properties.hasmath then
properties.hasmath=not tfmdata.nomath
end
tfmdata.MathConstants=nil
tfmdata.postprocessors=nil
tfmdata.fontname=nil
tfmdata.filename=nil
tfmdata.fullname=nil
tfmdata.name=nil
tfmdata.psname=nil
tfmdata.encodingbytes=nil
tfmdata.embedding=nil
tfmdata.tounicode=nil
tfmdata.cidinfo=nil
tfmdata.format=nil
tfmdata.direction=nil
tfmdata.type=nil
tfmdata.nomath=nil
tfmdata.designsize=nil
tfmdata.size=nil
tfmdata.stretch=nil
tfmdata.shrink=nil
tfmdata.step=nil
tfmdata.auto_expand=nil
tfmdata.auto_protrude=nil
tfmdata.extend=nil
tfmdata.slant=nil
tfmdata.units=nil
tfmdata.units_per_em=nil
tfmdata.cache=nil
properties.finalized=true
return tfmdata
end
local hashmethods={}
constructors.hashmethods=hashmethods
function constructors.hashfeatures(specification)
local features=specification.features
if features then
local t,tn={},0
for category,list in next,features do
if next(list) then
local hasher=hashmethods[category]
if hasher then
local hash=hasher(list)
if hash then
tn=tn+1
t[tn]=category..":"..hash
end
end
end
end
if tn>0 then
return concat(t," & ")
end
end
return "unknown"
end
hashmethods.normal=function(list)
local s={}
local n=0
for k,v in next,list do
if not k then
elseif k=="number" or k=="features" then
else
n=n+1
s[n]=k..'='..tostring(v)
end
end
if n>0 then
sort(s)
return concat(s,"+")
end
end
function constructors.hashinstance(specification,force)
local hash,size,fallbacks=specification.hash,specification.size,specification.fallbacks
if force or not hash then
hash=constructors.hashfeatures(specification)
specification.hash=hash
end
if size<1000 and designsizes[hash] then
size=math.round(constructors.scaled(size,designsizes[hash]))
specification.size=size
end
if fallbacks then
return hash..' @ '..tostring(size)..' @ '..fallbacks
else
return hash..' @ '..tostring(size)
end
end
function constructors.setname(tfmdata,specification)
if constructors.namemode=="specification" then
local specname=specification.specification
if specname then
tfmdata.properties.name=specname
if trace_defining then
report_otf("overloaded fontname %a",specname)
end
end
end
end
function constructors.checkedfilename(data)
local foundfilename=data.foundfilename
if not foundfilename then
local askedfilename=data.filename or ""
if askedfilename~="" then
askedfilename=resolvers.resolve(askedfilename)
foundfilename=resolvers.findbinfile(askedfilename,"") or ""
if foundfilename=="" then
report_defining("source file %a is not found",askedfilename)
foundfilename=resolvers.findbinfile(file.basename(askedfilename),"") or ""
if foundfilename~="" then
report_defining("using source file %a due to cache mismatch",foundfilename)
end
end
end
data.foundfilename=foundfilename
end
return foundfilename
end
local formats=allocate()
fonts.formats=formats
setmetatableindex(formats,function(t,k)
local l=lower(k)
if rawget(t,k) then
t[k]=l
return l
end
return rawget(t,file.suffix(l))
end)
do
local function setindeed(mode,source,target,group,name,position)
local action=source[mode]
if not action then
return
end
local t=target[mode]
if not t then
report_defining("fatal error in setting feature %a, group %a, mode %a",name,group,mode)
os.exit()
elseif position then
insert(t,position,{ name=name,action=action })
else
for i=1,#t do
local ti=t[i]
if ti.name==name then
ti.action=action
return
end
end
insert(t,{ name=name,action=action })
end
end
local function set(group,name,target,source)
target=target[group]
if not target then
report_defining("fatal target error in setting feature %a, group %a",name,group)
os.exit()
end
local source=source[group]
if not source then
report_defining("fatal source error in setting feature %a, group %a",name,group)
os.exit()
end
local position=source.position
setindeed("node",source,target,group,name,position)
setindeed("base",source,target,group,name,position)
setindeed("plug",source,target,group,name,position)
end
local function register(where,specification)
local name=specification.name
if name and name~="" then
local default=specification.default
local description=specification.description
local initializers=specification.initializers
local processors=specification.processors
local manipulators=specification.manipulators
local modechecker=specification.modechecker
if default then
where.defaults[name]=default
end
if description and description~="" then
where.descriptions[name]=description
end
if initializers then
set('initializers',name,where,specification)
end
if processors then
set('processors',name,where,specification)
end
if manipulators then
set('manipulators',name,where,specification)
end
if modechecker then
where.modechecker=modechecker
end
end
end
constructors.registerfeature=register
function constructors.getfeatureaction(what,where,mode,name)
what=handlers[what].features
if what then
where=what[where]
if where then
mode=where[mode]
if mode then
for i=1,#mode do
local m=mode[i]
if m.name==name then
return m.action
end
end
end
end
end
end
local newfeatures={}
constructors.newfeatures=newfeatures
constructors.features=newfeatures
local function setnewfeatures(what)
local handler=handlers[what]
local features=handler.features
if not features then
local tables=handler.tables
local statistics=handler.statistics
features=allocate {
defaults={},
descriptions=tables and tables.features or {},
used=statistics and statistics.usedfeatures or {},
initializers={ base={},node={},plug={} },
processors={ base={},node={},plug={} },
manipulators={ base={},node={},plug={} },
}
features.register=function(specification) return register(features,specification) end
handler.features=features
end
return features
end
setmetatable(newfeatures,{
__call=function(t,k) local v=t[k] return v end,
__index=function(t,k) local v=setnewfeatures(k) t[k]=v return v end,
})
end
do
local newhandler={}
constructors.handlers=newhandler
constructors.newhandler=newhandler
local function setnewhandler(what)
local handler=handlers[what]
if not handler then
handler={}
handlers[what]=handler
end
return handler
end
setmetatable(newhandler,{
__call=function(t,k) local v=t[k] return v end,
__index=function(t,k) local v=setnewhandler(k) t[k]=v return v end,
})
end
do
local newenhancer={}
constructors.enhancers=newenhancer
constructors.newenhancer=newenhancer
local function setnewenhancer(format)
local handler=handlers[format]
local enhancers=handler.enhancers
if not enhancers then
local actions=allocate()
local before=allocate()
local after=allocate()
local order=allocate()
local patches={ before=before,after=after }
local trace=false
local report=logs.reporter("fonts",format.." enhancing")
trackers.register(format..".loading",function(v) trace=v end)
local function enhance(name,data,filename,raw)
local enhancer=actions[name]
if enhancer then
if trace then
report("apply enhancement %a to file %a",name,filename)
ioflush()
end
enhancer(data,filename,raw)
else
end
end
local function apply(data,filename,raw)
local basename=file.basename(lower(filename))
if trace then
report("%s enhancing file %a","start",filename)
end
ioflush()
for e=1,#order do
local enhancer=order[e]
local b=before[enhancer]
if b then
for pattern,action in next,b do
if find(basename,pattern) then
action(data,filename,raw)
end
end
end
enhance(enhancer,data,filename,raw)
local a=after[enhancer]
if a then
for pattern,action in next,a do
if find(basename,pattern) then
action(data,filename,raw)
end
end
end
ioflush()
end
if trace then
report("%s enhancing file %a","stop",filename)
end
ioflush()
end
local function register(what,action)
if action then
if actions[what] then
else
order[#order+1]=what
end
actions[what]=action
else
report("bad enhancer %a",what)
end
end
local function patch(what,where,pattern,action)
local pw=patches[what]
if pw then
local ww=pw[where]
if ww then
ww[pattern]=action
else
pw[where]={ [pattern]=action}
end
end
end
enhancers={
register=register,
apply=apply,
patch=patch,
patches={ register=patch },
}
handler.enhancers=enhancers
end
return enhancers
end
setmetatable(newenhancer,{
__call=function(t,k) local v=t[k] return v end,
__index=function(t,k) local v=setnewenhancer(k) t[k]=v return v end,
})
end
function constructors.checkedfeatures(what,features)
local defaults=handlers[what].features.defaults
if features and next(features) then
features=fastcopy(features)
for key,value in next,defaults do
if features[key]==nil then
features[key]=value
end
end
return features
else
return fastcopy(defaults)
end
end
function constructors.initializefeatures(what,tfmdata,features,trace,report)
if features and next(features) then
local properties=tfmdata.properties or {}
local whathandler=handlers[what]
local whatfeatures=whathandler.features
local whatmodechecker=whatfeatures.modechecker
local mode=properties.mode or (whatmodechecker and whatmodechecker(tfmdata,features,features.mode)) or features.mode or "base"
properties.mode=mode
features.mode=mode
local done={}
while true do
local redo=false
local initializers=whatfeatures.initializers[mode]
if initializers then
for i=1,#initializers do
local step=initializers[i]
local feature=step.name
local value=features[feature]
if not value then
elseif done[feature] then
else
local action=step.action
if trace then
report("initializing feature %a to %a for mode %a for font %a",feature,
value,mode,tfmdata.properties.fullname)
end
action(tfmdata,value,features)
if mode~=properties.mode or mode~=features.mode then
if whatmodechecker then
properties.mode=whatmodechecker(tfmdata,features,properties.mode)
features.mode=properties.mode
end
if mode~=properties.mode then
mode=properties.mode
redo=true
end
end
done[feature]=true
end
if redo then
break
end
end
if not redo then
break
end
else
break
end
end
properties.mode=mode
return true
else
return false
end
end
function constructors.collectprocessors(what,tfmdata,features,trace,report)
local processes,nofprocesses={},0
if features and next(features) then
local properties=tfmdata.properties
local whathandler=handlers[what]
local whatfeatures=whathandler.features
local whatprocessors=whatfeatures.processors
local mode=properties.mode
local processors=whatprocessors[mode]
if processors then
for i=1,#processors do
local step=processors[i]
local feature=step.name
if features[feature] then
local action=step.action
if trace then
report("installing feature processor %a for mode %a for font %a",feature,mode,tfmdata.properties.fullname)
end
if action then
nofprocesses=nofprocesses+1
processes[nofprocesses]=action
end
end
end
elseif trace then
report("no feature processors for mode %a for font %a",mode,properties.fullname)
end
end
return processes
end
function constructors.applymanipulators(what,tfmdata,features,trace,report)
if features and next(features) then
local properties=tfmdata.properties
local whathandler=handlers[what]
local whatfeatures=whathandler.features
local whatmanipulators=whatfeatures.manipulators
local mode=properties.mode
local manipulators=whatmanipulators[mode]
if manipulators then
for i=1,#manipulators do
local step=manipulators[i]
local feature=step.name
local value=features[feature]
if value then
local action=step.action
if trace then
report("applying feature manipulator %a for mode %a for font %a",feature,mode,properties.fullname)
end
if action then
action(tfmdata,feature,value)
end
end
end
end
end
end
function constructors.addcoreunicodes(unicodes)
if not unicodes then
unicodes={}
end
unicodes.space=0x0020
unicodes.hyphen=0x002D
unicodes.zwj=0x200D
unicodes.zwnj=0x200C
return unicodes
end
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-con”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “fonts-enc” a7ace7c1969cd64a5ca9888838f3edb6] ---
if not modules then modules={} end modules ['luatex-font-enc']={
version=1.001,
comment="companion to luatex-*.tex",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
if context then
texio.write_nl("fatal error: this module is not for context")
os.exit()
end
local fonts=fonts
local encodings={}
fonts.encodings=encodings
encodings.agl={}
encodings.known={}
setmetatable(encodings.agl,{ __index=function(t,k)
if k=="unicodes" then
texio.write(" <loading (extended) adobe glyph list>")
local unicodes=dofile(resolvers.findfile("font-age.lua"))
encodings.agl={ unicodes=unicodes }
return unicodes
else
return nil
end
end })
encodings.cache=containers.define("fonts","enc",encodings.version,true)
function encodings.load(filename)
local name=file.removesuffix(filename)
local data=containers.read(encodings.cache,name)
if data then
return data
end
local vector,tag,hash,unicodes={},"",{},{}
local foundname=resolvers.findfile(filename,'enc')
if foundname and foundname~="" then
local ok,encoding,size=resolvers.loadbinfile(foundname)
if ok and encoding then
encoding=string.gsub(encoding,"%%(.-)\n","")
local unicoding=encodings.agl.unicodes
local tag,vec=string.match(encoding,"/(%w+)%s*%[(.*)%]%s*def")
local i=0
for ch in string.gmatch(vec,"/([%a%d%.]+)") do
if ch~=".notdef" then
vector[i]=ch
if not hash[ch] then
hash[ch]=i
else
end
local u=unicoding[ch]
if u then
unicodes[u]=i
end
end
i=i+1
end
end
end
local data={
name=name,
tag=tag,
vector=vector,
hash=hash,
unicodes=unicodes
}
return containers.write(encodings.cache,name,data)
end
end --- [luaotfload, fontloader-2017-02-11.lua scope for “fonts-enc”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-cid” 52421d1fdaa07ec4b1d936c6ff5079be] ---
if not modules then modules={} end modules ['font-cid']={
version=1.001,
comment="companion to font-otf.lua (cidmaps)",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local format,match,lower=string.format,string.match,string.lower
local tonumber=tonumber
local P,S,R,C,V,lpegmatch=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.V,lpeg.match
local fonts,logs,trackers=fonts,logs,trackers
local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end)
local report_otf=logs.reporter("fonts","otf loading")
local cid={}
fonts.cid=cid
local cidmap={}
local cidmax=10
local number=C(R("09","af","AF")^1)
local space=S(" \n\r\t")
local spaces=space^0
local period=P(".")
local periods=period*period
local name=P("/")*C((1-space)^1)
local unicodes,names={},{}
local function do_one(a,b)
unicodes[tonumber(a)]=tonumber(b,16)
end
local function do_range(a,b,c)
c=tonumber(c,16)
for i=tonumber(a),tonumber(b) do
unicodes[i]=c
c=c+1
end
end
local function do_name(a,b)
names[tonumber(a)]=b
end
local grammar=P { "start",
start=number*spaces*number*V("series"),
series=(spaces*(V("one")+V("range")+V("named")))^1,
one=(number*spaces*number)/do_one,
range=(number*periods*number*spaces*number)/do_range,
named=(number*spaces*name)/do_name
}
local function loadcidfile(filename)
local data=io.loaddata(filename)
if data then
unicodes,names={},{}
lpegmatch(grammar,data)
local supplement,registry,ordering=match(filename,"^(.-)%-(.-)%-()%.(.-)$")
return {
supplement=supplement,
registry=registry,
ordering=ordering,
filename=filename,
unicodes=unicodes,
names=names,
}
end
end
cid.loadfile=loadcidfile
local template="%s-%s-%s.cidmap"
local function locate(registry,ordering,supplement)
local filename=format(template,registry,ordering,supplement)
local hashname=lower(filename)
local found=cidmap[hashname]
if not found then
if trace_loading then
report_otf("checking cidmap, registry %a, ordering %a, supplement %a, filename %a",registry,ordering,supplement,filename)
end
local fullname=resolvers.findfile(filename,'cid') or ""
if fullname~="" then
found=loadcidfile(fullname)
if found then
if trace_loading then
report_otf("using cidmap file %a",filename)
end
cidmap[hashname]=found
found.usedname=file.basename(filename)
end
end
end
return found
end
function cid.getmap(specification)
if not specification then
report_otf("invalid cidinfo specification, table expected")
return
end
local registry=specification.registry
local ordering=specification.ordering
local supplement=specification.supplement
local filename=format(registry,ordering,supplement)
local lowername=lower(filename)
local found=cidmap[lowername]
if found then
return found
end
if ordering=="Identity" then
local found={
supplement=supplement,
registry=registry,
ordering=ordering,
filename=filename,
unicodes={},
names={},
}
cidmap[lowername]=found
return found
end
if trace_loading then
report_otf("cidmap needed, registry %a, ordering %a, supplement %a",registry,ordering,supplement)
end
found=locate(registry,ordering,supplement)
if not found then
local supnum=tonumber(supplement)
local cidnum=nil
if supnum<cidmax then
for s=supnum+1,cidmax do
local c=locate(registry,ordering,s)
if c then
found,cidnum=c,s
break
end
end
end
if not found and supnum>0 then
for s=supnum-1,0,-1 do
local c=locate(registry,ordering,s)
if c then
found,cidnum=c,s
break
end
end
end
registry=lower(registry)
ordering=lower(ordering)
if found and cidnum>0 then
for s=0,cidnum-1 do
local filename=format(template,registry,ordering,s)
if not cidmap[filename] then
cidmap[filename]=found
end
end
end
end
return found
end
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-cid”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-map” 8708bde7467785c4d3b7afdaf2f9333a] ---
if not modules then modules={} end modules ['font-map']={
version=1.001,
comment="companion to font-ini.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local tonumber,next,type=tonumber,next,type
local match,format,find,concat,gsub,lower=string.match,string.format,string.find,table.concat,string.gsub,string.lower
local P,R,S,C,Ct,Cc,lpegmatch=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cc,lpeg.match
local floor=math.floor
local formatters=string.formatters
local sortedhash,sortedkeys=table.sortedhash,table.sortedkeys
local trace_loading=false trackers.register("fonts.loading",function(v) trace_loading=v end)
local trace_mapping=false trackers.register("fonts.mapping",function(v) trace_mapping=v end)
local report_fonts=logs.reporter("fonts","loading")
local force_ligatures=false directives.register("fonts.mapping.forceligatures",function(v) force_ligatures=v end)
local fonts=fonts or {}
local mappings=fonts.mappings or {}
fonts.mappings=mappings
local allocate=utilities.storage.allocate
local hex=R("AF","af","09")
local hexfour=(hex*hex*hex^-2)/function(s) return tonumber(s,16) end
local hexsix=(hex*hex*hex^-4)/function(s) return tonumber(s,16) end
local dec=(R("09")^1)/tonumber
local period=P(".")
local unicode=(P("uni")+P("UNI"))*(hexfour*(period+P(-1))*Cc(false)+Ct(hexfour^1)*Cc(true))
local ucode=(P("u")+P("U") )*(hexsix*(period+P(-1))*Cc(false)+Ct(hexsix^1)*Cc(true))
local index=P("index")*dec*Cc(false)
local parser=unicode+ucode+index
local parsers={}
local function makenameparser(str)
if not str or str=="" then
return parser
else
local p=parsers[str]
if not p then
p=P(str)*period*dec*Cc(false)
parsers[str]=p
end
return p
end
end
local f_single=formatters["%04X"]
local f_double=formatters["%04X%04X"]
local function tounicode16(unicode)
if unicode<0xD7FF or (unicode>0xDFFF and unicode<=0xFFFF) then
return f_single(unicode)
else
unicode=unicode-0x10000
return f_double(floor(unicode/1024)+0xD800,unicode%1024+0xDC00)
end
end
local function tounicode16sequence(unicodes)
local t={}
for l=1,#unicodes do
local u=unicodes[l]
if u<0xD7FF or (u>0xDFFF and u<=0xFFFF) then
t[l]=f_single(u)
else
u=u-0x10000
t[l]=f_double(floor(u/1024)+0xD800,u%1024+0xDC00)
end
end
return concat(t)
end
local function tounicode(unicode,name)
if type(unicode)=="table" then
local t={}
for l=1,#unicode do
local u=unicode[l]
if u<0xD7FF or (u>0xDFFF and u<=0xFFFF) then
t[l]=f_single(u)
else
u=u-0x10000
t[l]=f_double(floor(u/1024)+0xD800,u%1024+0xDC00)
end
end
return concat(t)
else
if unicode<0xD7FF or (unicode>0xDFFF and unicode<=0xFFFF) then
return f_single(unicode)
else
unicode=unicode-0x10000
return f_double(floor(unicode/1024)+0xD800,unicode%1024+0xDC00)
end
end
end
local function fromunicode16(str)
if #str==4 then
return tonumber(str,16)
else
local l,r=match(str,"(....)(....)")
return 0x10000+(tonumber(l,16)-0xD800)*0x400+tonumber(r,16)-0xDC00
end
end
mappings.makenameparser=makenameparser
mappings.tounicode=tounicode
mappings.tounicode16=tounicode16
mappings.tounicode16sequence=tounicode16sequence
mappings.fromunicode16=fromunicode16
local ligseparator=P("_")
local varseparator=P(".")
local namesplitter=Ct(C((1-ligseparator-varseparator)^1)*(ligseparator*C((1-ligseparator-varseparator)^1))^0)
do
local overloads=allocate {
IJ={ name="I_J",unicode={ 0x49,0x4A },mess=0x0132 },
ij={ name="i_j",unicode={ 0x69,0x6A },mess=0x0133 },
ff={ name="f_f",unicode={ 0x66,0x66 },mess=0xFB00 },
fi={ name="f_i",unicode={ 0x66,0x69 },mess=0xFB01 },
fl={ name="f_l",unicode={ 0x66,0x6C },mess=0xFB02 },
ffi={ name="f_f_i",unicode={ 0x66,0x66,0x69 },mess=0xFB03 },
ffl={ name="f_f_l",unicode={ 0x66,0x66,0x6C },mess=0xFB04 },
fj={ name="f_j",unicode={ 0x66,0x6A } },
fk={ name="f_k",unicode={ 0x66,0x6B } },
}
local o={}
for k,v in next,overloads do
local name=v.name
local mess=v.mess
if name then
o[name]=v
end
if mess then
o[mess]=v
end
o[k]=v
end
mappings.overloads=o
end
function mappings.addtounicode(data,filename,checklookups)
local resources=data.resources
local unicodes=resources.unicodes
if not unicodes then
if trace_mapping then
report_fonts("no unicode list, quitting tounicode for %a",filename)
end
return
end
local properties=data.properties
local descriptions=data.descriptions
local overloads=mappings.overloads
unicodes['space']=unicodes['space'] or 32
unicodes['hyphen']=unicodes['hyphen'] or 45
unicodes['zwj']=unicodes['zwj'] or 0x200D
unicodes['zwnj']=unicodes['zwnj'] or 0x200C
local private=fonts.constructors and fonts.constructors.privateoffset or 0xF0000
local unicodevector=fonts.encodings.agl.unicodes or {}
local contextvector=fonts.encodings.agl.ctxcodes or {}
local missing={}
local nofmissing=0
local oparser=nil
local cidnames=nil
local cidcodes=nil
local cidinfo=properties.cidinfo
local usedmap=cidinfo and fonts.cid.getmap(cidinfo)
local uparser=makenameparser()
if usedmap then
oparser=usedmap and makenameparser(cidinfo.ordering)
cidnames=usedmap.names
cidcodes=usedmap.unicodes
end
local ns=0
local nl=0
local dlist=sortedkeys(descriptions)
for i=1,#dlist do
local du=dlist[i]
local glyph=descriptions[du]
local name=glyph.name
if name then
local overload=overloads[name] or overloads[du]
if overload then
glyph.unicode=overload.unicode
else
local gu=glyph.unicode
if not gu or gu==-1 or du>=private or (du>=0xE000 and du<=0xF8FF) or du==0xFFFE or du==0xFFFF then
local unicode=unicodevector[name] or contextvector[name]
if unicode then
glyph.unicode=unicode
ns=ns+1
end
if (not unicode) and usedmap then
local foundindex=lpegmatch(oparser,name)
if foundindex then
unicode=cidcodes[foundindex]
if unicode then
glyph.unicode=unicode
ns=ns+1
else
local reference=cidnames[foundindex]
if reference then
local foundindex=lpegmatch(oparser,reference)
if foundindex then
unicode=cidcodes[foundindex]
if unicode then
glyph.unicode=unicode
ns=ns+1
end
end
if not unicode or unicode=="" then
local foundcodes,multiple=lpegmatch(uparser,reference)
if foundcodes then
glyph.unicode=foundcodes
if multiple then
nl=nl+1
unicode=true
else
ns=ns+1
unicode=foundcodes
end
end
end
end
end
end
end
if not unicode or unicode=="" then
local split=lpegmatch(namesplitter,name)
local nsplit=split and #split or 0
if nsplit==0 then
elseif nsplit==1 then
local base=split[1]
local u=unicodes[base] or unicodevector[base] or contextvector[name]
if not u then
elseif type(u)=="table" then
if u[1]<private then
unicode=u
glyph.unicode=unicode
end
elseif u<private then
unicode=u
glyph.unicode=unicode
end
else
local t,n={},0
for l=1,nsplit do
local base=split[l]
local u=unicodes[base] or unicodevector[base] or contextvector[name]
if not u then
break
elseif type(u)=="table" then
if u[1]>=private then
break
end
n=n+1
t[n]=u[1]
else
if u>=private then
break
end
n=n+1
t[n]=u
end
end
if n>0 then
if n==1 then
unicode=t[1]
else
unicode=t
end
glyph.unicode=unicode
end
end
nl=nl+1
end
if not unicode or unicode=="" then
local foundcodes,multiple=lpegmatch(uparser,name)
if foundcodes then
glyph.unicode=foundcodes
if multiple then
nl=nl+1
unicode=true
else
ns=ns+1
unicode=foundcodes
end
end
end
local r=overloads[unicode]
if r then
unicode=r.unicode
glyph.unicode=unicode
end
if not unicode then
missing[du]=true
nofmissing=nofmissing+1
end
end
end
else
local overload=overloads[du]
if overload then
glyph.unicode=overload.unicode
end
end
end
if type(checklookups)=="function" then
checklookups(data,missing,nofmissing)
end
local collected=false
local unicoded=0
for i=1,#dlist do
local du=dlist[i]
local glyph=descriptions[du]
if glyph.class=="ligature" and (force_ligatures or not glyph.unicode) then
if not collected then
collected=fonts.handlers.otf.readers.getcomponents(data)
if not collected then
break
end
end
local u=collected[du]
if u then
local n=#u
for i=1,n do
if u[i]>private then
n=0
break
end
end
if n>0 then
if n>1 then
glyph.unicode=u
else
glyph.unicode=u[1]
end
unicoded=unicoded+1
end
end
end
end
if trace_mapping and unicoded>0 then
report_fonts("%n ligature tounicode mappings deduced from gsub ligature features",unicoded)
end
if trace_mapping then
for i=1,#dlist do
local du=dlist[i]
local glyph=descriptions[du]
local name=glyph.name or "-"
local index=glyph.index or 0
local unicode=glyph.unicode
if unicode then
if type(unicode)=="table" then
local unicodes={}
for i=1,#unicode do
unicodes[i]=formatters("%U",unicode[i])
end
report_fonts("internal slot %U, name %a, unicode %U, tounicode % t",index,name,du,unicodes)
else
report_fonts("internal slot %U, name %a, unicode %U, tounicode %U",index,name,du,unicode)
end
else
report_fonts("internal slot %U, name %a, unicode %U",index,name,du)
end
end
end
if trace_loading and (ns>0 or nl>0) then
report_fonts("%s tounicode entries added, ligatures %s",nl+ns,ns)
end
end
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-map”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-oti” 8f48c06a1d632febd7231ad5dfadfc53] ---
if not modules then modules={} end modules ['font-oti']={
version=1.001,
comment="companion to font-ini.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local lower=string.lower
local fonts=fonts
local constructors=fonts.constructors
local otf=constructors.handlers.otf
local otffeatures=constructors.features.otf
local registerotffeature=otffeatures.register
local otftables=otf.tables or {}
otf.tables=otftables
local allocate=utilities.storage.allocate
registerotffeature {
name="features",
description="initialization of feature handler",
default=true,
}
local function setmode(tfmdata,value)
if value then
tfmdata.properties.mode=lower(value)
end
end
otf.modeinitializer=setmode
local function setlanguage(tfmdata,value)
if value then
local cleanvalue=lower(value)
local languages=otftables and otftables.languages
local properties=tfmdata.properties
if not languages then
properties.language=cleanvalue
elseif languages[value] then
properties.language=cleanvalue
else
properties.language="dflt"
end
end
end
local function setscript(tfmdata,value)
if value then
local cleanvalue=lower(value)
local scripts=otftables and otftables.scripts
local properties=tfmdata.properties
if not scripts then
properties.script=cleanvalue
elseif scripts[value] then
properties.script=cleanvalue
else
properties.script="dflt"
end
end
end
registerotffeature {
name="mode",
description="mode",
initializers={
base=setmode,
node=setmode,
plug=setmode,
}
}
registerotffeature {
name="language",
description="language",
initializers={
base=setlanguage,
node=setlanguage,
plug=setlanguage,
}
}
registerotffeature {
name="script",
description="script",
initializers={
base=setscript,
node=setscript,
plug=setscript,
}
}
otftables.featuretypes=allocate {
gpos_single="position",
gpos_pair="position",
gpos_cursive="position",
gpos_mark2base="position",
gpos_mark2ligature="position",
gpos_mark2mark="position",
gpos_context="position",
gpos_contextchain="position",
gsub_single="substitution",
gsub_multiple="substitution",
gsub_alternate="substitution",
gsub_ligature="substitution",
gsub_context="substitution",
gsub_contextchain="substitution",
gsub_reversecontextchain="substitution",
gsub_reversesub="substitution",
}
function otffeatures.checkeddefaultscript(featuretype,autoscript,scripts)
if featuretype=="position" then
local default=scripts.dflt
if default then
if autoscript=="position" or autoscript==true then
return default
else
report_otf("script feature %s not applied, enable default positioning")
end
else
end
elseif featuretype=="substitution" then
local default=scripts.dflt
if default then
if autoscript=="substitution" or autoscript==true then
return default
end
end
end
end
function otffeatures.checkeddefaultlanguage(featuretype,autolanguage,languages)
if featuretype=="position" then
local default=languages.dflt
if default then
if autolanguage=="position" or autolanguage==true then
return default
else
report_otf("language feature %s not applied, enable default positioning")
end
else
end
elseif featuretype=="substitution" then
local default=languages.dflt
if default then
if autolanguage=="substitution" or autolanguage==true then
return default
end
end
end
end
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-oti”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-otr” 2bd0085b78027f261218d63034f43474] ---
if not modules then modules={} end modules ['font-otr']={
version=1.001,
comment="companion to font-ini.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local next,type,unpack=next,type,unpack
local byte,lower,char,strip,gsub=string.byte,string.lower,string.char,string.strip,string.gsub
local bittest=bit32.btest
local concat,remove,unpack,fastcopy=table.concat,table.remov,table.unpack,table.fastcopy
local floor,abs,sqrt,round=math.floor,math.abs,math.sqrt,math.round
local P,R,S,C,Cs,Cc,Ct,Carg,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Ct,lpeg.Carg,lpeg.Cmt
local lpegmatch=lpeg.match
local setmetatableindex=table.setmetatableindex
local formatters=string.formatters
local sortedkeys=table.sortedkeys
local sortedhash=table.sortedhash
local stripstring=string.strip
local utf16_to_utf8_be=utf.utf16_to_utf8_be
local report=logs.reporter("otf reader")
local trace_cmap=false
fonts=fonts or {}
local handlers=fonts.handlers or {}
fonts.handlers=handlers
local otf=handlers.otf or {}
handlers.otf=otf
local readers=otf.readers or {}
otf.readers=readers
local streamreader=utilities.files
local streamwriter=utilities.files
readers.streamreader=streamreader
readers.streamwriter=streamwriter
local openfile=streamreader.open
local closefile=streamreader.close
local setposition=streamreader.setposition
local skipshort=streamreader.skipshort
local readbytes=streamreader.readbytes
local readstring=streamreader.readstring
local readbyte=streamreader.readcardinal1
local readushort=streamreader.readcardinal2
local readuint=streamreader.readcardinal3
local readulong=streamreader.readcardinal4
local readshort=streamreader.readinteger2
local readlong=streamreader.readinteger4
local readfixed=streamreader.readfixed4
local readfword=readshort
local readufword=readushort
local readoffset=readushort
local read2dot14=streamreader.read2dot14
function streamreader.readtag(f)
return lower(strip(readstring(f,4)))
end
local function readlongdatetime(f)
local a,b,c,d,e,f,g,h=readbytes(f,8)
return 0x100000000*d+0x1000000*e+0x10000*f+0x100*g+h
end
local tableversion=0.004
readers.tableversion=tableversion
local privateoffset=fonts.constructors and fonts.constructors.privateoffset or 0xF0000
local reportedskipped={}
local function reportskippedtable(tag)
if not reportedskipped[tag] then
report("loading of table %a skipped (reported once only)",tag)
reportedskipped[tag]=true
end
end
local reservednames={ [0]="copyright",
"family",
"subfamily",
"uniqueid",
"fullname",
"version",
"postscriptname",
"trademark",
"manufacturer",
"designer",
"description",
"vendorurl",
"designerurl",
"license",
"licenseurl",
"reserved",
"typographicfamily",
"typographicsubfamily",
"compatiblefullname",
"sampletext",
"cidfindfontname",
"wwsfamily",
"wwssubfamily",
"lightbackgroundpalette",
"darkbackgroundpalette",
}
local platforms={ [0]="unicode",
"macintosh",
"iso",
"windows",
"custom",
}
local encodings={
unicode={ [0]="unicode 1.0 semantics",
"unicode 1.1 semantics",
"iso/iec 10646",
"unicode 2.0 bmp",
"unicode 2.0 full",
"unicode variation sequences",
"unicode full repertoire",
},
macintosh={ [0]="roman","japanese","chinese (traditional)","korean","arabic","hebrew","greek","russian",
"rsymbol","devanagari","gurmukhi","gujarati","oriya","bengali","tamil","telugu","kannada",
"malayalam","sinhalese","burmese","khmer","thai","laotian","georgian","armenian",
"chinese (simplified)","tibetan","mongolian","geez","slavic","vietnamese","sindhi",
"uninterpreted",
},
iso={ [0]="7-bit ascii",
"iso 10646",
"iso 8859-1",
},
windows={ [0]="symbol",
"unicode bmp",
"shiftjis",
"prc",
"big5",
"wansung",
"johab",
"reserved 7",
"reserved 8",
"reserved 9",
"unicode ucs-4",
},
custom={
}
}
local decoders={
unicode={},
macintosh={},
iso={},
windows={
["unicode semantics"]=utf16_to_utf8_be,
["unicode bmp"]=utf16_to_utf8_be,
["unicode full"]=utf16_to_utf8_be,
["unicode 1.0 semantics"]=utf16_to_utf8_be,
["unicode 1.1 semantics"]=utf16_to_utf8_be,
["unicode 2.0 bmp"]=utf16_to_utf8_be,
["unicode 2.0 full"]=utf16_to_utf8_be,
["unicode variation sequences"]=utf16_to_utf8_be,
["unicode full repertoire"]=utf16_to_utf8_be,
},
custom={},
}
local languages={
unicode={
[ 0]="english",
},
macintosh={
[ 0]="english",
},
iso={},
windows={
[0x0409]="english - united states",
},
custom={},
}
local standardromanencoding={ [0]=
"notdef",".null","nonmarkingreturn","space","exclam","quotedbl",
"numbersign","dollar","percent","ampersand","quotesingle","parenleft",
"parenright","asterisk","plus","comma","hyphen","period","slash",
"zero","one","two","three","four","five","six","seven","eight",
"nine","colon","semicolon","less","equal","greater","question","at",
"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O",
"P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft",
"backslash","bracketright","asciicircum","underscore","grave","a","b",
"c","d","e","f","g","h","i","j","k","l","m","n","o","p","q",
"r","s","t","u","v","w","x","y","z","braceleft","bar",
"braceright","asciitilde","Adieresis","Aring","Ccedilla","Eacute",
"Ntilde","Odieresis","Udieresis","aacute","agrave","acircumflex",
"adieresis","atilde","aring","ccedilla","eacute","egrave",
"ecircumflex","edieresis","iacute","igrave","icircumflex","idieresis",
"ntilde","oacute","ograve","ocircumflex","odieresis","otilde","uacute",
"ugrave","ucircumflex","udieresis","dagger","degree","cent","sterling",
"section","bullet","paragraph","germandbls","registered","copyright",
"trademark","acute","dieresis","notequal","AE","Oslash","infinity",
"plusminus","lessequal","greaterequal","yen","mu","partialdiff",
"summation","product","pi","integral","ordfeminine","ordmasculine",
"Omega","ae","oslash","questiondown","exclamdown","logicalnot",
"radical","florin","approxequal","Delta","guillemotleft",
"guillemotright","ellipsis","nonbreakingspace","Agrave","Atilde",
"Otilde","OE","oe","endash","emdash","quotedblleft","quotedblright",
"quoteleft","quoteright","divide","lozenge","ydieresis","Ydieresis",
"fraction","currency","guilsinglleft","guilsinglright","fi","fl",
"daggerdbl","periodcentered","quotesinglbase","quotedblbase",
"perthousand","Acircumflex","Ecircumflex","Aacute","Edieresis","Egrave",
"Iacute","Icircumflex","Idieresis","Igrave","Oacute","Ocircumflex",
"apple","Ograve","Uacute","Ucircumflex","Ugrave","dotlessi",
"circumflex","tilde","macron","breve","dotaccent","ring","cedilla",
"hungarumlaut","ogonek","caron","Lslash","lslash","Scaron","scaron",
"Zcaron","zcaron","brokenbar","Eth","eth","Yacute","yacute","Thorn",
"thorn","minus","multiply","onesuperior","twosuperior","threesuperior",
"onehalf","onequarter","threequarters","franc","Gbreve","gbreve",
"Idotaccent","Scedilla","scedilla","Cacute","cacute","Ccaron","ccaron",
"dcroat",
}
local weights={
[100]="thin",
[200]="extralight",
[300]="light",
[400]="normal",
[500]="medium",
[600]="semibold",
[700]="bold",
[800]="extrabold",
[900]="black",
}
local widths={
[1]="ultracondensed",
[2]="extracondensed",
[3]="condensed",
[4]="semicondensed",
[5]="normal",
[6]="semiexpanded",
[7]="expanded",
[8]="extraexpanded",
[9]="ultraexpanded",
}
setmetatableindex(weights,function(t,k)
local r=floor((k+50)/100)*100
local v=(r>900 and "black") or rawget(t,r) or "normal"
return v
end)
setmetatableindex(widths,function(t,k)
return "normal"
end)
local panoseweights={
[ 0]="normal",
[ 1]="normal",
[ 2]="verylight",
[ 3]="light",
[ 4]="thin",
[ 5]="book",
[ 6]="medium",
[ 7]="demi",
[ 8]="bold",
[ 9]="heavy",
[10]="black",
}
local panosewidths={
[ 0]="normal",
[ 1]="normal",
[ 2]="normal",
[ 3]="normal",
[ 4]="normal",
[ 5]="expanded",
[ 6]="condensed",
[ 7]="veryexpanded",
[ 8]="verycondensed",
[ 9]="monospaced",
}
local platformnames={
postscriptname=true,
fullname=true,
family=true,
subfamily=true,
typographicfamily=true,
typographicsubfamily=true,
compatiblefullname=true,
}
function readers.name(f,fontdata,specification)
local datatable=fontdata.tables.name
if datatable then
setposition(f,datatable.offset)
local format=readushort(f)
local nofnames=readushort(f)
local offset=readushort(f)
local start=datatable.offset+offset
local namelists={
unicode={},
windows={},
macintosh={},
}
for i=1,nofnames do
local platform=platforms[readushort(f)]
if platform then
local namelist=namelists[platform]
if namelist then
local encoding=readushort(f)
local language=readushort(f)
local encodings=encodings[platform]
local languages=languages[platform]
if encodings and languages then
local encoding=encodings[encoding]
local language=languages[language]
if encoding and language then
local name=reservednames[readushort(f)]
if name then
namelist[#namelist+1]={
platform=platform,
encoding=encoding,
language=language,
name=name,
length=readushort(f),
offset=start+readushort(f),
}
else
skipshort(f,2)
end
else
skipshort(f,3)
end
else
skipshort(f,3)
end
else
skipshort(f,5)
end
else
skipshort(f,5)
end
end
local names={}
local done={}
local function filter(platform,e,l)
local namelist=namelists[platform]
for i=1,#namelist do
local name=namelist[i]
local nametag=name.name
if not done[nametag] then
local encoding=name.encoding
local language=name.language
if (not e or encoding==e) and (not l or language==l) then
setposition(f,name.offset)
local content=readstring(f,name.length)
local decoder=decoders[platform]
if decoder then
decoder=decoder[encoding]
end
if decoder then
content=decoder(content)
end
names[nametag]={
content=content,
platform=platform,
encoding=encoding,
language=language,
}
done[nametag]=true
end
end
end
end
filter("windows","unicode bmp","english - united states")
filter("macintosh","roman","english")
filter("windows")
filter("macintosh")
filter("unicode")
fontdata.names=names
if specification.platformnames then
local collected={}
for platform,namelist in next,namelists do
local filtered=false
for i=1,#namelist do
local entry=namelist[i]
local name=entry.name
if platformnames[name] then
setposition(f,entry.offset)
local content=readstring(f,entry.length)
local encoding=entry.encoding
local decoder=decoders[platform]
if decoder then
decoder=decoder[encoding]
end
if decoder then
content=decoder(content)
end
if filtered then
filtered[name]=content
else
filtered={ [name]=content }
end
end
end
if filtered then
collected[platform]=filtered
end
end
fontdata.platformnames=collected
end
else
fontdata.names={}
end
end
local validutf=lpeg.patterns.validutf8
local function getname(fontdata,key)
local names=fontdata.names
if names then
local value=names[key]
if value then
local content=value.content
return lpegmatch(validutf,content) and content or nil
end
end
end
readers["os/2"]=function(f,fontdata)
local datatable=fontdata.tables["os/2"]
if datatable then
setposition(f,datatable.offset)
local version=readushort(f)
local windowsmetrics={
version=version,
averagewidth=readshort(f),
weightclass=readushort(f),
widthclass=readushort(f),
fstype=readushort(f),
subscriptxsize=readshort(f),
subscriptysize=readshort(f),
subscriptxoffset=readshort(f),
subscriptyoffset=readshort(f),
superscriptxsize=readshort(f),
superscriptysize=readshort(f),
superscriptxoffset=readshort(f),
superscriptyoffset=readshort(f),
strikeoutsize=readshort(f),
strikeoutpos=readshort(f),
familyclass=readshort(f),
panose={ readbytes(f,10) },
unicoderanges={ readulong(f),readulong(f),readulong(f),readulong(f) },
vendor=readstring(f,4),
fsselection=readushort(f),
firstcharindex=readushort(f),
lastcharindex=readushort(f),
typoascender=readshort(f),
typodescender=readshort(f),
typolinegap=readshort(f),
winascent=readushort(f),
windescent=readushort(f),
}
if version>=1 then
windowsmetrics.codepageranges={ readulong(f),readulong(f) }
end
if version>=3 then
windowsmetrics.xheight=readshort(f)
windowsmetrics.capheight=readshort(f)
windowsmetrics.defaultchar=readushort(f)
windowsmetrics.breakchar=readushort(f)
end
windowsmetrics.weight=windowsmetrics.weightclass and weights[windowsmetrics.weightclass]
windowsmetrics.width=windowsmetrics.widthclass and widths [windowsmetrics.widthclass]
windowsmetrics.panoseweight=panoseweights[windowsmetrics.panose[3]]
windowsmetrics.panosewidth=panosewidths [windowsmetrics.panose[4]]
fontdata.windowsmetrics=windowsmetrics
else
fontdata.windowsmetrics={}
end
end
readers.head=function(f,fontdata)
local datatable=fontdata.tables.head
if datatable then
setposition(f,datatable.offset)
local fontheader={
version=readfixed(f),
revision=readfixed(f),
checksum=readulong(f),
magic=readulong(f),
flags=readushort(f),
units=readushort(f),
created=readlongdatetime(f),
modified=readlongdatetime(f),
xmin=readshort(f),
ymin=readshort(f),
xmax=readshort(f),
ymax=readshort(f),
macstyle=readushort(f),
smallpixels=readushort(f),
directionhint=readshort(f),
indextolocformat=readshort(f),
glyphformat=readshort(f),
}
fontdata.fontheader=fontheader
else
fontdata.fontheader={}
end
fontdata.nofglyphs=0
end
readers.hhea=function(f,fontdata,specification)
if specification.details then
local datatable=fontdata.tables.hhea
if datatable then
setposition(f,datatable.offset)
fontdata.horizontalheader={
version=readfixed(f),
ascender=readfword(f),
descender=readfword(f),
linegap=readfword(f),
maxadvancewidth=readufword(f),
minleftsidebearing=readfword(f),
minrightsidebearing=readfword(f),
maxextent=readfword(f),
caretsloperise=readshort(f),
caretsloperun=readshort(f),
caretoffset=readshort(f),
reserved_1=readshort(f),
reserved_2=readshort(f),
reserved_3=readshort(f),
reserved_4=readshort(f),
metricdataformat=readshort(f),
nofmetrics=readushort(f),
}
else
fontdata.horizontalheader={
nofmetrics=0,
}
end
end
end
readers.vhea=function(f,fontdata,specification)
if specification.details then
local datatable=fontdata.tables.vhea
if datatable then
setposition(f,datatable.offset)
local version=readfixed(f)
fontdata.verticalheader={
version=version,
ascender=readfword(f),
descender=readfword(f),
linegap=readfword(f),
maxadvanceheight=readufword(f),
mintopsidebearing=readfword(f),
minbottomsidebearing=readfword(f),
maxextent=readfword(f),
caretsloperise=readshort(f),
caretsloperun=readshort(f),
caretoffset=readshort(f),
reserved_1=readshort(f),
reserved_2=readshort(f),
reserved_3=readshort(f),
reserved_4=readshort(f),
metricdataformat=readshort(f),
nofmetrics=readushort(f),
}
else
fontdata.verticalheader={
nofmetrics=0,
}
end
end
end
readers.maxp=function(f,fontdata,specification)
if specification.details then
local datatable=fontdata.tables.maxp
if datatable then
setposition(f,datatable.offset)
local version=readfixed(f)
local nofglyphs=readushort(f)
fontdata.nofglyphs=nofglyphs
if version==0.5 then
fontdata.maximumprofile={
version=version,
nofglyphs=nofglyphs,
}
return
elseif version==1.0 then
fontdata.maximumprofile={
version=version,
nofglyphs=nofglyphs,
points=readushort(f),
contours=readushort(f),
compositepoints=readushort(f),
compositecontours=readushort(f),
zones=readushort(f),
twilightpoints=readushort(f),
storage=readushort(f),
functiondefs=readushort(f),
instructiondefs=readushort(f),
stackelements=readushort(f),
sizeofinstructions=readushort(f),
componentelements=readushort(f),
componentdepth=readushort(f),
}
return
end
end
fontdata.maximumprofile={
version=version,
nofglyphs=0,
}
end
end
readers.hmtx=function(f,fontdata,specification)
if specification.glyphs then
local datatable=fontdata.tables.hmtx
if datatable then
setposition(f,datatable.offset)
local horizontalheader=fontdata.horizontalheader
local nofmetrics=horizontalheader.nofmetrics
local glyphs=fontdata.glyphs
local nofglyphs=fontdata.nofglyphs
local width=0
local leftsidebearing=0
for i=0,nofmetrics-1 do
local glyph=glyphs[i]
width=readshort(f)
leftsidebearing=readshort(f)
if width~=0 then
glyph.width=width
end
end
for i=nofmetrics,nofglyphs-1 do
local glyph=glyphs[i]
if width~=0 then
glyph.width=width
end
end
end
end
end
readers.vmtx=function(f,fontdata,specification)
if specification.glyphs then
local datatable=fontdata.tables.vmtx
if datatable then
setposition(f,datatable.offset)
local verticalheader=fontdata.verticalheader
local nofmetrics=verticalheader.nofmetrics
local glyphs=fontdata.glyphs
local nofglyphs=fontdata.nofglyphs
local vheight=0
local vdefault=verticalheader.ascender+verticalheader.descender
local topsidebearing=0
for i=0,nofmetrics-1 do
local glyph=glyphs[i]
vheight=readshort(f)
topsidebearing=readshort(f)
if vheight~=0 and vheight~=vdefault then
glyph.vheight=vheight
end
end
for i=nofmetrics,nofglyphs-1 do
local glyph=glyphs[i]
if vheight~=0 and vheight~=vdefault then
glyph.vheight=vheight
end
end
end
end
end
readers.vorg=function(f,fontdata,specification)
if specification.glyphs then
local datatable=fontdata.tables.vorg
if datatable then
report("todo: %s","vorg")
end
end
end
readers.post=function(f,fontdata,specification)
local datatable=fontdata.tables.post
if datatable then
setposition(f,datatable.offset)
local version=readfixed(f)
fontdata.postscript={
version=version,
italicangle=round(1000*readfixed(f))/1000,
underlineposition=readfword(f),
underlinethickness=readfword(f),
monospaced=readulong(f),
minmemtype42=readulong(f),
maxmemtype42=readulong(f),
minmemtype1=readulong(f),
maxmemtype1=readulong(f),
}
if not specification.glyphs then
elseif version==1.0 then
for index=0,#standardromanencoding do
glyphs[index].name=standardromanencoding[index]
end
elseif version==2.0 then
local glyphs=fontdata.glyphs
local nofglyphs=readushort(f)
local indices={}
local names={}
local maxnames=0
for i=0,nofglyphs-1 do
local nameindex=readushort(f)
if nameindex>=258 then
maxnames=maxnames+1
nameindex=nameindex-257
indices[nameindex]=i
else
glyphs[i].name=standardromanencoding[nameindex]
end
end
for i=1,maxnames do
local mapping=indices[i]
if not mapping then
report("quit post name fetching at %a of %a: %s",i,maxnames,"no index")
break
else
local length=readbyte(f)
if length>0 then
glyphs[mapping].name=readstring(f,length)
else
report("quit post name fetching at %a of %a: %s",i,maxnames,"overflow")
break
end
end
end
elseif version==2.5 then
elseif version==3.0 then
end
else
fontdata.postscript={}
end
end
readers.cff=function(f,fontdata,specification)
if specification.glyphs then
reportskippedtable("cff")
end
end
local formatreaders={}
local duplicatestoo=true
local sequence={
{ 3,1,4 },
{ 3,10,12 },
{ 0,3,4 },
{ 0,1,4 },
{ 0,0,6 },
{ 3,0,6 },
{ 0,5,14 },
{ 3,10,13 },
}
local supported={}
for i=1,#sequence do
local sp,se,sf=unpack(sequence[i])
local p=supported[sp]
if not p then
p={}
supported[sp]=p
end
local e=p[se]
if not e then
e={}
p[se]=e
end
e[sf]=true
end
formatreaders[4]=function(f,fontdata,offset)
setposition(f,offset+2)
local length=readushort(f)
local language=readushort(f)
local nofsegments=readushort(f)/2
skipshort(f,3)
local endchars={}
local startchars={}
local deltas={}
local offsets={}
local indices={}
local mapping=fontdata.mapping
local glyphs=fontdata.glyphs
local duplicates=fontdata.duplicates
local nofdone=0
for i=1,nofsegments do
endchars[i]=readushort(f)
end
local reserved=readushort(f)
for i=1,nofsegments do
startchars[i]=readushort(f)
end
for i=1,nofsegments do
deltas[i]=readshort(f)
end
for i=1,nofsegments do
offsets[i]=readushort(f)
end
local size=(length-2*2-5*2-4*nofsegments*2)/2
for i=1,size-1 do
indices[i]=readushort(f)
end
for segment=1,nofsegments do
local startchar=startchars[segment]
local endchar=endchars[segment]
local offset=offsets[segment]
local delta=deltas[segment]
if startchar==0xFFFF and endchar==0xFFFF then
elseif startchar==0xFFFF and offset==0 then
elseif offset==0xFFFF then
elseif offset==0 then
if trace_cmap then
report("format 4.%i segment %2i from %C upto %C at index %H",1,segment,startchar,endchar,(startchar+delta)%65536)
end
for unicode=startchar,endchar do
local index=(unicode+delta)%65536
if index and index>0 then
local glyph=glyphs[index]
if glyph then
local gu=glyph.unicode
if not gu then
glyph.unicode=unicode
nofdone=nofdone+1
elseif gu~=unicode then
if duplicatestoo then
local d=duplicates[gu]
if d then
d[unicode]=true
else
duplicates[gu]={ [unicode]=true }
end
else
report("duplicate case 1: %C %04i %s",unicode,index,glyphs[index].name)
end
end
if not mapping[index] then
mapping[index]=unicode
end
end
end
end
else
local shift=(segment-nofsegments+offset/2)-startchar
if trace_cmap then
report("format 4.%i segment %2i from %C upto %C at index %H",0,segment,startchar,endchar,(startchar+delta)%65536)
end
for unicode=startchar,endchar do
local slot=shift+unicode
local index=indices[slot]
if index and index>0 then
index=(index+delta)%65536
local glyph=glyphs[index]
if glyph then
local gu=glyph.unicode
if not gu then
glyph.unicode=unicode
nofdone=nofdone+1
elseif gu~=unicode then
if duplicatestoo then
local d=duplicates[gu]
if d then
d[unicode]=true
else
duplicates[gu]={ [unicode]=true }
end
else
report("duplicate case 2: %C %04i %s",unicode,index,glyphs[index].name)
end
end
if not mapping[index] then
mapping[index]=unicode
end
end
end
end
end
end
return nofdone
end
formatreaders[6]=function(f,fontdata,offset)
setposition(f,offset)
local format=readushort(f)
local length=readushort(f)
local language=readushort(f)
local mapping=fontdata.mapping
local glyphs=fontdata.glyphs
local duplicates=fontdata.duplicates
local start=readushort(f)
local count=readushort(f)
local stop=start+count-1
local nofdone=0
if trace_cmap then
report("format 6 from %C to %C",2,start,stop)
end
for unicode=start,stop do
local index=readushort(f)
if index>0 then
local glyph=glyphs[index]
if glyph then
local gu=glyph.unicode
if not gu then
glyph.unicode=unicode
nofdone=nofdone+1
elseif gu~=unicode then
end
if not mapping[index] then
mapping[index]=unicode
end
end
end
end
return nofdone
end
formatreaders[12]=function(f,fontdata,offset)
setposition(f,offset+2+2+4+4)
local mapping=fontdata.mapping
local glyphs=fontdata.glyphs
local duplicates=fontdata.duplicates
local nofgroups=readulong(f)
local nofdone=0
for i=1,nofgroups do
local first=readulong(f)
local last=readulong(f)
local index=readulong(f)
if trace_cmap then
report("format 12 from %C to %C starts at index %i",first,last,index)
end
for unicode=first,last do
local glyph=glyphs[index]
if glyph then
local gu=glyph.unicode
if not gu then
glyph.unicode=unicode
nofdone=nofdone+1
elseif gu~=unicode then
local d=duplicates[gu]
if d then
d[unicode]=true
else
duplicates[gu]={ [unicode]=true }
end
end
if not mapping[index] then
mapping[index]=unicode
end
end
index=index+1
end
end
return nofdone
end
formatreaders[13]=function(f,fontdata,offset)
setposition(f,offset+2+2+4+4)
local mapping=fontdata.mapping
local glyphs=fontdata.glyphs
local duplicates=fontdata.duplicates
local nofgroups=readulong(f)
local nofdone=0
for i=1,nofgroups do
local first=readulong(f)
local last=readulong(f)
local index=readulong(f)
if first<privateoffset then
if trace_cmap then
report("format 13 from %C to %C get index %i",first,last,index)
end
local glyph=glyphs[index]
local unicode=glyph.unicode
if not unicode then
unicode=first
glyph.unicode=unicode
first=first+1
end
local list=duplicates[unicode]
mapping[index]=unicode
if not list then
list={}
duplicates[unicode]=list
end
if last>=privateoffset then
local limit=privateoffset-1
report("format 13 from %C to %C pruned to %C",first,last,limit)
last=limit
end
for unicode=first,last do
list[unicode]=true
end
nofdone=nofdone+last-first+1
else
report("format 13 from %C to %C ignored",first,last)
end
end
return nofdone
end
formatreaders[14]=function(f,fontdata,offset)
if offset and offset~=0 then
setposition(f,offset)
local format=readushort(f)
local length=readulong(f)
local nofrecords=readulong(f)
local records={}
local variants={}
local nofdone=0
fontdata.variants=variants
for i=1,nofrecords do
records[i]={
selector=readuint(f),
default=readulong(f),
other=readulong(f),
}
end
for i=1,nofrecords do
local record=records[i]
local selector=record.selector
local default=record.default
local other=record.other
local other=record.other
if other~=0 then
setposition(f,offset+other)
local mapping={}
local count=readulong(f)
for i=1,count do
mapping[readuint(f)]=readushort(f)
end
nofdone=nofdone+count
variants[selector]=mapping
end
end
return nofdone
else
return 0
end
end
local function checkcmap(f,fontdata,records,platform,encoding,format)
local data=records[platform]
if not data then
return 0
end
data=data[encoding]
if not data then
return 0
end
data=data[format]
if not data then
return 0
end
local reader=formatreaders[format]
if not reader then
return 0
end
local p=platforms[platform]
local e=encodings[p]
local n=reader(f,fontdata,data) or 0
report("cmap checked: platform %i (%s), encoding %i (%s), format %i, new unicodes %i",platform,p,encoding,e and e[encoding] or "?",format,n)
return n
end
function readers.cmap(f,fontdata,specification)
if specification.glyphs then
local datatable=fontdata.tables.cmap
if datatable then
local tableoffset=datatable.offset
setposition(f,tableoffset)
local version=readushort(f)
local noftables=readushort(f)
local records={}
local unicodecid=false
local variantcid=false
local variants={}
local duplicates=fontdata.duplicates or {}
fontdata.duplicates=duplicates
for i=1,noftables do
local platform=readushort(f)
local encoding=readushort(f)
local offset=readulong(f)
local record=records[platform]
if not record then
records[platform]={
[encoding]={
offsets={ offset },
formats={},
}
}
else
local subtables=record[encoding]
if not subtables then
record[encoding]={
offsets={ offset },
formats={},
}
else
local offsets=subtables.offsets
offsets[#offsets+1]=offset
end
end
end
report("found cmaps:")
for platform,record in sortedhash(records) do
local p=platforms[platform]
local e=encodings[p]
local sp=supported[platform]
local ps=p or "?"
if sp then
report(" platform %i: %s",platform,ps)
else
report(" platform %i: %s (unsupported)",platform,ps)
end
for encoding,subtables in sortedhash(record) do
local se=sp and sp[encoding]
local es=e and e[encoding] or "?"
if se then
report(" encoding %i: %s",encoding,es)
else
report(" encoding %i: %s (unsupported)",encoding,es)
end
local offsets=subtables.offsets
local formats=subtables.formats
for i=1,#offsets do
local offset=tableoffset+offsets[i]
setposition(f,offset)
formats[readushort(f)]=offset
end
record[encoding]=formats
local list=sortedkeys(formats)
for i=1,#list do
if not (se and se[list[i]]) then
list[i]=list[i].." (unsupported)"
end
end
report(" formats: % t",list)
end
end
local ok=false
for i=1,#sequence do
local sp,se,sf=unpack(sequence[i])
if checkcmap(f,fontdata,records,sp,se,sf)>0 then
ok=true
end
end
if not ok then
report("no useable unicode cmap found")
end
fontdata.cidmaps={
version=version,
noftables=noftables,
records=records,
}
else
fontdata.cidmaps={}
end
end
end
function readers.loca(f,fontdata,specification)
if specification.glyphs then
reportskippedtable("loca")
end
end
function readers.glyf(f,fontdata,specification)
if specification.glyphs then
reportskippedtable("glyf")
end
end
function readers.colr(f,fontdata,specification)
if specification.glyphs then
reportskippedtable("colr")
end
end
function readers.cpal(f,fontdata,specification)
if specification.glyphs then
reportskippedtable("cpal")
end
end
function readers.svg(f,fontdata,specification)
if specification.glyphs then
reportskippedtable("svg")
end
end
function readers.kern(f,fontdata,specification)
if specification.kerns then
local datatable=fontdata.tables.kern
if datatable then
setposition(f,datatable.offset)
local version=readushort(f)
local noftables=readushort(f)
for i=1,noftables do
local version=readushort(f)
local length=readushort(f)
local coverage=readushort(f)
local format=bit32.rshift(coverage,8)
if format==0 then
local nofpairs=readushort(f)
local searchrange=readushort(f)
local entryselector=readushort(f)
local rangeshift=readushort(f)
local kerns={}
local glyphs=fontdata.glyphs
for i=1,nofpairs do
local left=readushort(f)
local right=readushort(f)
local kern=readfword(f)
local glyph=glyphs[left]
local kerns=glyph.kerns
if kerns then
kerns[right]=kern
else
glyph.kerns={ [right]=kern }
end
end
elseif format==2 then
report("todo: kern classes")
else
report("todo: kerns")
end
end
end
end
end
function readers.gdef(f,fontdata,specification)
if specification.details then
reportskippedtable("gdef")
end
end
function readers.gsub(f,fontdata,specification)
if specification.details then
reportskippedtable("gsub")
end
end
function readers.gpos(f,fontdata,specification)
if specification.details then
reportskippedtable("gpos")
end
end
function readers.math(f,fontdata,specification)
if specification.glyphs then
reportskippedtable("math")
end
end
local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo)
local fontdata=sub and maindata.subfonts and maindata.subfonts[sub] or maindata
local names=fontdata.names
local info=nil
if names then
local metrics=fontdata.windowsmetrics or {}
local postscript=fontdata.postscript or {}
local fontheader=fontdata.fontheader or {}
local cffinfo=fontdata.cffinfo or {}
local filename=fontdata.filename
local weight=getname(fontdata,"weight") or (cffinfo and cffinfo.weight) or (metrics and metrics.weight)
local width=getname(fontdata,"width") or (cffinfo and cffinfo.width ) or (metrics and metrics.width )
local fontname=getname(fontdata,"postscriptname")
local fullname=getname(fontdata,"fullname")
local family=getname(fontdata,"family")
local subfamily=getname(fontdata,"subfamily")
local familyname=getname(fontdata,"typographicfamily")
local subfamilyname=getname(fontdata,"typographicsubfamily")
local compatiblename=getname(fontdata,"compatiblefullname")
if rawfamilynames then
else
if not familyname then familyname=family end
if not subfamilyname then subfamilyname=subfamily end
end
info={
subfontindex=fontdata.subfontindex or sub or 0,
version=getname(fontdata,"version"),
fontname=fontname,
fullname=fullname,
family=family,
subfamily=subfamily,
familyname=familyname,
subfamilyname=subfamilyname,
compatiblename=compatiblename,
weight=weight and lower(weight),
width=width and lower(width),
pfmweight=metrics.weightclass or 400,
pfmwidth=metrics.widthclass or 5,
panosewidth=metrics.panosewidth,
panoseweight=metrics.panoseweight,
italicangle=postscript.italicangle or 0,
units=fontheader.units or 0,
designsize=fontdata.designsize,
minsize=fontdata.minsize,
maxsize=fontdata.maxsize,
monospaced=(tonumber(postscript.monospaced or 0)>0) or metrics.panosewidth=="monospaced",
averagewidth=metrics.averagewidth,
xheight=metrics.xheight,
capheight=metrics.capheight,
ascender=metrics.typoascender,
descender=metrics.typodescender,
platformnames=platformnames and fontdata.platformnames or nil,
}
if metricstoo then
local keys={
"version",
"ascender","descender","linegap",
"maxadvancewidth","maxadvanceheight","maxextent",
"minbottomsidebearing","mintopsidebearing",
}
local h=fontdata.horizontalheader or {}
local v=fontdata.verticalheader or {}
if h then
local th={}
local tv={}
for i=1,#keys do
local key=keys[i]
th[key]=h[key] or 0
tv[key]=v[key] or 0
end
info.horizontalmetrics=th
info.verticalmetrics=tv
end
end
elseif n then
info={
filename=fontdata.filename,
comment="there is no info for subfont "..n,
}
else
info={
filename=fontdata.filename,
comment="there is no info",
}
end
return info
end
local function loadtables(f,specification,offset)
if offset then
setposition(f,offset)
end
local tables={}
local basename=file.basename(specification.filename)
local filesize=specification.filesize
local filetime=specification.filetime
local fontdata={
filename=basename,
filesize=filesize,
filetime=filetime,
version=readstring(f,4),
noftables=readushort(f),
searchrange=readushort(f),
entryselector=readushort(f),
rangeshift=readushort(f),
tables=tables,
}
for i=1,fontdata.noftables do
local tag=lower(stripstring(readstring(f,4)))
local checksum=readulong(f)
local offset=readulong(f)
local length=readulong(f)
if offset+length>filesize then
report("bad %a table in file %a",tag,basename)
end
tables[tag]={
checksum=checksum,
offset=offset,
length=length,
}
end
if tables.cff then
fontdata.format="opentype"
else
fontdata.format="truetype"
end
return fontdata
end
local function prepareglyps(fontdata)
local glyphs=setmetatableindex(function(t,k)
local v={
index=k,
}
t[k]=v
return v
end)
fontdata.glyphs=glyphs
fontdata.mapping={}
end
local function readdata(f,offset,specification)
local fontdata=loadtables(f,specification,offset)
if specification.glyphs then
prepareglyps(fontdata)
end
readers["name"](f,fontdata,specification)
local askedname=specification.askedname
if askedname then
local fullname=getname(fontdata,"fullname") or ""
local cleanname=gsub(askedname,"[^a-zA-Z0-9]","")
local foundname=gsub(fullname,"[^a-zA-Z0-9]","")
if lower(cleanname)~=lower(foundname) then
return
end
end
readers["os/2"](f,fontdata,specification)
readers["head"](f,fontdata,specification)
readers["maxp"](f,fontdata,specification)
readers["hhea"](f,fontdata,specification)
readers["vhea"](f,fontdata,specification)
readers["hmtx"](f,fontdata,specification)
readers["vmtx"](f,fontdata,specification)
readers["vorg"](f,fontdata,specification)
readers["post"](f,fontdata,specification)
readers["cff" ](f,fontdata,specification)
readers["cmap"](f,fontdata,specification)
readers["loca"](f,fontdata,specification)
readers["glyf"](f,fontdata,specification)
readers["colr"](f,fontdata,specification)
readers["cpal"](f,fontdata,specification)
readers["svg" ](f,fontdata,specification)
readers["kern"](f,fontdata,specification)
readers["gdef"](f,fontdata,specification)
readers["gsub"](f,fontdata,specification)
readers["gpos"](f,fontdata,specification)
readers["math"](f,fontdata,specification)
fontdata.locations=nil
fontdata.tables=nil
fontdata.cidmaps=nil
fontdata.dictionaries=nil
return fontdata
end
local function loadfontdata(specification)
local filename=specification.filename
local fileattr=lfs.attributes(filename)
local filesize=fileattr and fileattr.size or 0
local filetime=fileattr and fileattr.modification or 0
local f=openfile(filename,true)
if not f then
report("unable to open %a",filename)
elseif filesize==0 then
report("empty file %a",filename)
closefile(f)
else
specification.filesize=filesize
specification.filetime=filetime
local version=readstring(f,4)
local fontdata=nil
if version=="OTTO" or version=="true" or version=="\0\1\0\0" then
fontdata=readdata(f,0,specification)
elseif version=="ttcf" then
local subfont=tonumber(specification.subfont)
local offsets={}
local ttcversion=readulong(f)
local nofsubfonts=readulong(f)
for i=1,nofsubfonts do
offsets[i]=readulong(f)
end
if subfont then
if subfont>=1 and subfont<=nofsubfonts then
fontdata=readdata(f,offsets[subfont],specification)
else
report("no subfont %a in file %a",subfont,filename)
end
else
subfont=specification.subfont
if type(subfont)=="string" and subfont~="" then
specification.askedname=subfont
for i=1,nofsubfonts do
fontdata=readdata(f,offsets[i],specification)
if fontdata then
fontdata.subfontindex=i
report("subfont named %a has index %a",subfont,i)
break
end
end
if not fontdata then
report("no subfont named %a",subfont)
end
else
local subfonts={}
fontdata={
filename=filename,
filesize=filesize,
filetime=filetime,
version=version,
subfonts=subfonts,
ttcversion=ttcversion,
nofsubfonts=nofsubfonts,
}
for i=1,nofsubfonts do
subfonts[i]=readdata(f,offsets[i],specification)
end
end
end
else
report("unknown version %a in file %a",version,filename)
end
closefile(f)
return fontdata or {}
end
end
local function loadfont(specification,n)
if type(specification)=="string" then
specification={
filename=specification,
info=true,
details=true,
glyphs=true,
shapes=true,
kerns=true,
globalkerns=true,
lookups=true,
subfont=n or true,
tounicode=false,
}
end
if specification.shapes or specification.lookups or specification.kerns then
specification.glyphs=true
end
if specification.glyphs then
specification.details=true
end
if specification.details then
specification.info=true
end
if specification.platformnames then
specification.platformnames=true
end
local function message(str)
report("fatal error in file %a: %s\n%s",specification.filename,str,debug.traceback())
end
local ok,result=xpcall(loadfontdata,message,specification)
if ok then
return result
end
end
function readers.loadshapes(filename,n)
local fontdata=loadfont {
filename=filename,
shapes=true,
subfont=n,
}
if fontdata then
for k,v in next,fontdata.glyphs do
v.class=nil
v.index=nil
v.math=nil
end
end
return fontdata and {
filename=filename,
format=fontdata.format,
glyphs=fontdata.glyphs,
units=fontdata.fontheader.units,
} or {
filename=filename,
format="unknown",
glyphs={},
units=0,
}
end
function readers.loadfont(filename,n)
local fontdata=loadfont {
filename=filename,
glyphs=true,
shapes=false,
lookups=true,
subfont=n,
}
if fontdata then
return {
tableversion=tableversion,
creator="context mkiv",
size=fontdata.filesize,
time=fontdata.filetime,
glyphs=fontdata.glyphs,
descriptions=fontdata.descriptions,
format=fontdata.format,
goodies={},
metadata=getinfo(fontdata,n,false,false,true),
properties={
hasitalics=fontdata.hasitalics or false,
maxcolorclass=fontdata.maxcolorclass,
hascolor=fontdata.hascolor or false,
},
resources={
filename=filename,
private=privateoffset,
duplicates=fontdata.duplicates or {},
features=fontdata.features or {},
sublookups=fontdata.sublookups or {},
marks=fontdata.marks or {},
markclasses=fontdata.markclasses or {},
marksets=fontdata.marksets or {},
sequences=fontdata.sequences or {},
variants=fontdata.variants,
version=getname(fontdata,"version"),
cidinfo=fontdata.cidinfo,
mathconstants=fontdata.mathconstants,
colorpalettes=fontdata.colorpalettes,
svgshapes=fontdata.svgshapes,
},
}
end
end
function readers.getinfo(filename,specification)
local subfont=nil
local platformnames=false
local rawfamilynames=false
if type(specification)=="table" then
subfont=tonumber(specification.subfont)
platformnames=specification.platformnames
rawfamilynames=specification.rawfamilynames
else
subfont=tonumber(specification)
end
local fontdata=loadfont {
filename=filename,
details=true,
platformnames=platformnames,
}
if fontdata then
local subfonts=fontdata.subfonts
if not subfonts then
return getinfo(fontdata,nil,platformnames,rawfamilynames)
elseif not subfont then
local info={}
for i=1,#subfonts do
info[i]=getinfo(fontdata,i,platformnames,rawfamilynames)
end
return info
elseif subfont>=1 and subfont<=#subfonts then
return getinfo(fontdata,subfont,platformnames,rawfamilynames)
else
return {
filename=filename,
comment="there is no subfont "..subfont.." in this file"
}
end
else
return {
filename=filename,
comment="the file cannot be opened for reading",
}
end
end
function readers.rehash(fontdata,hashmethod)
report("the %a helper is not yet implemented","rehash")
end
function readers.checkhash(fontdata)
report("the %a helper is not yet implemented","checkhash")
end
function readers.pack(fontdata,hashmethod)
report("the %a helper is not yet implemented","pack")
end
function readers.unpack(fontdata)
report("the %a helper is not yet implemented","unpack")
end
function readers.expand(fontdata)
report("the %a helper is not yet implemented","unpack")
end
function readers.compact(fontdata)
report("the %a helper is not yet implemented","compact")
end
local extenders={}
function readers.registerextender(extender)
extenders[#extenders+1]=extender
end
function readers.extend(fontdata)
for i=1,#extenders do
local extender=extenders[i]
local name=extender.name or "unknown"
local action=extender.action
if action then
action(fontdata)
end
end
end
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-otr”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-cff” f3fc74e8629f7a2825c34a34550c790d] ---
if not modules then modules={} end modules ['font-cff']={
version=1.001,
comment="companion to font-ini.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local next,type,tonumber=next,type,tonumber
local byte=string.byte
local concat,remove=table.concat,table.remove
local floor,abs,round,ceil=math.floor,math.abs,math.round,math.ceil
local P,C,R,S,C,Cs,Ct=lpeg.P,lpeg.C,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Ct
local lpegmatch=lpeg.match
local formatters=string.formatters
local readers=fonts.handlers.otf.readers
local streamreader=readers.streamreader
local readbytes=streamreader.readbytes
local readstring=streamreader.readstring
local readbyte=streamreader.readcardinal1
local readushort=streamreader.readcardinal2
local readuint=streamreader.readcardinal3
local readulong=streamreader.readcardinal4
local setposition=streamreader.setposition
local getposition=streamreader.getposition
local setmetatableindex=table.setmetatableindex
local trace_charstrings=false trackers.register("fonts.cff.charstrings",function(v) trace_charstrings=v end)
local report=logs.reporter("otf reader","cff")
local parsedictionaries
local parsecharstring
local parsecharstrings
local resetcharstrings
local parseprivates
local defaultstrings={ [0]=
".notdef","space","exclam","quotedbl","numbersign","dollar","percent",
"ampersand","quoteright","parenleft","parenright","asterisk","plus",
"comma","hyphen","period","slash","zero","one","two","three","four",
"five","six","seven","eight","nine","colon","semicolon","less",
"equal","greater","question","at","A","B","C","D","E","F","G","H",
"I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W",
"X","Y","Z","bracketleft","backslash","bracketright","asciicircum",
"underscore","quoteleft","a","b","c","d","e","f","g","h","i","j",
"k","l","m","n","o","p","q","r","s","t","u","v","w","x","y",
"z","braceleft","bar","braceright","asciitilde","exclamdown","cent",
"sterling","fraction","yen","florin","section","currency",
"quotesingle","quotedblleft","guillemotleft","guilsinglleft",
"guilsinglright","fi","fl","endash","dagger","daggerdbl",
"periodcentered","paragraph","bullet","quotesinglbase","quotedblbase",
"quotedblright","guillemotright","ellipsis","perthousand","questiondown",
"grave","acute","circumflex","tilde","macron","breve","dotaccent",
"dieresis","ring","cedilla","hungarumlaut","ogonek","caron","emdash",
"AE","ordfeminine","Lslash","Oslash","OE","ordmasculine","ae",
"dotlessi","lslash","oslash","oe","germandbls","onesuperior",
"logicalnot","mu","trademark","Eth","onehalf","plusminus","Thorn",
"onequarter","divide","brokenbar","degree","thorn","threequarters",
"twosuperior","registered","minus","eth","multiply","threesuperior",
"copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring",
"Atilde","Ccedilla","Eacute","Ecircumflex","Edieresis","Egrave",
"Iacute","Icircumflex","Idieresis","Igrave","Ntilde","Oacute",
"Ocircumflex","Odieresis","Ograve","Otilde","Scaron","Uacute",
"Ucircumflex","Udieresis","Ugrave","Yacute","Ydieresis","Zcaron",
"aacute","acircumflex","adieresis","agrave","aring","atilde",
"ccedilla","eacute","ecircumflex","edieresis","egrave","iacute",
"icircumflex","idieresis","igrave","ntilde","oacute","ocircumflex",
"odieresis","ograve","otilde","scaron","uacute","ucircumflex",
"udieresis","ugrave","yacute","ydieresis","zcaron","exclamsmall",
"Hungarumlautsmall","dollaroldstyle","dollarsuperior","ampersandsmall",
"Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader",
"onedotenleader","zerooldstyle","oneoldstyle","twooldstyle",
"threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle",
"sevenoldstyle","eightoldstyle","nineoldstyle","commasuperior",
"threequartersemdash","periodsuperior","questionsmall","asuperior",
"bsuperior","centsuperior","dsuperior","esuperior","isuperior",
"lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior",
"tsuperior","ff","ffi","ffl","parenleftinferior","parenrightinferior",
"Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall",
"Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall",
"Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall",
"Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall",
"Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah",
"Tildesmall","exclamdownsmall","centoldstyle","Lslashsmall",
"Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall",
"Dotaccentsmall","Macronsmall","figuredash","hypheninferior",
"Ogoneksmall","Ringsmall","Cedillasmall","questiondownsmall","oneeighth",
"threeeighths","fiveeighths","seveneighths","onethird","twothirds",
"zerosuperior","foursuperior","fivesuperior","sixsuperior",
"sevensuperior","eightsuperior","ninesuperior","zeroinferior",
"oneinferior","twoinferior","threeinferior","fourinferior",
"fiveinferior","sixinferior","seveninferior","eightinferior",
"nineinferior","centinferior","dollarinferior","periodinferior",
"commainferior","Agravesmall","Aacutesmall","Acircumflexsmall",
"Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall",
"Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall",
"Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall",
"Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall",
"Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall",
"Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall",
"Thornsmall","Ydieresissmall","001.000","001.001","001.002","001.003",
"Black","Bold","Book","Light","Medium","Regular","Roman","Semibold",
}
local cffreaders={
readbyte,
readushort,
readuint,
readulong,
}
local function readheader(f)
local offset=getposition(f)
local header={
offset=offset,
major=readbyte(f),
minor=readbyte(f),
size=readbyte(f),
osize=readbyte(f),
}
setposition(f,offset+header.size)
return header
end
local function readlengths(f)
local count=readushort(f)
if count==0 then
return {}
end
local osize=readbyte(f)
local read=cffreaders[osize]
if not read then
report("bad offset size: %i",osize)
return {}
end
local lengths={}
local previous=read(f)
for i=1,count do
local offset=read(f)
lengths[i]=offset-previous
previous=offset
end
return lengths
end
local function readfontnames(f)
local names=readlengths(f)
for i=1,#names do
names[i]=readstring(f,names[i])
end
return names
end
local function readtopdictionaries(f)
local dictionaries=readlengths(f)
for i=1,#dictionaries do
dictionaries[i]=readstring(f,dictionaries[i])
end
return dictionaries
end
local function readstrings(f)
local lengths=readlengths(f)
local strings=setmetatableindex({},defaultstrings)
local index=#defaultstrings
for i=1,#lengths do
index=index+1
strings[index]=readstring(f,lengths[i])
end
return strings
end
do
local stack={}
local top=0
local result={}
local strings={}
local p_single=P("\00")/function()
result.version=strings[stack[top]] or "unset"
top=0
end+P("\01")/function()
result.notice=strings[stack[top]] or "unset"
top=0
end+P("\02")/function()
result.fullname=strings[stack[top]] or "unset"
top=0
end+P("\03")/function()
result.familyname=strings[stack[top]] or "unset"
top=0
end+P("\04")/function()
result.weight=strings[stack[top]] or "unset"
top=0
end+P("\05")/function()
result.fontbbox={ unpack(stack,1,4) }
top=0
end
+P("\13")/function()
result.uniqueid=stack[top]
top=0
end+P("\14")/function()
result.xuid=concat(stack,"",1,top)
top=0
end+P("\15")/function()
result.charset=stack[top]
top=0
end+P("\16")/function()
result.encoding=stack[top]
top=0
end+P("\17")/function()
result.charstrings=stack[top]
top=0
end+P("\18")/function()
result.private={
size=stack[top-1],
offset=stack[top],
}
top=0
end+P("\19")/function()
result.subroutines=stack[top]
end+P("\20")/function()
result.defaultwidthx=stack[top]
end+P("\21")/function()
result.nominalwidthx=stack[top]
end
local p_double=P("\12")*(
P("\00")/function()
result.copyright=stack[top]
top=0
end+P("\01")/function()
result.monospaced=stack[top]==1 and true or false
top=0
end+P("\02")/function()
result.italicangle=stack[top]
top=0
end+P("\03")/function()
result.underlineposition=stack[top]
top=0
end+P("\04")/function()
result.underlinethickness=stack[top]
top=0
end+P("\05")/function()
result.painttype=stack[top]
top=0
end+P("\06")/function()
result.charstringtype=stack[top]
top=0
end+P("\07")/function()
result.fontmatrix={ unpack(stack,1,6) }
top=0
end+P("\08")/function()
result.strokewidth=stack[top]
top=0
end+P("\20")/function()
result.syntheticbase=stack[top]
top=0
end+P("\21")/function()
result.postscript=strings[stack[top]] or "unset"
top=0
end+P("\22")/function()
result.basefontname=strings[stack[top]] or "unset"
top=0
end+P("\21")/function()
result.basefontblend=stack[top]
top=0
end+P("\30")/function()
result.cid.registry=strings[stack[top-2]] or "unset"
result.cid.ordering=strings[stack[top-1]] or "unset"
result.cid.supplement=stack[top]
top=0
end+P("\31")/function()
result.cid.fontversion=stack[top]
top=0
end+P("\32")/function()
result.cid.fontrevision=stack[top]
top=0
end+P("\33")/function()
result.cid.fonttype=stack[top]
top=0
end+P("\34")/function()
result.cid.count=stack[top]
top=0
end+P("\35")/function()
result.cid.uidbase=stack[top]
top=0
end+P("\36")/function()
result.cid.fdarray=stack[top]
top=0
end+P("\37")/function()
result.cid.fdselect=stack[top]
top=0
end+P("\38")/function()
result.cid.fontname=strings[stack[top]] or "unset"
top=0
end
)
local p_last=P("\x0F")/"0"+P("\x1F")/"1"+P("\x2F")/"2"+P("\x3F")/"3"+P("\x4F")/"4"+P("\x5F")/"5"+P("\x6F")/"6"+P("\x7F")/"7"+P("\x8F")/"8"+P("\x9F")/"9"+P("\xAF")/""+P("\xBF")/""+P("\xCF")/""+P("\xDF")/""+P("\xEF")/""+R("\xF0\xFF")/""
local remap={
["\x00"]="00",["\x01"]="01",["\x02"]="02",["\x03"]="03",["\x04"]="04",["\x05"]="05",["\x06"]="06",["\x07"]="07",["\x08"]="08",["\x09"]="09",["\x0A"]="0.",["\x0B"]="0E",["\x0C"]="0E-",["\x0D"]="0",["\x0E"]="0-",["\x0F"]="0",
["\x10"]="10",["\x11"]="11",["\x12"]="12",["\x13"]="13",["\x14"]="14",["\x15"]="15",["\x16"]="16",["\x17"]="17",["\x18"]="18",["\x19"]="19",["\x1A"]="0.",["\x1B"]="0E",["\x1C"]="0E-",["\x1D"]="0",["\x1E"]="0-",["\x1F"]="0",
["\x20"]="20",["\x21"]="21",["\x22"]="22",["\x23"]="23",["\x24"]="24",["\x25"]="25",["\x26"]="26",["\x27"]="27",["\x28"]="28",["\x29"]="29",["\x2A"]="0.",["\x2B"]="0E",["\x2C"]="0E-",["\x2D"]="0",["\x2E"]="0-",["\x2F"]="0",
["\x30"]="30",["\x31"]="31",["\x32"]="32",["\x33"]="33",["\x34"]="34",["\x35"]="35",["\x36"]="36",["\x37"]="37",["\x38"]="38",["\x39"]="39",["\x3A"]="0.",["\x3B"]="0E",["\x3C"]="0E-",["\x3D"]="0",["\x3E"]="0-",["\x3F"]="0",
["\x40"]="40",["\x41"]="41",["\x42"]="42",["\x43"]="43",["\x44"]="44",["\x45"]="45",["\x46"]="46",["\x47"]="47",["\x48"]="48",["\x49"]="49",["\x4A"]="0.",["\x4B"]="0E",["\x4C"]="0E-",["\x4D"]="0",["\x4E"]="0-",["\x4F"]="0",
["\x50"]="50",["\x51"]="51",["\x52"]="52",["\x53"]="53",["\x54"]="54",["\x55"]="55",["\x56"]="56",["\x57"]="57",["\x58"]="58",["\x59"]="59",["\x5A"]="0.",["\x5B"]="0E",["\x5C"]="0E-",["\x5D"]="0",["\x5E"]="0-",["\x5F"]="0",
["\x60"]="60",["\x61"]="61",["\x62"]="62",["\x63"]="63",["\x64"]="64",["\x65"]="65",["\x66"]="66",["\x67"]="67",["\x68"]="68",["\x69"]="69",["\x6A"]="0.",["\x6B"]="0E",["\x6C"]="0E-",["\x6D"]="0",["\x6E"]="0-",["\x6F"]="0",
["\x70"]="70",["\x71"]="71",["\x72"]="72",["\x73"]="73",["\x74"]="74",["\x75"]="75",["\x76"]="76",["\x77"]="77",["\x78"]="78",["\x79"]="79",["\x7A"]="0.",["\x7B"]="0E",["\x7C"]="0E-",["\x7D"]="0",["\x7E"]="0-",["\x7F"]="0",
["\x80"]="80",["\x81"]="81",["\x82"]="82",["\x83"]="83",["\x84"]="84",["\x85"]="85",["\x86"]="86",["\x87"]="87",["\x88"]="88",["\x89"]="89",["\x8A"]="0.",["\x8B"]="0E",["\x8C"]="0E-",["\x8D"]="0",["\x8E"]="0-",["\x8F"]="0",
["\x90"]="90",["\x91"]="91",["\x92"]="92",["\x93"]="93",["\x94"]="94",["\x95"]="95",["\x96"]="96",["\x97"]="97",["\x98"]="98",["\x99"]="99",["\x9A"]="0.",["\x9B"]="0E",["\x9C"]="0E-",["\x9D"]="0",["\x9E"]="0-",["\x9F"]="0",
["\xA0"]=".0",["\xA1"]=".1",["\xA2"]=".2",["\xA3"]=".3",["\xA4"]=".4",["\xA5"]=".5",["\xA6"]=".6",["\xA7"]=".7",["\xA8"]=".8",["\xA9"]=".9",["\xAA"]="..",["\xAB"]=".E",["\xAC"]=".E-",["\xAD"]=".",["\xAE"]=".-",["\xAF"]=".",
["\xB0"]="E0",["\xB1"]="E1",["\xB2"]="E2",["\xB3"]="E3",["\xB4"]="E4",["\xB5"]="E5",["\xB6"]="E6",["\xB7"]="E7",["\xB8"]="E8",["\xB9"]="E9",["\xBA"]="E.",["\xBB"]="EE",["\xBC"]="EE-",["\xBD"]="E",["\xBE"]="E-",["\xBF"]="E",
["\xC0"]="E-0",["\xC1"]="E-1",["\xC2"]="E-2",["\xC3"]="E-3",["\xC4"]="E-4",["\xC5"]="E-5",["\xC6"]="E-6",["\xC7"]="E-7",["\xC8"]="E-8",["\xC9"]="E-9",["\xCA"]="E-.",["\xCB"]="E-E",["\xCC"]="E-E-",["\xCD"]="E-",["\xCE"]="E--",["\xCF"]="E-",
["\xD0"]="-0",["\xD1"]="-1",["\xD2"]="-2",["\xD3"]="-3",["\xD4"]="-4",["\xD5"]="-5",["\xD6"]="-6",["\xD7"]="-7",["\xD8"]="-8",["\xD9"]="-9",["\xDA"]="-.",["\xDB"]="-E",["\xDC"]="-E-",["\xDD"]="-",["\xDE"]="--",["\xDF"]="-",
}
local p_nibbles=P("\30")*Cs(((1-p_last)/remap)^0+p_last)/function(n)
top=top+1
stack[top]=tonumber(n) or 0
end
local p_byte=C(R("\32\246"))/function(b0)
top=top+1
stack[top]=byte(b0)-139
end
local p_positive=C(R("\247\250"))*C(1)/function(b0,b1)
top=top+1
stack[top]=(byte(b0)-247)*256+byte(b1)+108
end
local p_negative=C(R("\251\254"))*C(1)/function(b0,b1)
top=top+1
stack[top]=-(byte(b0)-251)*256-byte(b1)-108
end
local p_short=P("\28")*C(1)*C(1)/function(b1,b2)
top=top+1
local n=0x100*byte(b1)+byte(b2)
if n>=0x8000 then
stack[top]=n-0xFFFF-1
else
stack[top]=n
end
end
local p_long=P("\29")*C(1)*C(1)*C(1)*C(1)/function(b1,b2,b3,b4)
top=top+1
local n=0x1000000*byte(b1)+0x10000*byte(b2)+0x100*byte(b3)+byte(b4)
if n>=0x8000000 then
stack[top]=n-0xFFFFFFFF-1
else
stack[top]=n
end
end
local p_unsupported=P(1)/function(detail)
top=0
end
local p_dictionary=(
p_byte+p_positive+p_negative+p_short+p_long+p_nibbles+p_single+p_double+p_unsupported
)^1
parsedictionaries=function(data,dictionaries)
stack={}
strings=data.strings
for i=1,#dictionaries do
top=0
result={
monospaced=false,
italicangle=0,
underlineposition=-100,
underlinethickness=50,
painttype=0,
charstringtype=2,
fontmatrix={ 0.001,0,0,0.001,0,0 },
fontbbox={ 0,0,0,0 },
strokewidth=0,
charset=0,
encoding=0,
cid={
fontversion=0,
fontrevision=0,
fonttype=0,
count=8720,
}
}
lpegmatch(p_dictionary,dictionaries[i])
dictionaries[i]=result
end
result={}
top=0
stack={}
end
parseprivates=function(data,dictionaries)
stack={}
strings=data.strings
for i=1,#dictionaries do
local private=dictionaries[i].private
if private and private.data then
top=0
result={
forcebold=false,
languagegroup=0,
expansionfactor=0.06,
initialrandomseed=0,
subroutines=0,
defaultwidthx=0,
nominalwidthx=0,
cid={
},
}
lpegmatch(p_dictionary,private.data)
private.data=result
end
end
result={}
top=0
stack={}
end
local x=0
local y=0
local width=false
local r=0
local stems=0
local globalbias=0
local localbias=0
local globals=false
local locals=false
local depth=1
local xmin=0
local xmax=0
local ymin=0
local ymax=0
local checked=false
local keepcurve=false
local version=2
local function showstate(where)
report("%w%-10s : [%s] n=%i",depth*2,where,concat(stack," ",1,top),top)
end
local function showvalue(where,value,showstack)
if showstack then
report("%w%-10s : %s : [%s] n=%i",depth*2,where,tostring(value),concat(stack," ",1,top),top)
else
report("%w%-10s : %s",depth*2,where,tostring(value))
end
end
local function moveto(x,y)
if keepcurve then
r=r+1
result[r]={ x,y,"m" }
end
if checked then
if x<xmin then xmin=x elseif x>xmax then xmax=x end
if y<ymin then ymin=y elseif y>ymax then ymax=y end
else
xmin=x
ymin=y
xmax=x
ymax=y
checked=true
end
end
local function lineto(x,y)
if keepcurve then
r=r+1
result[r]={ x,y,"l" }
end
if checked then
if x<xmin then xmin=x elseif x>xmax then xmax=x end
if y<ymin then ymin=y elseif y>ymax then ymax=y end
else
xmin=x
ymin=y
xmax=x
ymax=y
checked=true
end
end
local function curveto(x1,y1,x2,y2,x3,y3)
if keepcurve then
r=r+1
result[r]={ x1,y1,x2,y2,x3,y3,"c" }
end
if checked then
if x1<xmin then xmin=x1 elseif x1>xmax then xmax=x1 end
if y1<ymin then ymin=y1 elseif y1>ymax then ymax=y1 end
else
xmin=x1
ymin=y1
xmax=x1
ymax=y1
checked=true
end
if x2<xmin then xmin=x2 elseif x2>xmax then xmax=x2 end
if y2<ymin then ymin=y2 elseif y2>ymax then ymax=y2 end
if x3<xmin then xmin=x3 elseif x3>xmax then xmax=x3 end
if y3<ymin then ymin=y3 elseif y3>ymax then ymax=y3 end
end
local function rmoveto()
if top>2 then
if not width then
width=stack[1]
if trace_charstrings then
showvalue("width",width)
end
end
elseif not width then
width=true
end
if trace_charstrings then
showstate("rmoveto")
end
x=x+stack[top-1]
y=y+stack[top]
top=0
moveto(x,y)
end
local function hmoveto()
if top>1 then
if not width then
width=stack[1]
if trace_charstrings then
showvalue("width",width)
end
end
elseif not width then
width=true
end
if trace_charstrings then
showstate("hmoveto")
end
x=x+stack[top]
top=0
moveto(x,y)
end
local function vmoveto()
if top>1 then
if not width then
width=stack[1]
if trace_charstrings then
showvalue("width",width)
end
end
elseif not width then
width=true
end
if trace_charstrings then
showstate("vmoveto")
end
y=y+stack[top]
top=0
moveto(x,y)
end
local function rlineto()
if trace_charstrings then
showstate("rlineto")
end
for i=1,top,2 do
x=x+stack[i]
y=y+stack[i+1]
lineto(x,y)
end
top=0
end
local function xlineto(swap)
for i=1,top do
if swap then
x=x+stack[i]
swap=false
else
y=y+stack[i]
swap=true
end
lineto(x,y)
end
top=0
end
local function hlineto()
if trace_charstrings then
showstate("hlineto")
end
xlineto(true)
end
local function vlineto()
if trace_charstrings then
showstate("vlineto")
end
xlineto(false)
end
local function rrcurveto()
if trace_charstrings then
showstate("rrcurveto")
end
for i=1,top,6 do
local ax=x+stack[i]
local ay=y+stack[i+1]
local bx=ax+stack[i+2]
local by=ay+stack[i+3]
x=bx+stack[i+4]
y=by+stack[i+5]
curveto(ax,ay,bx,by,x,y)
end
top=0
end
local function hhcurveto()
if trace_charstrings then
showstate("hhcurveto")
end
local s=1
if top%2~=0 then
y=y+stack[1]
s=2
end
for i=s,top,4 do
local ax=x+stack[i]
local ay=y
local bx=ax+stack[i+1]
local by=ay+stack[i+2]
x=bx+stack[i+3]
y=by
curveto(ax,ay,bx,by,x,y)
end
top=0
end
local function vvcurveto()
if trace_charstrings then
showstate("vvcurveto")
end
local s=1
local d=0
if top%2~=0 then
d=stack[1]
s=2
end
for i=s,top,4 do
local ax=x+d
local ay=y+stack[i]
local bx=ax+stack[i+1]
local by=ay+stack[i+2]
x=bx
y=by+stack[i+3]
curveto(ax,ay,bx,by,x,y)
d=0
end
top=0
end
local function xxcurveto(swap)
local last=top%4~=0 and stack[top]
if last then
top=top-1
end
local sw=swap
for i=1,top,4 do
local ax,ay,bx,by
if swap then
ax=x+stack[i]
ay=y
bx=ax+stack[i+1]
by=ay+stack[i+2]
y=by+stack[i+3]
if last and i+3==top then
x=bx+last
else
x=bx
end
swap=false
else
ax=x
ay=y+stack[i]
bx=ax+stack[i+1]
by=ay+stack[i+2]
x=bx+stack[i+3]
if last and i+3==top then
y=by+last
else
y=by
end
swap=true
end
curveto(ax,ay,bx,by,x,y)
end
top=0
end
local function hvcurveto()
if trace_charstrings then
showstate("hvcurveto")
end
xxcurveto(true)
end
local function vhcurveto()
if trace_charstrings then
showstate("vhcurveto")
end
xxcurveto(false)
end
local function rcurveline()
if trace_charstrings then
showstate("rcurveline")
end
for i=1,top-2,6 do
local ax=x+stack[i]
local ay=y+stack[i+1]
local bx=ax+stack[i+2]
local by=ay+stack[i+3]
x=bx+stack[i+4]
y=by+stack[i+5]
curveto(ax,ay,bx,by,x,y)
end
x=x+stack[top-1]
y=y+stack[top]
lineto(x,y)
top=0
end
local function rlinecurve()
if trace_charstrings then
showstate("rlinecurve")
end
if top>6 then
for i=1,top-6,2 do
x=x+stack[i]
y=y+stack[i+1]
lineto(x,y)
end
end
local ax=x+stack[top-5]
local ay=y+stack[top-4]
local bx=ax+stack[top-3]
local by=ay+stack[top-2]
x=bx+stack[top-1]
y=by+stack[top]
curveto(ax,ay,bx,by,x,y)
top=0
end
local function flex()
if trace_charstrings then
showstate("flex")
end
local ax=x+stack[1]
local ay=y+stack[2]
local bx=ax+stack[3]
local by=ay+stack[4]
local cx=bx+stack[5]
local cy=by+stack[6]
curveto(ax,ay,bx,by,cx,cy)
local dx=cx+stack[7]
local dy=cy+stack[8]
local ex=dx+stack[9]
local ey=dy+stack[10]
x=ex+stack[11]
y=ey+stack[12]
curveto(dx,dy,ex,ey,x,y)
top=0
end
local function hflex()
if trace_charstrings then
showstate("hflex")
end
local ax=x+stack[1]
local ay=y
local bx=ax+stack[2]
local by=ay+stack[3]
local cx=bx+stack[4]
local cy=by
curveto(ax,ay,bx,by,cx,cy)
local dx=cx+stack[5]
local dy=by
local ex=dx+stack[6]
local ey=y
x=ex+stack[7]
curveto(dx,dy,ex,ey,x,y)
top=0
end
local function hflex1()
if trace_charstrings then
showstate("hflex1")
end
local ax=x+stack[1]
local ay=y+stack[2]
local bx=ax+stack[3]
local by=ay+stack[4]
local cx=bx+stack[5]
local cy=by
curveto(ax,ay,bx,by,cx,cy)
local dx=cx+stack[6]
local dy=by
local ex=dx+stack[7]
local ey=dy+stack[8]
x=ex+stack[9]
curveto(dx,dy,ex,ey,x,y)
top=0
end
local function flex1()
if trace_charstrings then
showstate("flex1")
end
local ax=x+stack[1]
local ay=y+stack[2]
local bx=ax+stack[3]
local by=ay+stack[4]
local cx=bx+stack[5]
local cy=by+stack[6]
curveto(ax,ay,bx,by,cx,cy)
local dx=cx+stack[7]
local dy=cy+stack[8]
local ex=dx+stack[9]
local ey=dy+stack[10]
if abs(ex-x)>abs(ey-y) then
x=ex+stack[11]
else
y=ey+stack[11]
end
curveto(dx,dy,ex,ey,x,y)
top=0
end
local function getstem()
if top==0 then
elseif top%2~=0 then
if width then
remove(stack,1)
else
width=remove(stack,1)
if trace_charstrings then
showvalue("width",width)
end
end
top=top-1
end
if trace_charstrings then
showstate("stem")
end
stems=stems+top/2
top=0
end
local function getmask()
if top==0 then
elseif top%2~=0 then
if width then
remove(stack,1)
else
width=remove(stack,1)
if trace_charstrings then
showvalue("width",width)
end
end
top=top-1
end
if trace_charstrings then
showstate(operator==19 and "hintmark" or "cntrmask")
end
stems=stems+top/2
top=0
if stems==0 then
elseif stems<=8 then
return 1
else
return floor((stems+7)/8)
end
end
local function unsupported(t)
if trace_charstrings then
showstate("unsupported "..t)
end
top=0
end
local function unsupportedsub(t)
if trace_charstrings then
showstate("unsupported sub "..t)
end
top=0
end
local function getstem3()
if trace_charstrings then
showstate("stem3")
end
top=0
end
local function divide()
if version==1 then
local d=stack[top]
top=top-1
stack[top]=stack[top]/d
end
end
local function closepath()
if version==1 then
if trace_charstrings then
showstate("closepath")
end
end
top=0
end
local function hsbw()
if version==1 then
if trace_charstrings then
showstate("dotsection")
end
width=stack[top]
end
top=0
end
local function seac()
if version==1 then
if trace_charstrings then
showstate("seac")
end
end
top=0
end
local function sbw()
if version==1 then
if trace_charstrings then
showstate("sbw")
end
width=stack[top-1]
end
top=0
end
local function callothersubr()
if version==1 then
if trace_charstrings then
showstate("callothersubr (unsupported)")
end
end
top=0
end
local function pop()
if version==1 then
if trace_charstrings then
showstate("pop (unsupported)")
end
top=top+1
stack[top]=0
else
top=0
end
end
local function setcurrentpoint()
if version==1 then
if trace_charstrings then
showstate("pop (unsupported)")
end
x=x+stack[top-1]
y=y+stack[top]
end
top=0
end
local actions={ [0]=unsupported,
getstem,
unsupported,
getstem,
vmoveto,
rlineto,
hlineto,
vlineto,
rrcurveto,
unsupported,
unsupported,
unsupported,
unsupported,
hsbw,
unsupported,
unsupported,
unsupported,
unsupported,
getstem,
getmask,
getmask,
rmoveto,
hmoveto,
getstem,
rcurveline,
rlinecurve,
vvcurveto,
hhcurveto,
unsupported,
unsupported,
vhcurveto,
hvcurveto,
}
local subactions={
[000]=dotsection,
[001]=getstem3,
[002]=getstem3,
[006]=seac,
[007]=sbw,
[012]=divide,
[016]=callothersubr,
[017]=pop,
[033]=setcurrentpoint,
[034]=hflex,
[035]=flex,
[036]=hflex1,
[037]=flex1,
}
local p_bytes=Ct((P(1)/byte)^0)
local function call(scope,list,bias,process)
depth=depth+1
if top==0 then
showstate(formatters["unknown %s call"](scope))
top=0
else
local index=stack[top]+bias
top=top-1
if trace_charstrings then
showvalue(scope,index,true)
end
local tab=list[index]
if tab then
if type(tab)=="string" then
tab=lpegmatch(p_bytes,tab)
list[index]=tab
end
process(tab)
else
showstate(formatters["unknown %s call %i"](scope,index))
top=0
end
end
depth=depth-1
end
local function process(tab)
local i=1
local n=#tab
while i<=n do
local t=tab[i]
if t>=32 and t<=246 then
top=top+1
stack[top]=t-139
i=i+1
elseif t>=247 and t<=250 then
top=top+1
stack[top]=(t-247)*256+tab[i+1]+108
i=i+2
elseif t>=251 and t<=254 then
top=top+1
stack[top]=-(t-251)*256-tab[i+1]-108
i=i+2
elseif t==28 then
top=top+1
local n=0x100*tab[i+1]+tab[i+2]
if n>=0x8000 then
stack[top]=n-0xFFFF-1
else
stack[top]=n
end
i=i+3
elseif t==255 then
local n=0x100*tab[i+1]+tab[i+2]
top=top+1
if n>=0x8000 then
stack[top]=n-0xFFFF-1+(0x100*tab[i+3]+tab[i+4])/0xFFFF
else
stack[top]=n+(0x100*tab[i+3]+tab[i+4])/0xFFFF
end
i=i+5
elseif t==11 then
if trace_charstrings then
showstate("return")
end
return
elseif t==10 then
call("local",locals,localbias,process)
i=i+1
elseif t==14 then
if width then
elseif top>0 then
width=stack[1]
if trace_charstrings then
showvalue("width",width)
end
else
width=true
end
if trace_charstrings then
showstate("endchar")
end
return
elseif t==29 then
call("global",globals,globalbias,process)
i=i+1
elseif t==12 then
i=i+1
local t=tab[i]
local a=subactions[t]
if a then
a(t)
else
if trace_charstrings then
showvalue("<subaction>",t)
end
top=0
end
i=i+1
else
local a=actions[t]
if a then
local s=a(t)
if s then
i=i+s
end
else
if trace_charstrings then
showvalue("<action>",t)
end
top=0
end
i=i+1
end
end
end
local function setbias(globals,locals)
if version==1 then
return
false,
false
else
local g,l=#globals,#locals
return
((g<1240 and 107) or (g<33900 and 1131) or 32768)+1,
((l<1240 and 107) or (l<33900 and 1131) or 32768)+1
end
end
parsecharstrings=function(data,glyphs,doshapes,tversion)
local dictionary=data.dictionaries[1]
local charstrings=dictionary.charstrings
local charset=dictionary.charset
local private=dictionary.private or { data={} }
keepcurve=doshapes
version=tversion
stack={}
glyphs=glyphs or {}
strings=data.strings
globals=data.routines or {}
locals=dictionary.subroutines or {}
globalbias,localbias=setbias(globals,locals)
local nominalwidth=private.data.nominalwidthx or 0
local defaultwidth=private.data.defaultwidthx or 0
for i=1,#charstrings do
local tab=charstrings[i]
if type(tab)=="string" then
tab=lpegmatch(p_bytes,tab)
end
local index=i-1
x=0
y=0
width=false
r=0
top=0
stems=0
result={}
xmin=0
xmax=0
ymin=0
ymax=0
checked=false
if trace_charstrings then
report("glyph: %i",index)
report("data: % t",tab)
end
process(tab)
local boundingbox={ round(xmin),round(ymin),round(xmax),round(ymax) }
if width==true or width==false then
width=defaultwidth
else
width=nominalwidth+width
end
local glyph=glyphs[index]
if not glyph then
glyphs[index]={
segments=doshapes~=false and result or nil,
boundingbox=boundingbox,
width=width,
name=charset[index],
}
else
glyph.segments=doshapes~=false and result or nil
glyph.boundingbox=boundingbox
if not glyph.width then
glyph.width=width
end
if charset and not glyph.name then
glyph.name=charset[index]
end
end
if trace_charstrings then
report("width: %s",tostring(width))
report("boundingbox: % t",boundingbox)
end
charstrings[i]=nil
end
return glyphs
end
parsecharstring=function(data,dictionary,tab,glyphs,index,doshapes,tversion)
local private=dictionary.private
keepcurve=doshapes
version=tversion
strings=data.strings
locals=dictionary.subroutines or {}
globals=data.routines or {}
globalbias,localbias=setbias(globals,locals)
local nominalwidth=private and private.data.nominalwidthx or 0
local defaultwidth=private and private.data.defaultwidthx or 0
if type(tab)=="string" then
tab=lpegmatch(p_bytes,tab)
end
x=0
y=0
width=false
r=0
top=0
stems=0
result={}
xmin=0
xmax=0
ymin=0
ymax=0
checked=false
if trace_charstrings then
report("glyph: %i",index)
report("data: % t",tab)
end
process(tab)
local boundingbox={ xmin,ymin,xmax,ymax }
if width==true or width==false then
width=defaultwidth
else
width=nominalwidth+width
end
index=index-1
local glyph=glyphs[index]
if not glyph then
glyphs[index]={
segments=doshapes~=false and result or nil,
boundingbox=boundingbox,
width=width,
name=charset[index],
}
else
glyph.segments=doshapes~=false and result or nil
glyph.boundingbox=boundingbox
if not glyph.width then
glyph.width=width
end
if charset and not glyph.name then
glyph.name=charset[index]
end
end
if trace_charstrings then
report("width: %s",tostring(width))
report("boundingbox: % t",boundingbox)
end
end
resetcharstrings=function()
result={}
top=0
stack={}
end
end
local function readglobals(f,data)
local routines=readlengths(f)
for i=1,#routines do
routines[i]=readstring(f,routines[i])
end
data.routines=routines
end
local function readencodings(f,data)
data.encodings={}
end
local function readcharsets(f,data,dictionary)
local header=data.header
local strings=data.strings
local nofglyphs=data.nofglyphs
local charsetoffset=dictionary.charset
if charsetoffset~=0 then
setposition(f,header.offset+charsetoffset)
local format=readbyte(f)
local charset={ [0]=".notdef" }
dictionary.charset=charset
if format==0 then
for i=1,nofglyphs do
charset[i]=strings[readushort(f)]
end
elseif format==1 or format==2 then
local readcount=format==1 and readbyte or readushort
local i=1
while i<=nofglyphs do
local sid=readushort(f)
local n=readcount(f)
for s=sid,sid+n do
charset[i]=strings[s]
i=i+1
if i>nofglyphs then
break
end
end
end
else
report("cff parser: unsupported charset format %a",format)
end
end
end
local function readprivates(f,data)
local header=data.header
local dictionaries=data.dictionaries
local private=dictionaries[1].private
if private then
setposition(f,header.offset+private.offset)
private.data=readstring(f,private.size)
end
end
local function readlocals(f,data,dictionary)
local header=data.header
local private=dictionary.private
if private then
local subroutineoffset=private.data.subroutines
if subroutineoffset~=0 then
setposition(f,header.offset+private.offset+subroutineoffset)
local subroutines=readlengths(f)
for i=1,#subroutines do
subroutines[i]=readstring(f,subroutines[i])
end
dictionary.subroutines=subroutines
private.data.subroutines=nil
else
dictionary.subroutines={}
end
else
dictionary.subroutines={}
end
end
local function readcharstrings(f,data)
local header=data.header
local dictionaries=data.dictionaries
local dictionary=dictionaries[1]
local type=dictionary.charstringtype
local offset=dictionary.charstrings
if type==2 then
setposition(f,header.offset+offset)
local charstrings=readlengths(f)
local nofglyphs=#charstrings
for i=1,nofglyphs do
charstrings[i]=readstring(f,charstrings[i])
end
data.nofglyphs=nofglyphs
dictionary.charstrings=charstrings
else
report("unsupported charstr type %i",type)
data.nofglyphs=0
dictionary.charstrings={}
end
end
local function readcidprivates(f,data)
local header=data.header
local dictionaries=data.dictionaries[1].cid.dictionaries
for i=1,#dictionaries do
local dictionary=dictionaries[i]
local private=dictionary.private
if private then
setposition(f,header.offset+private.offset)
private.data=readstring(f,private.size)
end
end
parseprivates(data,dictionaries)
end
local function readnoselect(f,data,glyphs,doshapes,version)
local dictionaries=data.dictionaries
local dictionary=dictionaries[1]
readglobals(f,data)
readcharstrings(f,data)
readencodings(f,data)
readcharsets(f,data,dictionary)
readprivates(f,data)
parseprivates(data,data.dictionaries)
readlocals(f,data,dictionary)
parsecharstrings(data,glyphs,doshapes,version)
resetcharstrings()
end
readers.parsecharstrings=parsecharstrings
local function readfdselect(f,data,glyphs,doshapes,version)
local header=data.header
local dictionaries=data.dictionaries
local dictionary=dictionaries[1]
local cid=dictionary.cid
local cidselect=cid and cid.fdselect
readglobals(f,data)
readcharstrings(f,data)
readencodings(f,data)
local charstrings=dictionary.charstrings
local fdindex={}
local nofglyphs=data.nofglyphs
local maxindex=-1
setposition(f,header.offset+cidselect)
local format=readbyte(f)
if format==1 then
for i=0,nofglyphs do
local index=readbyte(i)
fdindex[i]=index
if index>maxindex then
maxindex=index
end
end
elseif format==3 then
local nofranges=readushort(f)
local first=readushort(f)
local index=readbyte(f)
while true do
local last=readushort(f)
if index>maxindex then
maxindex=index
end
for i=first,last do
fdindex[i]=index
end
if last>=nofglyphs then
break
else
first=last+1
index=readbyte(f)
end
end
else
end
if maxindex>=0 then
local cidarray=cid.fdarray
setposition(f,header.offset+cidarray)
local dictionaries=readlengths(f)
for i=1,#dictionaries do
dictionaries[i]=readstring(f,dictionaries[i])
end
parsedictionaries(data,dictionaries)
cid.dictionaries=dictionaries
readcidprivates(f,data)
for i=1,#dictionaries do
readlocals(f,data,dictionaries[i])
end
for i=1,#charstrings do
parsecharstring(data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes,version)
end
resetcharstrings()
end
end
function readers.cff(f,fontdata,specification)
if specification.details then
local datatable=fontdata.tables.cff
if datatable then
local offset=datatable.offset
local glyphs=fontdata.glyphs
if not f then
report("invalid filehandle")
return
end
if offset then
setposition(f,offset)
end
local header=readheader(f)
if header.major>1 then
report("version mismatch")
return
end
local names=readfontnames(f)
local dictionaries=readtopdictionaries(f)
local strings=readstrings(f)
local data={
header=header,
names=names,
dictionaries=dictionaries,
strings=strings,
nofglyphs=fontdata.nofglyphs,
}
parsedictionaries(data,data.dictionaries)
local d=dictionaries[1]
local c=d.cid
fontdata.cffinfo={
familynamename=d.familyname,
fullname=d.fullname,
boundingbox=d.boundingbox,
weight=d.weight,
italicangle=d.italicangle,
underlineposition=d.underlineposition,
underlinethickness=d.underlinethickness,
monospaced=d.monospaced,
}
fontdata.cidinfo=c and {
registry=c.registry,
ordering=c.ordering,
supplement=c.supplement,
}
if not specification.glyphs then
else
local cid=d.cid
if cid and cid.fdselect then
readfdselect(f,data,glyphs,specification.shapes or false)
else
readnoselect(f,data,glyphs,specification.shapes or false)
end
end
end
end
end
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-cff”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-ttf” e0893de6d0f3f421ee4386fa90429db8] ---
if not modules then modules={} end modules ['font-ttf']={
version=1.001,
comment="companion to font-ini.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local next,type,unpack=next,type,unpack
local bittest=bit32.btest
local sqrt=math.sqrt
local report=logs.reporter("otf reader","ttf")
local readers=fonts.handlers.otf.readers
local streamreader=readers.streamreader
local setposition=streamreader.setposition
local getposition=streamreader.getposition
local skipbytes=streamreader.skip
local readbyte=streamreader.readcardinal1
local readushort=streamreader.readcardinal2
local readulong=streamreader.readcardinal4
local readchar=streamreader.readinteger1
local readshort=streamreader.readinteger2
local read2dot14=streamreader.read2dot14
local function mergecomposites(glyphs,shapes)
local function merge(index,shape,components)
local contours={}
local nofcontours=0
for i=1,#components do
local component=components[i]
local subindex=component.index
local subshape=shapes[subindex]
local subcontours=subshape.contours
if not subcontours then
local subcomponents=subshape.components
if subcomponents then
subcontours=merge(subindex,subshape,subcomponents)
end
end
if subcontours then
local matrix=component.matrix
local xscale=matrix[1]
local xrotate=matrix[2]
local yrotate=matrix[3]
local yscale=matrix[4]
local xoffset=matrix[5]
local yoffset=matrix[6]
for i=1,#subcontours do
local points=subcontours[i]
local result={}
for i=1,#points do
local p=points[i]
local x=p[1]
local y=p[2]
result[i]={
xscale*x+xrotate*y+xoffset,
yscale*y+yrotate*x+yoffset,
p[3]
}
end
nofcontours=nofcontours+1
contours[nofcontours]=result
end
else
report("missing contours composite %s, component %s of %s, glyph %s",index,i,#components,subindex)
end
end
shape.contours=contours
shape.components=nil
return contours
end
for index=1,#glyphs do
local shape=shapes[index]
local components=shape.components
if components then
merge(index,shape,components)
end
end
end
local function readnothing(f,nofcontours)
return {
type="nothing",
}
end
local function curveto(m_x,m_y,l_x,l_y,r_x,r_y)
return {
l_x+2/3*(m_x-l_x),l_y+2/3*(m_y-l_y),
r_x+2/3*(m_x-r_x),r_y+2/3*(m_y-r_y),
r_x,r_y,"c"
}
end
local function contours2outlines(glyphs,shapes)
local quadratic=true
for index=1,#glyphs do
local glyph=glyphs[index]
local shape=shapes[index]
local contours=shape.contours
if contours then
local nofcontours=#contours
local segments={}
local nofsegments=0
glyph.segments=segments
if nofcontours>0 then
for i=1,nofcontours do
local contour=contours[i]
local nofcontour=#contour
if nofcontour>0 then
local first_pt=contour[1]
local first_on=first_pt[3]
if nofcontour==1 then
first_pt[3]="m"
nofsegments=nofsegments+1
segments[nofsegments]=first_pt
else
local first_on=first_pt[3]
local last_pt=contour[nofcontour]
local last_on=last_pt[3]
local start=1
local control_pt=false
if first_on then
start=2
else
if last_on then
first_pt=last_pt
else
first_pt={ (first_pt[1]+last_pt[1])/2,(first_pt[2]+last_pt[2])/2,false }
end
control_pt=first_pt
end
nofsegments=nofsegments+1
segments[nofsegments]={ first_pt[1],first_pt[2],"m" }
local previous_pt=first_pt
for i=start,nofcontour do
local current_pt=contour[i]
local current_on=current_pt[3]
local previous_on=previous_pt[3]
if previous_on then
if current_on then
nofsegments=nofsegments+1
segments[nofsegments]={ current_pt[1],current_pt[2],"l" }
else
control_pt=current_pt
end
elseif current_on then
local ps=segments[nofsegments]
nofsegments=nofsegments+1
if quadratic then
segments[nofsegments]={ control_pt[1],control_pt[2],current_pt[1],current_pt[2],"q" }
else
local p=segments[nofsegments-1] local n=#p
segments[nofsegments]=curveto(control_pt[1],control_pt[2],p[n-2],p[n-1],current_pt[1],current_pt[2])
end
control_pt=false
else
nofsegments=nofsegments+1
local halfway_x=(previous_pt[1]+current_pt[1])/2
local halfway_y=(previous_pt[2]+current_pt[2])/2
if quadratic then
segments[nofsegments]={ control_pt[1],control_pt[2],halfway_x,halfway_y,"q" }
else
local p=segments[nofsegments-1] local n=#p
segments[nofsegments]=curveto(control_pt[1],control_pt[2],p[n-2],p[n-1],halfway_x,halfway_y)
end
control_pt=current_pt
end
previous_pt=current_pt
end
if first_pt==last_pt then
else
nofsegments=nofsegments+1
if not control_pt then
segments[nofsegments]={ first_pt[1],first_pt[2],"l" }
elseif quadratic then
segments[nofsegments]={ control_pt[1],control_pt[2],first_pt[1],first_pt[2],"q" }
else
local p=last_pt local n=#p
segments[nofsegments]=curveto(control_pt[1],control_pt[2],p[n-2],p[n-1],first_pt[1],first_pt[2])
end
end
end
end
end
end
end
end
end
local function readglyph(f,nofcontours)
local points={}
local endpoints={}
local instructions={}
local flags={}
for i=1,nofcontours do
endpoints[i]=readshort(f)+1
end
local nofpoints=endpoints[nofcontours]
local nofinstructions=readushort(f)
skipbytes(f,nofinstructions)
local i=1
while i<=nofpoints do
local flag=readbyte(f)
flags[i]=flag
if bittest(flag,0x0008) then
for j=1,readbyte(f) do
i=i+1
flags[i]=flag
end
end
i=i+1
end
local x=0
for i=1,nofpoints do
local flag=flags[i]
local short=bittest(flag,0x0002)
local same=bittest(flag,0x0010)
if short then
if same then
x=x+readbyte(f)
else
x=x-readbyte(f)
end
elseif same then
else
x=x+readshort(f)
end
points[i]={ x,y,bittest(flag,0x0001) }
end
local y=0
for i=1,nofpoints do
local flag=flags[i]
local short=bittest(flag,0x0004)
local same=bittest(flag,0x0020)
if short then
if same then
y=y+readbyte(f)
else
y=y-readbyte(f)
end
elseif same then
else
y=y+readshort(f)
end
points[i][2]=y
end
local first=1
for i=1,#endpoints do
local last=endpoints[i]
endpoints[i]={ unpack(points,first,last) }
first=last+1
end
return {
type="glyph",
contours=endpoints,
}
end
local function readcomposite(f)
local components={}
local nofcomponents=0
local instructions=false
while true do
local flags=readushort(f)
local index=readushort(f)
local f_xyarg=bittest(flags,0x0002)
local f_offset=bittest(flags,0x0800)
local xscale=1
local xrotate=0
local yrotate=0
local yscale=1
local xoffset=0
local yoffset=0
local base=false
local reference=false
if f_xyarg then
if bittest(flags,0x0001) then
xoffset=readshort(f)
yoffset=readshort(f)
else
xoffset=readchar(f)
yoffset=readchar(f)
end
else
if bittest(flags,0x0001) then
base=readshort(f)
reference=readshort(f)
else
base=readchar(f)
reference=readchar(f)
end
end
if bittest(flags,0x0008) then
xscale=read2dot14(f)
yscale=xscale
if f_xyarg and f_offset then
xoffset=xoffset*xscale
yoffset=yoffset*yscale
end
elseif bittest(flags,0x0040) then
xscale=read2dot14(f)
yscale=read2dot14(f)
if f_xyarg and f_offset then
xoffset=xoffset*xscale
yoffset=yoffset*yscale
end
elseif bittest(flags,0x0080) then
xscale=read2dot14(f)
xrotate=read2dot14(f)
yrotate=read2dot14(f)
yscale=read2dot14(f)
if f_xyarg and f_offset then
xoffset=xoffset*sqrt(xscale^2+xrotate^2)
yoffset=yoffset*sqrt(yrotate^2+yscale^2)
end
end
nofcomponents=nofcomponents+1
components[nofcomponents]={
index=index,
usemine=bittest(flags,0x0200),
round=bittest(flags,0x0006),
base=base,
reference=reference,
matrix={ xscale,xrotate,yrotate,yscale,xoffset,yoffset },
}
if bittest(flags,0x0100) then
instructions=true
end
if not bittest(flags,0x0020) then
break
end
end
return {
type="composite",
components=components,
}
end
function readers.loca(f,fontdata,specification)
if specification.glyphs then
local datatable=fontdata.tables.loca
if datatable then
local offset=fontdata.tables.glyf.offset
local format=fontdata.fontheader.indextolocformat
local locations={}
setposition(f,datatable.offset)
if format==1 then
local nofglyphs=datatable.length/4-1
-1
for i=0,nofglyphs do
locations[i]=offset+readulong(f)
end
fontdata.nofglyphs=nofglyphs
else
local nofglyphs=datatable.length/2-1
-1
for i=0,nofglyphs do
locations[i]=offset+readushort(f)*2
end
fontdata.nofglyphs=nofglyphs
end
fontdata.locations=locations
end
end
end
function readers.glyf(f,fontdata,specification)
if specification.glyphs then
local datatable=fontdata.tables.glyf
if datatable then
local locations=fontdata.locations
if locations then
local glyphs=fontdata.glyphs
local nofglyphs=fontdata.nofglyphs
local filesize=fontdata.filesize
local nothing={ 0,0,0,0 }
local shapes={}
local loadshapes=specification.shapes
for index=0,nofglyphs do
local location=locations[index]
if location>=filesize then
report("discarding %s glyphs due to glyph location bug",nofglyphs-index+1)
fontdata.nofglyphs=index-1
fontdata.badfont=true
break
elseif location>0 then
setposition(f,location)
local nofcontours=readshort(f)
glyphs[index].boundingbox={
readshort(f),
readshort(f),
readshort(f),
readshort(f),
}
if not loadshapes then
elseif nofcontours==0 then
shapes[index]=readnothing(f,nofcontours)
elseif nofcontours>0 then
shapes[index]=readglyph(f,nofcontours)
else
shapes[index]=readcomposite(f,nofcontours)
end
else
if loadshapes then
shapes[index]={}
end
glyphs[index].boundingbox=nothing
end
end
if loadshapes then
mergecomposites(glyphs,shapes)
contours2outlines(glyphs,shapes)
end
end
end
end
end
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-ttf”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-dsp” 4a5266ada979d5c2d48867dc3ffaefea] ---
if not modules then modules={} end modules ['font-dsp']={
version=1.001,
comment="companion to font-ini.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local next,type=next,type
local bittest=bit32.btest
local rshift=bit32.rshift
local concat=table.concat
local lower=string.lower
local copy=table.copy
local sub=string.sub
local strip=string.strip
local tohash=table.tohash
local reversed=table.reversed
local setmetatableindex=table.setmetatableindex
local formatters=string.formatters
local sortedkeys=table.sortedkeys
local sortedhash=table.sortedhash
local report=logs.reporter("otf reader")
local readers=fonts.handlers.otf.readers
local streamreader=readers.streamreader
local setposition=streamreader.setposition
local getposition=streamreader.getposition
local skipshort=streamreader.skipshort
local readushort=streamreader.readcardinal2
local readulong=streamreader.readcardinal4
local readshort=streamreader.readinteger2
local readfword=readshort
local readstring=streamreader.readstring
local readtag=streamreader.readtag
local readbytes=streamreader.readbytes
local gsubhandlers={}
local gposhandlers={}
local lookupidoffset=-1
local classes={
"base",
"ligature",
"mark",
"component",
}
local gsubtypes={
"single",
"multiple",
"alternate",
"ligature",
"context",
"chainedcontext",
"extension",
"reversechainedcontextsingle",
}
local gpostypes={
"single",
"pair",
"cursive",
"marktobase",
"marktoligature",
"marktomark",
"context",
"chainedcontext",
"extension",
}
local chaindirections={
context=0,
chainedcontext=1,
reversechainedcontextsingle=-1,
}
local lookupnames={
gsub={
single="gsub_single",
multiple="gsub_multiple",
alternate="gsub_alternate",
ligature="gsub_ligature",
context="gsub_context",
chainedcontext="gsub_contextchain",
reversechainedcontextsingle="gsub_reversecontextchain",
},
gpos={
single="gpos_single",
pair="gpos_pair",
cursive="gpos_cursive",
marktobase="gpos_mark2base",
marktoligature="gpos_mark2ligature",
marktomark="gpos_mark2mark",
context="gpos_context",
chainedcontext="gpos_contextchain",
}
}
local lookupflags=setmetatableindex(function(t,k)
local v={
bittest(k,0x0008) and true or false,
bittest(k,0x0004) and true or false,
bittest(k,0x0002) and true or false,
bittest(k,0x0001) and true or false,
}
t[k]=v
return v
end)
local function readcoverage(f,offset,simple)
setposition(f,offset)
local coverageformat=readushort(f)
local coverage={}
if coverageformat==1 then
local nofcoverage=readushort(f)
if simple then
for i=1,nofcoverage do
coverage[i]=readushort(f)
end
else
for i=0,nofcoverage-1 do
coverage[readushort(f)]=i
end
end
elseif coverageformat==2 then
local nofranges=readushort(f)
local n=simple and 1 or 0
for i=1,nofranges do
local firstindex=readushort(f)
local lastindex=readushort(f)
local coverindex=readushort(f)
if simple then
for i=firstindex,lastindex do
coverage[n]=i
n=n+1
end
else
for i=firstindex,lastindex do
coverage[i]=n
n=n+1
end
end
end
else
report("unknown coverage format %a ",coverageformat)
end
return coverage
end
local function readclassdef(f,offset,preset)
setposition(f,offset)
local classdefformat=readushort(f)
local classdef={}
if type(preset)=="number" then
for k=0,preset-1 do
classdef[k]=1
end
end
if classdefformat==1 then
local index=readushort(f)
local nofclassdef=readushort(f)
for i=1,nofclassdef do
classdef[index]=readushort(f)+1
index=index+1
end
elseif classdefformat==2 then
local nofranges=readushort(f)
local n=0
for i=1,nofranges do
local firstindex=readushort(f)
local lastindex=readushort(f)
local class=readushort(f)+1
for i=firstindex,lastindex do
classdef[i]=class
end
end
else
report("unknown classdef format %a ",classdefformat)
end
if type(preset)=="table" then
for k in next,preset do
if not classdef[k] then
classdef[k]=1
end
end
end
return classdef
end
local function classtocoverage(defs)
if defs then
local list={}
for index,class in next,defs do
local c=list[class]
if c then
c[#c+1]=index
else
list[class]={ index }
end
end
return list
end
end
local function readposition(f,format)
if format==0 then
return nil
end
local x=bittest(format,0x0001) and readshort(f) or 0
local y=bittest(format,0x0002) and readshort(f) or 0
local h=bittest(format,0x0004) and readshort(f) or 0
local v=bittest(format,0x0008) and readshort(f) or 0
if x==0 and y==0 and h==0 and v==0 then
return nil
else
return { x,y,h,v }
end
end
local function readanchor(f,offset)
if not offset or offset==0 then
return nil
end
setposition(f,offset)
local format=readshort(f)
if format==0 then
report("invalid anchor format %i @ position %i",format,offset)
return false
elseif format>3 then
report("unsupported anchor format %i @ position %i",format,offset)
return false
end
return { readshort(f),readshort(f) }
end
local function readfirst(f,offset)
if offset then
setposition(f,offset)
end
return { readushort(f) }
end
local function readarray(f,offset,first)
if offset then
setposition(f,offset)
end
local n=readushort(f)
if first then
local t={ first }
for i=2,n do
t[i]=readushort(f)
end
return t,n
elseif n>0 then
local t={}
for i=1,n do
t[i]=readushort(f)
end
return t,n
end
end
local function readcoveragearray(f,offset,t,simple)
if not t then
return nil
end
local n=#t
if n==0 then
return nil
end
for i=1,n do
t[i]=readcoverage(f,offset+t[i],simple)
end
return t
end
local function covered(subset,all)
local used,u
for i=1,#subset do
local s=subset[i]
if all[s] then
if used then
u=u+1
used[u]=s
else
u=1
used={ s }
end
end
end
return used
end
local function readlookuparray(f,noflookups,nofcurrent)
local lookups={}
if noflookups>0 then
local length=0
for i=1,noflookups do
local index=readushort(f)+1
if index>length then
length=index
end
lookups[index]=readushort(f)+1
end
for index=1,length do
if not lookups[index] then
lookups[index]=false
end
end
end
return lookups
end
local function unchainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,what)
local tableoffset=lookupoffset+offset
setposition(f,tableoffset)
local subtype=readushort(f)
if subtype==1 then
local coverage=readushort(f)
local subclasssets=readarray(f)
local rules={}
if subclasssets then
coverage=readcoverage(f,tableoffset+coverage,true)
for i=1,#subclasssets do
local offset=subclasssets[i]
if offset>0 then
local firstcoverage=coverage[i]
local rulesoffset=tableoffset+offset
local subclassrules=readarray(f,rulesoffset)
for rule=1,#subclassrules do
setposition(f,rulesoffset+subclassrules[rule])
local nofcurrent=readushort(f)
local noflookups=readushort(f)
local current={ { firstcoverage } }
for i=2,nofcurrent do
current[i]={ readushort(f) }
end
local lookups=readlookuparray(f,noflookups,nofcurrent)
rules[#rules+1]={
current=current,
lookups=lookups
}
end
end
end
else
report("empty subclassset in %a subtype %i","unchainedcontext",subtype)
end
return {
format="glyphs",
rules=rules,
}
elseif subtype==2 then
local coverage=readushort(f)
local currentclassdef=readushort(f)
local subclasssets=readarray(f)
local rules={}
if subclasssets then
coverage=readcoverage(f,tableoffset+coverage)
currentclassdef=readclassdef(f,tableoffset+currentclassdef,coverage)
local currentclasses=classtocoverage(currentclassdef,fontdata.glyphs)
for class=1,#subclasssets do
local offset=subclasssets[class]
if offset>0 then
local firstcoverage=currentclasses[class]
if firstcoverage then
firstcoverage=covered(firstcoverage,coverage)
if firstcoverage then
local rulesoffset=tableoffset+offset
local subclassrules=readarray(f,rulesoffset)
for rule=1,#subclassrules do
setposition(f,rulesoffset+subclassrules[rule])
local nofcurrent=readushort(f)
local noflookups=readushort(f)
local current={ firstcoverage }
for i=2,nofcurrent do
current[i]=currentclasses[readushort(f)+1]
end
local lookups=readlookuparray(f,noflookups,nofcurrent)
rules[#rules+1]={
current=current,
lookups=lookups
}
end
else
report("no coverage")
end
else
report("no coverage class")
end
end
end
else
report("empty subclassset in %a subtype %i","unchainedcontext",subtype)
end
return {
format="class",
rules=rules,
}
elseif subtype==3 then
local current=readarray(f)
local noflookups=readushort(f)
local lookups=readlookuparray(f,noflookups,#current)
current=readcoveragearray(f,tableoffset,current,true)
return {
format="coverage",
rules={
{
current=current,
lookups=lookups,
}
}
}
else
report("unsupported subtype %a in %a %s",subtype,"unchainedcontext",what)
end
end
local function chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,what)
local tableoffset=lookupoffset+offset
setposition(f,tableoffset)
local subtype=readushort(f)
if subtype==1 then
local coverage=readushort(f)
local subclasssets=readarray(f)
local rules={}
if subclasssets then
coverage=readcoverage(f,tableoffset+coverage,true)
for i=1,#subclasssets do
local offset=subclasssets[i]
if offset>0 then
local firstcoverage=coverage[i]
local rulesoffset=tableoffset+offset
local subclassrules=readarray(f,rulesoffset)
for rule=1,#subclassrules do
setposition(f,rulesoffset+subclassrules[rule])
local nofbefore=readushort(f)
local before
if nofbefore>0 then
before={}
for i=1,nofbefore do
before[i]={ readushort(f) }
end
end
local nofcurrent=readushort(f)
local current={ { firstcoverage } }
for i=2,nofcurrent do
current[i]={ readushort(f) }
end
local nofafter=readushort(f)
local after
if nofafter>0 then
after={}
for i=1,nofafter do
after[i]={ readushort(f) }
end
end
local noflookups=readushort(f)
local lookups=readlookuparray(f,noflookups,nofcurrent)
rules[#rules+1]={
before=before,
current=current,
after=after,
lookups=lookups,
}
end
end
end
else
report("empty subclassset in %a subtype %i","chainedcontext",subtype)
end
return {
format="glyphs",
rules=rules,
}
elseif subtype==2 then
local coverage=readushort(f)
local beforeclassdef=readushort(f)
local currentclassdef=readushort(f)
local afterclassdef=readushort(f)
local subclasssets=readarray(f)
local rules={}
if subclasssets then
local coverage=readcoverage(f,tableoffset+coverage)
local beforeclassdef=readclassdef(f,tableoffset+beforeclassdef,nofglyphs)
local currentclassdef=readclassdef(f,tableoffset+currentclassdef,coverage)
local afterclassdef=readclassdef(f,tableoffset+afterclassdef,nofglyphs)
local beforeclasses=classtocoverage(beforeclassdef,fontdata.glyphs)
local currentclasses=classtocoverage(currentclassdef,fontdata.glyphs)
local afterclasses=classtocoverage(afterclassdef,fontdata.glyphs)
for class=1,#subclasssets do
local offset=subclasssets[class]
if offset>0 then
local firstcoverage=currentclasses[class]
if firstcoverage then
firstcoverage=covered(firstcoverage,coverage)
if firstcoverage then
local rulesoffset=tableoffset+offset
local subclassrules=readarray(f,rulesoffset)
for rule=1,#subclassrules do
setposition(f,rulesoffset+subclassrules[rule])
local nofbefore=readushort(f)
local before
if nofbefore>0 then
before={}
for i=1,nofbefore do
before[i]=beforeclasses[readushort(f)+1]
end
end
local nofcurrent=readushort(f)
local current={ firstcoverage }
for i=2,nofcurrent do
current[i]=currentclasses[readushort(f)+1]
end
local nofafter=readushort(f)
local after
if nofafter>0 then
after={}
for i=1,nofafter do
after[i]=afterclasses[readushort(f)+1]
end
end
local noflookups=readushort(f)
local lookups=readlookuparray(f,noflookups,nofcurrent)
rules[#rules+1]={
before=before,
current=current,
after=after,
lookups=lookups,
}
end
else
report("no coverage")
end
else
report("class is not covered")
end
end
end
else
report("empty subclassset in %a subtype %i","chainedcontext",subtype)
end
return {
format="class",
rules=rules,
}
elseif subtype==3 then
local before=readarray(f)
local current=readarray(f)
local after=readarray(f)
local noflookups=readushort(f)
local lookups=readlookuparray(f,noflookups,#current)
before=readcoveragearray(f,tableoffset,before,true)
current=readcoveragearray(f,tableoffset,current,true)
after=readcoveragearray(f,tableoffset,after,true)
return {
format="coverage",
rules={
{
before=before,
current=current,
after=after,
lookups=lookups,
}
}
}
else
report("unsupported subtype %a in %a %s",subtype,"chainedcontext",what)
end
end
local function extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,types,handlers,what)
local tableoffset=lookupoffset+offset
setposition(f,tableoffset)
local subtype=readushort(f)
if subtype==1 then
local lookuptype=types[readushort(f)]
local faroffset=readulong(f)
local handler=handlers[lookuptype]
if handler then
return handler(f,fontdata,lookupid,tableoffset+faroffset,0,glyphs,nofglyphs),lookuptype
else
report("no handler for lookuptype %a subtype %a in %s %s",lookuptype,subtype,what,"extension")
end
else
report("unsupported subtype %a in %s %s",subtype,what,"extension")
end
end
function gsubhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
local tableoffset=lookupoffset+offset
setposition(f,tableoffset)
local subtype=readushort(f)
if subtype==1 then
local coverage=readushort(f)
local delta=readshort(f)
local coverage=readcoverage(f,tableoffset+coverage)
for index in next,coverage do
local newindex=index+delta
if index>nofglyphs or newindex>nofglyphs then
report("invalid index in %s format %i: %i -> %i (max %i)","single",subtype,index,newindex,nofglyphs)
coverage[index]=nil
else
coverage[index]=newindex
end
end
return {
coverage=coverage
}
elseif subtype==2 then
local coverage=readushort(f)
local nofreplacements=readushort(f)
local replacements={}
for i=1,nofreplacements do
replacements[i]=readushort(f)
end
local coverage=readcoverage(f,tableoffset+coverage)
for index,newindex in next,coverage do
newindex=newindex+1
if index>nofglyphs or newindex>nofglyphs then
report("invalid index in %s format %i: %i -> %i (max %i)","single",subtype,index,newindex,nofglyphs)
coverage[index]=nil
else
coverage[index]=replacements[newindex]
end
end
return {
coverage=coverage
}
else
report("unsupported subtype %a in %a substitution",subtype,"single")
end
end
local function sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,what)
local tableoffset=lookupoffset+offset
setposition(f,tableoffset)
local subtype=readushort(f)
if subtype==1 then
local coverage=readushort(f)
local nofsequence=readushort(f)
local sequences={}
for i=1,nofsequence do
sequences[i]=readushort(f)
end
for i=1,nofsequence do
setposition(f,tableoffset+sequences[i])
local n=readushort(f)
local s={}
for i=1,n do
s[i]=readushort(f)
end
sequences[i]=s
end
local coverage=readcoverage(f,tableoffset+coverage)
for index,newindex in next,coverage do
newindex=newindex+1
if index>nofglyphs or newindex>nofglyphs then
report("invalid index in %s format %i: %i -> %i (max %i)",what,subtype,index,newindex,nofglyphs)
coverage[index]=nil
else
coverage[index]=sequences[newindex]
end
end
return {
coverage=coverage
}
else
report("unsupported subtype %a in %a substitution",subtype,what)
end
end
function gsubhandlers.multiple(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
return sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"multiple")
end
function gsubhandlers.alternate(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
return sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"alternate")
end
function gsubhandlers.ligature(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
local tableoffset=lookupoffset+offset
setposition(f,tableoffset)
local subtype=readushort(f)
if subtype==1 then
local coverage=readushort(f)
local nofsets=readushort(f)
local ligatures={}
for i=1,nofsets do
ligatures[i]=readushort(f)
end
for i=1,nofsets do
local offset=lookupoffset+offset+ligatures[i]
setposition(f,offset)
local n=readushort(f)
local l={}
for i=1,n do
l[i]=offset+readushort(f)
end
ligatures[i]=l
end
local coverage=readcoverage(f,tableoffset+coverage)
for index,newindex in next,coverage do
local hash={}
local ligatures=ligatures[newindex+1]
for i=1,#ligatures do
local offset=ligatures[i]
setposition(f,offset)
local lig=readushort(f)
local cnt=readushort(f)
local hsh=hash
for i=2,cnt do
local c=readushort(f)
local h=hsh[c]
if not h then
h={}
hsh[c]=h
end
hsh=h
end
hsh.ligature=lig
end
coverage[index]=hash
end
return {
coverage=coverage
}
else
report("unsupported subtype %a in %a substitution",subtype,"ligature")
end
end
function gsubhandlers.context(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
return unchainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"substitution"),"context"
end
function gsubhandlers.chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
return chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"substitution"),"chainedcontext"
end
function gsubhandlers.extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
return extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,gsubtypes,gsubhandlers,"substitution")
end
function gsubhandlers.reversechainedcontextsingle(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
local tableoffset=lookupoffset+offset
setposition(f,tableoffset)
local subtype=readushort(f)
if subtype==1 then
local current=readfirst(f)
local before=readarray(f)
local after=readarray(f)
local replacements=readarray(f)
current=readcoveragearray(f,tableoffset,current,true)
before=readcoveragearray(f,tableoffset,before,true)
after=readcoveragearray(f,tableoffset,after,true)
return {
coverage={
format="reversecoverage",
before=before,
current=current,
after=after,
replacements=replacements,
}
},"reversechainedcontextsingle"
else
report("unsupported subtype %a in %a substitution",subtype,"reversechainedcontextsingle")
end
end
local function readpairsets(f,tableoffset,sets,format1,format2)
local done={}
for i=1,#sets do
local offset=sets[i]
local reused=done[offset]
if not reused then
setposition(f,tableoffset+offset)
local n=readushort(f)
reused={}
for i=1,n do
reused[i]={
readushort(f),
readposition(f,format1),
readposition(f,format2)
}
end
done[offset]=reused
end
sets[i]=reused
end
return sets
end
local function readpairclasssets(f,nofclasses1,nofclasses2,format1,format2)
local classlist1={}
for i=1,nofclasses1 do
local classlist2={}
classlist1[i]=classlist2
for j=1,nofclasses2 do
local one=readposition(f,format1)
local two=readposition(f,format2)
if one or two then
classlist2[j]={ one,two }
else
classlist2[j]=false
end
end
end
return classlist1
end
function gposhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
local tableoffset=lookupoffset+offset
setposition(f,tableoffset)
local subtype=readushort(f)
if subtype==1 then
local coverage=readushort(f)
local format=readushort(f)
local value=readposition(f,format)
local coverage=readcoverage(f,tableoffset+coverage)
for index,newindex in next,coverage do
coverage[index]=value
end
return {
format="pair",
coverage=coverage
}
elseif subtype==2 then
local coverage=readushort(f)
local format=readushort(f)
local values={}
local nofvalues=readushort(f)
for i=1,nofvalues do
values[i]=readposition(f,format)
end
local coverage=readcoverage(f,tableoffset+coverage)
for index,newindex in next,coverage do
coverage[index]=values[newindex+1]
end
return {
format="pair",
coverage=coverage
}
else
report("unsupported subtype %a in %a positioning",subtype,"single")
end
end
function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
local tableoffset=lookupoffset+offset
setposition(f,tableoffset)
local subtype=readushort(f)
if subtype==1 then
local coverage=readushort(f)
local format1=readushort(f)
local format2=readushort(f)
local sets=readarray(f)
sets=readpairsets(f,tableoffset,sets,format1,format2)
coverage=readcoverage(f,tableoffset+coverage)
for index,newindex in next,coverage do
local set=sets[newindex+1]
local hash={}
for i=1,#set do
local value=set[i]
if value then
local other=value[1]
local first=value[2]
local second=value[3]
if first or second then
hash[other]={ first,second }
else
hash[other]=nil
end
end
end
coverage[index]=hash
end
return {
format="pair",
coverage=coverage
}
elseif subtype==2 then
local coverage=readushort(f)
local format1=readushort(f)
local format2=readushort(f)
local classdef1=readushort(f)
local classdef2=readushort(f)
local nofclasses1=readushort(f)
local nofclasses2=readushort(f)
local classlist=readpairclasssets(f,nofclasses1,nofclasses2,format1,format2)
coverage=readcoverage(f,tableoffset+coverage)
classdef1=readclassdef(f,tableoffset+classdef1,coverage)
classdef2=readclassdef(f,tableoffset+classdef2,nofglyphs)
local usedcoverage={}
for g1,c1 in next,classdef1 do
if coverage[g1] then
local l1=classlist[c1]
if l1 then
local hash={}
for paired,class in next,classdef2 do
local offsets=l1[class]
if offsets then
local first=offsets[1]
local second=offsets[2]
if first or second then
hash[paired]={ first,second }
else
end
end
end
usedcoverage[g1]=hash
end
end
end
return {
format="pair",
coverage=usedcoverage
}
elseif subtype==3 then
report("yet unsupported subtype %a in %a positioning",subtype,"pair")
else
report("unsupported subtype %a in %a positioning",subtype,"pair")
end
end
function gposhandlers.cursive(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
local tableoffset=lookupoffset+offset
setposition(f,tableoffset)
local subtype=readushort(f)
if subtype==1 then
local coverage=tableoffset+readushort(f)
local nofrecords=readushort(f)
local records={}
for i=1,nofrecords do
local entry=readushort(f)
local exit=readushort(f)
records[i]={
entry=entry~=0 and (tableoffset+entry) or false,
exit=exit~=0 and (tableoffset+exit ) or false,
}
end
coverage=readcoverage(f,coverage)
for i=1,nofrecords do
local r=records[i]
records[i]={
1,
readanchor(f,r.entry) or nil,
readanchor(f,r.exit ) or nil,
}
end
for index,newindex in next,coverage do
coverage[index]=records[newindex+1]
end
return {
coverage=coverage
}
else
report("unsupported subtype %a in %a positioning",subtype,"cursive")
end
end
local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,ligature)
local tableoffset=lookupoffset+offset
setposition(f,tableoffset)
local subtype=readushort(f)
if subtype==1 then
local markcoverage=tableoffset+readushort(f)
local basecoverage=tableoffset+readushort(f)
local nofclasses=readushort(f)
local markoffset=tableoffset+readushort(f)
local baseoffset=tableoffset+readushort(f)
local markcoverage=readcoverage(f,markcoverage)
local basecoverage=readcoverage(f,basecoverage,true)
setposition(f,markoffset)
local markclasses={}
local nofmarkclasses=readushort(f)
local lastanchor=fontdata.lastanchor or 0
local usedanchors={}
for i=1,nofmarkclasses do
local class=readushort(f)+1
local offset=readushort(f)
if offset==0 then
markclasses[i]=false
else
markclasses[i]={ class,markoffset+offset }
end
usedanchors[class]=true
end
for i=1,nofmarkclasses do
local mc=markclasses[i]
if mc then
mc[2]=readanchor(f,mc[2])
end
end
setposition(f,baseoffset)
local nofbaserecords=readushort(f)
local baserecords={}
if ligature then
for i=1,nofbaserecords do
local offset=readushort(f)
if offset==0 then
baserecords[i]=false
else
baserecords[i]=baseoffset+offset
end
end
for i=1,nofbaserecords do
local recordoffset=baserecords[i]
if recordoffset then
setposition(f,recordoffset)
local nofcomponents=readushort(f)
local components={}
for i=1,nofcomponents do
local classes={}
for i=1,nofclasses do
local offset=readushort(f)
if offset~=0 then
classes[i]=recordoffset+offset
else
classes[i]=false
end
end
components[i]=classes
end
baserecords[i]=components
end
end
local baseclasses={}
for i=1,nofclasses do
baseclasses[i]={}
end
for i=1,nofbaserecords do
local components=baserecords[i]
if components then
local b=basecoverage[i]
for c=1,#components do
local classes=components[c]
if classes then
for i=1,nofclasses do
local anchor=readanchor(f,classes[i])
local bclass=baseclasses[i]
local bentry=bclass[b]
if bentry then
bentry[c]=anchor
else
bclass[b]={ [c]=anchor }
end
end
end
end
end
end
for index,newindex in next,markcoverage do
markcoverage[index]=markclasses[newindex+1] or nil
end
return {
format="ligature",
baseclasses=baseclasses,
coverage=markcoverage,
}
else
for i=1,nofbaserecords do
local r={}
for j=1,nofclasses do
local offset=readushort(f)
if offset==0 then
r[j]=false
else
r[j]=baseoffset+offset
end
end
baserecords[i]=r
end
local baseclasses={}
for i=1,nofclasses do
baseclasses[i]={}
end
for i=1,nofbaserecords do
local r=baserecords[i]
local b=basecoverage[i]
for j=1,nofclasses do
baseclasses[j][b]=readanchor(f,r[j])
end
end
for index,newindex in next,markcoverage do
markcoverage[index]=markclasses[newindex+1] or nil
end
return {
format="base",
baseclasses=baseclasses,
coverage=markcoverage,
}
end
else
report("unsupported subtype %a in",subtype)
end
end
function gposhandlers.marktobase(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
return handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
end
function gposhandlers.marktoligature(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
return handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,true)
end
function gposhandlers.marktomark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
return handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
end
function gposhandlers.context(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
return unchainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"positioning"),"context"
end
function gposhandlers.chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
return chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"positioning"),"chainedcontext"
end
function gposhandlers.extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
return extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,gpostypes,gposhandlers,"positioning")
end
do
local plugins={}
function plugins.size(f,fontdata,tableoffset,feature)
if fontdata.designsize then
else
local function check(offset)
setposition(f,offset)
local designsize=readushort(f)
if designsize>0 then
local fontstyle=readushort(f)
local guimenuid=readushort(f)
local minsize=readushort(f)
local maxsize=readushort(f)
if minsize==0 and maxsize==0 and fontstyleid==0 and guimenuid==0 then
minsize=designsize
maxsize=designsize
end
if designsize>=minsize and designsize<=maxsize then
return minsize,maxsize,designsize
end
end
end
local minsize,maxsize,designsize=check(tableoffset+feature.offset+feature.parameters)
if not designsize then
minsize,maxsize,designsize=check(tableoffset+feature.parameters)
if designsize then
report("bad size feature in %a, falling back to wrong offset",fontdata.filename or "?")
else
report("bad size feature in %a,",fontdata.filename or "?")
end
end
if designsize then
fontdata.minsize=minsize
fontdata.maxsize=maxsize
fontdata.designsize=designsize
end
end
end
local function reorderfeatures(fontdata,scripts,features)
local scriptlangs={}
local featurehash={}
local featureorder={}
for script,languages in next,scripts do
for language,record in next,languages do
local hash={}
local list=record.featureindices
for k=1,#list do
local index=list[k]
local feature=features[index]
local lookups=feature.lookups
local tag=feature.tag
if tag then
hash[tag]=true
end
if lookups then
for i=1,#lookups do
local lookup=lookups[i]
local o=featureorder[lookup]
if o then
local okay=true
for i=1,#o do
if o[i]==tag then
okay=false
break
end
end
if okay then
o[#o+1]=tag
end
else
featureorder[lookup]={ tag }
end
local f=featurehash[lookup]
if f then
local h=f[tag]
if h then
local s=h[script]
if s then
s[language]=true
else
h[script]={ [language]=true }
end
else
f[tag]={ [script]={ [language]=true } }
end
else
featurehash[lookup]={ [tag]={ [script]={ [language]=true } } }
end
local h=scriptlangs[tag]
if h then
local s=h[script]
if s then
s[language]=true
else
h[script]={ [language]=true }
end
else
scriptlangs[tag]={ [script]={ [language]=true } }
end
end
end
end
end
end
return scriptlangs,featurehash,featureorder
end
local function readscriplan(f,fontdata,scriptoffset)
setposition(f,scriptoffset)
local nofscripts=readushort(f)
local scripts={}
for i=1,nofscripts do
scripts[readtag(f)]=scriptoffset+readushort(f)
end
local languagesystems=setmetatableindex("table")
for script,offset in next,scripts do
setposition(f,offset)
local defaultoffset=readushort(f)
local noflanguages=readushort(f)
local languages={}
if defaultoffset>0 then
languages.dflt=languagesystems[offset+defaultoffset]
end
for i=1,noflanguages do
local language=readtag(f)
local offset=offset+readushort(f)
languages[language]=languagesystems[offset]
end
scripts[script]=languages
end
for offset,usedfeatures in next,languagesystems do
if offset>0 then
setposition(f,offset)
local featureindices={}
usedfeatures.featureindices=featureindices
usedfeatures.lookuporder=readushort(f)
usedfeatures.requiredindex=readushort(f)
local noffeatures=readushort(f)
for i=1,noffeatures do
featureindices[i]=readushort(f)+1
end
end
end
return scripts
end
local function readfeatures(f,fontdata,featureoffset)
setposition(f,featureoffset)
local features={}
local noffeatures=readushort(f)
for i=1,noffeatures do
features[i]={
tag=readtag(f),
offset=readushort(f)
}
end
for i=1,noffeatures do
local feature=features[i]
local offset=featureoffset+feature.offset
setposition(f,offset)
local parameters=readushort(f)
local noflookups=readushort(f)
if noflookups>0 then
local lookups={}
feature.lookups=lookups
for j=1,noflookups do
lookups[j]=readushort(f)+1
end
end
if parameters>0 then
feature.parameters=parameters
local plugin=plugins[feature.tag]
if plugin then
plugin(f,fontdata,featureoffset,feature)
end
end
end
return features
end
local function readlookups(f,lookupoffset,lookuptypes,featurehash,featureorder)
setposition(f,lookupoffset)
local lookups={}
local noflookups=readushort(f)
for i=1,noflookups do
lookups[i]=readushort(f)
end
for lookupid=1,noflookups do
local index=lookups[lookupid]
setposition(f,lookupoffset+index)
local subtables={}
local typebits=readushort(f)
local flagbits=readushort(f)
local lookuptype=lookuptypes[typebits]
local lookupflags=lookupflags[flagbits]
local nofsubtables=readushort(f)
for j=1,nofsubtables do
local offset=readushort(f)
subtables[j]=offset+index
end
local markclass=bittest(flagbits,0x0010)
if markclass then
markclass=readushort(f)
end
local markset=rshift(flagbits,8)
if markset>0 then
markclass=markset
end
lookups[lookupid]={
type=lookuptype,
flags=lookupflags,
name=lookupid,
subtables=subtables,
markclass=markclass,
features=featurehash[lookupid],
order=featureorder[lookupid],
}
end
return lookups
end
local function readscriptoffsets(f,fontdata,tableoffset)
if not tableoffset then
return
end
setposition(f,tableoffset)
local version=readulong(f)
if version~=0x00010000 then
report("table version %a of %a is not supported (yet), maybe font %s is bad",version,what,fontdata.filename)
return
end
return tableoffset+readushort(f),tableoffset+readushort(f),tableoffset+readushort(f)
end
local f_lookupname=formatters["%s_%s_%s"]
local function resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what)
local sequences=fontdata.sequences or {}
local sublookuplist=fontdata.sublookups or {}
fontdata.sequences=sequences
fontdata.sublookups=sublookuplist
local nofsublookups=#sublookuplist
local nofsequences=#sequences
local lastsublookup=nofsublookups
local lastsequence=nofsequences
local lookupnames=lookupnames[what]
local sublookuphash={}
local sublookupcheck={}
local glyphs=fontdata.glyphs
local nofglyphs=fontdata.nofglyphs or #glyphs
local noflookups=#lookups
local lookupprefix=sub(what,2,2)
for lookupid=1,noflookups do
local lookup=lookups[lookupid]
local lookuptype=lookup.type
local subtables=lookup.subtables
local features=lookup.features
local handler=lookuphandlers[lookuptype]
if handler then
local nofsubtables=#subtables
local order=lookup.order
local flags=lookup.flags
if flags[1] then flags[1]="mark" end
if flags[2] then flags[2]="ligature" end
if flags[3] then flags[3]="base" end
local markclass=lookup.markclass
if nofsubtables>0 then
local steps={}
local nofsteps=0
local oldtype=nil
for s=1,nofsubtables do
local step,lt=handler(f,fontdata,lookupid,lookupoffset,subtables[s],glyphs,nofglyphs)
if lt then
lookuptype=lt
if oldtype and lt~=oldtype then
report("messy %s lookup type %a and %a",what,lookuptype,oldtype)
end
oldtype=lookuptype
end
if not step then
report("unsupported %s lookup type %a",what,lookuptype)
else
nofsteps=nofsteps+1
steps[nofsteps]=step
local rules=step.rules
if rules then
for i=1,#rules do
local rule=rules[i]
local before=rule.before
local current=rule.current
local after=rule.after
if before then
for i=1,#before do
before[i]=tohash(before[i])
end
rule.before=reversed(before)
end
if current then
for i=1,#current do
current[i]=tohash(current[i])
end
end
if after then
for i=1,#after do
after[i]=tohash(after[i])
end
end
end
end
end
end
if nofsteps~=nofsubtables then
report("bogus subtables removed in %s lookup type %a",what,lookuptype)
end
lookuptype=lookupnames[lookuptype] or lookuptype
if features then
nofsequences=nofsequences+1
local l={
index=nofsequences,
name=f_lookupname(lookupprefix,"s",lookupid+lookupidoffset),
steps=steps,
nofsteps=nofsteps,
type=lookuptype,
markclass=markclass or nil,
flags=flags,
order=order,
features=features,
}
sequences[nofsequences]=l
lookup.done=l
else
nofsublookups=nofsublookups+1
local l={
index=nofsublookups,
name=f_lookupname(lookupprefix,"l",lookupid+lookupidoffset),
steps=steps,
nofsteps=nofsteps,
type=lookuptype,
markclass=markclass or nil,
flags=flags,
}
sublookuplist[nofsublookups]=l
sublookuphash[lookupid]=nofsublookups
sublookupcheck[lookupid]=0
lookup.done=l
end
else
report("no subtables for lookup %a",lookupid)
end
else
report("no handler for lookup %a with type %a",lookupid,lookuptype)
end
end
local reported={}
local function report_issue(i,what,sequence,kind)
local name=sequence.name
if not reported[name] then
report("rule %i in %s lookup %a has %s lookups",i,what,name,kind)
reported[name]=true
end
end
for i=lastsequence+1,nofsequences do
local sequence=sequences[i]
local steps=sequence.steps
for i=1,#steps do
local step=steps[i]
local rules=step.rules
if rules then
for i=1,#rules do
local rule=rules[i]
local rlookups=rule.lookups
if not rlookups then
report_issue(i,what,sequence,"no")
elseif not next(rlookups) then
report_issue(i,what,sequence,"empty")
rule.lookups=nil
else
local length=#rlookups
for index=1,length do
local lookupid=rlookups[index]
if lookupid then
local h=sublookuphash[lookupid]
if not h then
local lookup=lookups[lookupid]
if lookup then
local d=lookup.done
if d then
nofsublookups=nofsublookups+1
h={
index=nofsublookups,
name=f_lookupname(lookupprefix,"d",lookupid+lookupidoffset),
derived=true,
steps=d.steps,
nofsteps=d.nofsteps,
type=d.lookuptype,
markclass=d.markclass or nil,
flags=d.flags,
}
sublookuplist[nofsublookups]=copy(h)
sublookuphash[lookupid]=nofsublookups
sublookupcheck[lookupid]=1
h=nofsublookups
else
report_issue(i,what,sequence,"missing")
rule.lookups=nil
break
end
else
report_issue(i,what,sequence,"bad")
rule.lookups=nil
break
end
else
sublookupcheck[lookupid]=sublookupcheck[lookupid]+1
end
rlookups[index]=h or false
else
rlookups[index]=false
end
end
end
end
end
end
end
for i,n in sortedhash(sublookupcheck) do
local l=lookups[i]
local t=l.type
if n==0 and t~="extension" then
local d=l.done
report("%s lookup %s of type %a is not used",what,d and d.name or l.name,t)
end
end
end
local function readscripts(f,fontdata,what,lookuptypes,lookuphandlers,lookupstoo)
local datatable=fontdata.tables[what]
if not datatable then
return
end
local tableoffset=datatable.offset
if not tableoffset then
return
end
local scriptoffset,featureoffset,lookupoffset=readscriptoffsets(f,fontdata,tableoffset)
if not scriptoffset then
return
end
local scripts=readscriplan(f,fontdata,scriptoffset)
local features=readfeatures(f,fontdata,featureoffset)
local scriptlangs,featurehash,featureorder=reorderfeatures(fontdata,scripts,features)
if fontdata.features then
fontdata.features[what]=scriptlangs
else
fontdata.features={ [what]=scriptlangs }
end
if not lookupstoo then
return
end
local lookups=readlookups(f,lookupoffset,lookuptypes,featurehash,featureorder)
if lookups then
resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what)
end
end
local function checkkerns(f,fontdata,specification)
local datatable=fontdata.tables.kern
if not datatable then
return
end
local features=fontdata.features
local gposfeatures=features and features.gpos
local name
if not gposfeatures or not gposfeatures.kern then
name="kern"
elseif specification.globalkerns then
name="globalkern"
else
report("ignoring global kern table using gpos kern feature")
return
end
report("adding global kern table as gpos feature %a",name)
setposition(f,datatable.offset)
local version=readushort(f)
local noftables=readushort(f)
local kerns=setmetatableindex("table")
for i=1,noftables do
local version=readushort(f)
local length=readushort(f)
local coverage=readushort(f)
local format=bit32.rshift(coverage,8)
if format==0 then
local nofpairs=readushort(f)
local searchrange=readushort(f)
local entryselector=readushort(f)
local rangeshift=readushort(f)
for i=1,nofpairs do
kerns[readushort(f)][readushort(f)]=readfword(f)
end
elseif format==2 then
else
end
end
local feature={ dflt={ dflt=true } }
if not features then
fontdata.features={ gpos={ [name]=feature } }
elseif not gposfeatures then
fontdata.features.gpos={ [name]=feature }
else
gposfeatures[name]=feature
end
local sequences=fontdata.sequences
if not sequences then
sequences={}
fontdata.sequences=sequences
end
local nofsequences=#sequences+1
sequences[nofsequences]={
index=nofsequences,
name=name,
steps={
{
coverage=kerns,
format="kern",
},
},
nofsteps=1,
type="gpos_pair",
flags={ false,false,false,false },
order={ name },
features={ [name]=feature },
}
end
function readers.gsub(f,fontdata,specification)
if specification.details then
readscripts(f,fontdata,"gsub",gsubtypes,gsubhandlers,specification.lookups)
end
end
function readers.gpos(f,fontdata,specification)
if specification.details then
readscripts(f,fontdata,"gpos",gpostypes,gposhandlers,specification.lookups)
if specification.lookups then
checkkerns(f,fontdata,specification)
end
end
end
end
function readers.gdef(f,fontdata,specification)
if specification.glyphs then
local datatable=fontdata.tables.gdef
if datatable then
local tableoffset=datatable.offset
setposition(f,tableoffset)
local version=readulong(f)
local classoffset=tableoffset+readushort(f)
local attachmentoffset=tableoffset+readushort(f)
local ligaturecarets=tableoffset+readushort(f)
local markclassoffset=tableoffset+readushort(f)
local marksetsoffset=version==0x00010002 and (tableoffset+readushort(f))
local glyphs=fontdata.glyphs
local marks={}
local markclasses=setmetatableindex("table")
local marksets=setmetatableindex("table")
fontdata.marks=marks
fontdata.markclasses=markclasses
fontdata.marksets=marksets
setposition(f,classoffset)
local classformat=readushort(f)
if classformat==1 then
local firstindex=readushort(f)
local lastindex=firstindex+readushort(f)-1
for index=firstindex,lastindex do
local class=classes[readushort(f)]
if class=="mark" then
marks[index]=true
end
glyphs[index].class=class
end
elseif classformat==2 then
local nofranges=readushort(f)
for i=1,nofranges do
local firstindex=readushort(f)
local lastindex=readushort(f)
local class=classes[readushort(f)]
if class then
for index=firstindex,lastindex do
glyphs[index].class=class
if class=="mark" then
marks[index]=true
end
end
end
end
end
setposition(f,markclassoffset)
local classformat=readushort(f)
if classformat==1 then
local firstindex=readushort(f)
local lastindex=firstindex+readushort(f)-1
for index=firstindex,lastindex do
markclasses[readushort(f)][index]=true
end
elseif classformat==2 then
local nofranges=readushort(f)
for i=1,nofranges do
local firstindex=readushort(f)
local lastindex=readushort(f)
local class=markclasses[readushort(f)]
for index=firstindex,lastindex do
class[index]=true
end
end
end
if marksetsoffset and marksetsoffset>tableoffset then
setposition(f,marksetsoffset)
local format=readushort(f)
if format==1 then
local nofsets=readushort(f)
local sets={}
for i=1,nofsets do
sets[i]=readulong(f)
end
for i=1,nofsets do
local offset=sets[i]
if offset~=0 then
marksets[i]=readcoverage(f,marksetsoffset+offset)
end
end
end
end
end
end
end
local function readmathvalue(f)
local v=readshort(f)
skipshort(f,1)
return v
end
local function readmathconstants(f,fontdata,offset)
setposition(f,offset)
fontdata.mathconstants={
ScriptPercentScaleDown=readshort(f),
ScriptScriptPercentScaleDown=readshort(f),
DelimitedSubFormulaMinHeight=readushort(f),
DisplayOperatorMinHeight=readushort(f),
MathLeading=readmathvalue(f),
AxisHeight=readmathvalue(f),
AccentBaseHeight=readmathvalue(f),
FlattenedAccentBaseHeight=readmathvalue(f),
SubscriptShiftDown=readmathvalue(f),
SubscriptTopMax=readmathvalue(f),
SubscriptBaselineDropMin=readmathvalue(f),
SuperscriptShiftUp=readmathvalue(f),
SuperscriptShiftUpCramped=readmathvalue(f),
SuperscriptBottomMin=readmathvalue(f),
SuperscriptBaselineDropMax=readmathvalue(f),
SubSuperscriptGapMin=readmathvalue(f),
SuperscriptBottomMaxWithSubscript=readmathvalue(f),
SpaceAfterScript=readmathvalue(f),
UpperLimitGapMin=readmathvalue(f),
UpperLimitBaselineRiseMin=readmathvalue(f),
LowerLimitGapMin=readmathvalue(f),
LowerLimitBaselineDropMin=readmathvalue(f),
StackTopShiftUp=readmathvalue(f),
StackTopDisplayStyleShiftUp=readmathvalue(f),
StackBottomShiftDown=readmathvalue(f),
StackBottomDisplayStyleShiftDown=readmathvalue(f),
StackGapMin=readmathvalue(f),
StackDisplayStyleGapMin=readmathvalue(f),
StretchStackTopShiftUp=readmathvalue(f),
StretchStackBottomShiftDown=readmathvalue(f),
StretchStackGapAboveMin=readmathvalue(f),
StretchStackGapBelowMin=readmathvalue(f),
FractionNumeratorShiftUp=readmathvalue(f),
FractionNumeratorDisplayStyleShiftUp=readmathvalue(f),
FractionDenominatorShiftDown=readmathvalue(f),
FractionDenominatorDisplayStyleShiftDown=readmathvalue(f),
FractionNumeratorGapMin=readmathvalue(f),
FractionNumeratorDisplayStyleGapMin=readmathvalue(f),
FractionRuleThickness=readmathvalue(f),
FractionDenominatorGapMin=readmathvalue(f),
FractionDenominatorDisplayStyleGapMin=readmathvalue(f),
SkewedFractionHorizontalGap=readmathvalue(f),
SkewedFractionVerticalGap=readmathvalue(f),
OverbarVerticalGap=readmathvalue(f),
OverbarRuleThickness=readmathvalue(f),
OverbarExtraAscender=readmathvalue(f),
UnderbarVerticalGap=readmathvalue(f),
UnderbarRuleThickness=readmathvalue(f),
UnderbarExtraDescender=readmathvalue(f),
RadicalVerticalGap=readmathvalue(f),
RadicalDisplayStyleVerticalGap=readmathvalue(f),
RadicalRuleThickness=readmathvalue(f),
RadicalExtraAscender=readmathvalue(f),
RadicalKernBeforeDegree=readmathvalue(f),
RadicalKernAfterDegree=readmathvalue(f),
RadicalDegreeBottomRaisePercent=readshort(f),
}
end
local function readmathglyphinfo(f,fontdata,offset)
setposition(f,offset)
local italics=readushort(f)
local accents=readushort(f)
local extensions=readushort(f)
local kerns=readushort(f)
local glyphs=fontdata.glyphs
if italics~=0 then
setposition(f,offset+italics)
local coverage=readushort(f)
local nofglyphs=readushort(f)
coverage=readcoverage(f,offset+italics+coverage,true)
setposition(f,offset+italics+4)
for i=1,nofglyphs do
local italic=readmathvalue(f)
if italic~=0 then
local glyph=glyphs[coverage[i]]
local math=glyph.math
if not math then
glyph.math={ italic=italic }
else
math.italic=italic
end
end
end
fontdata.hasitalics=true
end
if accents~=0 then
setposition(f,offset+accents)
local coverage=readushort(f)
local nofglyphs=readushort(f)
coverage=readcoverage(f,offset+accents+coverage,true)
setposition(f,offset+accents+4)
for i=1,nofglyphs do
local accent=readmathvalue(f)
if accent~=0 then
local glyph=glyphs[coverage[i]]
local math=glyph.math
if not math then
glyph.math={ accent=accent }
else
math.accent=accent
end
end
end
end
if extensions~=0 then
setposition(f,offset+extensions)
end
if kerns~=0 then
local kernoffset=offset+kerns
setposition(f,kernoffset)
local coverage=readushort(f)
local nofglyphs=readushort(f)
if nofglyphs>0 then
local function get(offset)
setposition(f,kernoffset+offset)
local n=readushort(f)
if n==0 then
local k=readmathvalue(f)
if k==0 then
else
return { { kern=k } }
end
else
local l={}
for i=1,n do
l[i]={ height=readmathvalue(f) }
end
for i=1,n do
l[i].kern=readmathvalue(f)
end
l[n+1]={ kern=readmathvalue(f) }
return l
end
end
local kernsets={}
for i=1,nofglyphs do
local topright=readushort(f)
local topleft=readushort(f)
local bottomright=readushort(f)
local bottomleft=readushort(f)
kernsets[i]={
topright=topright~=0 and topright or nil,
topleft=topleft~=0 and topleft or nil,
bottomright=bottomright~=0 and bottomright or nil,
bottomleft=bottomleft~=0 and bottomleft or nil,
}
end
coverage=readcoverage(f,kernoffset+coverage,true)
for i=1,nofglyphs do
local kernset=kernsets[i]
if next(kernset) then
local k=kernset.topright if k then kernset.topright=get(k) end
local k=kernset.topleft if k then kernset.topleft=get(k) end
local k=kernset.bottomright if k then kernset.bottomright=get(k) end
local k=kernset.bottomleft if k then kernset.bottomleft=get(k) end
if next(kernset) then
local glyph=glyphs[coverage[i]]
local math=glyph.math
if math then
math.kerns=kernset
else
glyph.math={ kerns=kernset }
end
end
end
end
end
end
end
local function readmathvariants(f,fontdata,offset)
setposition(f,offset)
local glyphs=fontdata.glyphs
local minoverlap=readushort(f)
local vcoverage=readushort(f)
local hcoverage=readushort(f)
local vnofglyphs=readushort(f)
local hnofglyphs=readushort(f)
local vconstruction={}
local hconstruction={}
for i=1,vnofglyphs do
vconstruction[i]=readushort(f)
end
for i=1,hnofglyphs do
hconstruction[i]=readushort(f)
end
fontdata.mathconstants.MinConnectorOverlap=minoverlap
local function get(offset,coverage,nofglyphs,construction,kvariants,kparts,kitalic)
if coverage~=0 and nofglyphs>0 then
local coverage=readcoverage(f,offset+coverage,true)
for i=1,nofglyphs do
local c=construction[i]
if c~=0 then
local index=coverage[i]
local glyph=glyphs[index]
local math=glyph.math
setposition(f,offset+c)
local assembly=readushort(f)
local nofvariants=readushort(f)
if nofvariants>0 then
local variants,v=nil,0
for i=1,nofvariants do
local variant=readushort(f)
if variant==index then
elseif variants then
v=v+1
variants[v]=variant
else
v=1
variants={ variant }
end
skipshort(f)
end
if not variants then
elseif not math then
math={ [kvariants]=variants }
glyph.math=math
else
math[kvariants]=variants
end
end
if assembly~=0 then
setposition(f,offset+c+assembly)
local italic=readmathvalue(f)
local nofparts=readushort(f)
local parts={}
for i=1,nofparts do
local p={
glyph=readushort(f),
start=readushort(f),
["end"]=readushort(f),
advance=readushort(f),
}
local flags=readushort(f)
if bittest(flags,0x0001) then
p.extender=1
end
parts[i]=p
end
if not math then
math={
[kparts]=parts
}
glyph.math=math
else
math[kparts]=parts
end
if italic and italic~=0 then
math[kitalic]=italic
end
end
end
end
end
end
get(offset,vcoverage,vnofglyphs,vconstruction,"vvariants","vparts","vitalic")
get(offset,hcoverage,hnofglyphs,hconstruction,"hvariants","hparts","hitalic")
end
function readers.math(f,fontdata,specification)
if specification.glyphs then
local datatable=fontdata.tables.math
if datatable then
local tableoffset=datatable.offset
setposition(f,tableoffset)
local version=readulong(f)
if version~=0x00010000 then
report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"math",fontdata.filename)
return
end
local constants=readushort(f)
local glyphinfo=readushort(f)
local variants=readushort(f)
if constants==0 then
report("the math table of %a has no constants",fontdata.filename)
else
readmathconstants(f,fontdata,tableoffset+constants)
end
if glyphinfo~=0 then
readmathglyphinfo(f,fontdata,tableoffset+glyphinfo)
end
if variants~=0 then
readmathvariants(f,fontdata,tableoffset+variants)
end
end
end
end
function readers.colr(f,fontdata,specification)
local datatable=fontdata.tables.colr
if datatable then
if specification.glyphs then
local tableoffset=datatable.offset
setposition(f,tableoffset)
local version=readushort(f)
if version~=0 then
report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"colr",fontdata.filename)
return
end
if not fontdata.tables.cpal then
report("color table %a in font %a has no mandate %a table","colr",fontdata.filename,"cpal")
fontdata.colorpalettes={}
end
local glyphs=fontdata.glyphs
local nofglyphs=readushort(f)
local baseoffset=readulong(f)
local layeroffset=readulong(f)
local noflayers=readushort(f)
local layerrecords={}
local maxclass=0
setposition(f,tableoffset+layeroffset)
for i=1,noflayers do
local slot=readushort(f)
local class=readushort(f)
if class<0xFFFF then
class=class+1
if class>maxclass then
maxclass=class
end
end
layerrecords[i]={
slot=slot,
class=class,
}
end
fontdata.maxcolorclass=maxclass
setposition(f,tableoffset+baseoffset)
for i=0,nofglyphs-1 do
local glyphindex=readushort(f)
local firstlayer=readushort(f)
local noflayers=readushort(f)
local t={}
for i=1,noflayers do
t[i]=layerrecords[firstlayer+i]
end
glyphs[glyphindex].colors=t
end
end
fontdata.hascolor=true
end
end
function readers.cpal(f,fontdata,specification)
if specification.glyphs then
local datatable=fontdata.tables.cpal
if datatable then
local tableoffset=datatable.offset
setposition(f,tableoffset)
local version=readushort(f)
if version>1 then
report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"cpal",fontdata.filename)
return
end
local nofpaletteentries=readushort(f)
local nofpalettes=readushort(f)
local nofcolorrecords=readushort(f)
local firstcoloroffset=readulong(f)
local colorrecords={}
local palettes={}
for i=1,nofpalettes do
palettes[i]=readushort(f)
end
if version==1 then
local palettettypesoffset=readulong(f)
local palettelabelsoffset=readulong(f)
local paletteentryoffset=readulong(f)
end
setposition(f,tableoffset+firstcoloroffset)
for i=1,nofcolorrecords do
local b,g,r,a=readbytes(f,4)
colorrecords[i]={
r,g,b,a~=255 and a or nil,
}
end
for i=1,nofpalettes do
local p={}
local o=palettes[i]
for j=1,nofpaletteentries do
p[j]=colorrecords[o+j]
end
palettes[i]=p
end
fontdata.colorpalettes=palettes
end
end
end
function readers.svg(f,fontdata,specification)
local datatable=fontdata.tables.svg
if datatable then
if specification.glyphs then
local tableoffset=datatable.offset
setposition(f,tableoffset)
local version=readushort(f)
if version~=0 then
report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"svg",fontdata.filename)
return
end
local glyphs=fontdata.glyphs
local indexoffset=tableoffset+readulong(f)
local reserved=readulong(f)
setposition(f,indexoffset)
local nofentries=readushort(f)
local entries={}
for i=1,nofentries do
entries[i]={
first=readushort(f),
last=readushort(f),
offset=indexoffset+readulong(f),
length=readulong(f),
}
end
for i=1,nofentries do
local entry=entries[i]
setposition(f,entry.offset)
entries[i]={
first=entry.first,
last=entry.last,
data=readstring(f,entry.length)
}
end
fontdata.svgshapes=entries
end
fontdata.hascolor=true
end
end
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-dsp”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-oup” f7237130b648a4c2b477dabedc7f90e8] ---
if not modules then modules={} end modules ['font-oup']={
version=1.001,
comment="companion to font-ini.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local next,type=next,type
local P,R,S=lpeg.P,lpeg.R,lpeg.S
local lpegmatch=lpeg.match
local insert,remove,copy,unpack=table.insert,table.remove,table.copy,table.unpack
local formatters=string.formatters
local sortedkeys=table.sortedkeys
local sortedhash=table.sortedhash
local tohash=table.tohash
local report=logs.reporter("otf reader")
local trace_markwidth=false trackers.register("otf.markwidth",function(v) trace_markwidth=v end)
local readers=fonts.handlers.otf.readers
local privateoffset=fonts.constructors and fonts.constructors.privateoffset or 0xF0000
local f_private=formatters["P%05X"]
local f_unicode=formatters["U%05X"]
local f_index=formatters["I%05X"]
local f_character_y=formatters["%C"]
local f_character_n=formatters["[ %C ]"]
local check_duplicates=true
local check_soft_hyphen=false
directives.register("otf.checksofthyphen",function(v)
check_soft_hyphen=v
end)
local function replaced(list,index,replacement)
if type(list)=="number" then
return replacement
elseif type(replacement)=="table" then
local t={}
local n=index-1
for i=1,n do
t[i]=list[i]
end
for i=1,#replacement do
n=n+1
t[n]=replacement[i]
end
for i=index+1,#list do
n=n+1
t[n]=list[i]
end
else
list[index]=replacement
return list
end
end
local function unifyresources(fontdata,indices)
local descriptions=fontdata.descriptions
local resources=fontdata.resources
if not descriptions or not resources then
return
end
local variants=fontdata.resources.variants
if variants then
for selector,unicodes in next,variants do
for unicode,index in next,unicodes do
unicodes[unicode]=indices[index]
end
end
end
local function remark(marks)
if marks then
local newmarks={}
for k,v in next,marks do
local u=indices[k]
if u then
newmarks[u]=v
else
report("discarding mark %i",k)
end
end
return newmarks
end
end
local marks=resources.marks
if marks then
resources.marks=remark(marks)
end
local markclasses=resources.markclasses
if markclasses then
for class,marks in next,markclasses do
markclasses[class]=remark(marks)
end
end
local marksets=resources.marksets
if marksets then
for class,marks in next,marksets do
marksets[class]=remark(marks)
end
end
local done={}
local duplicates=check_duplicates and resources.duplicates
if duplicates and not next(duplicates) then
duplicates=false
end
local function recover(cover)
for i=1,#cover do
local c=cover[i]
if not done[c] then
local t={}
for k,v in next,c do
t[indices[k]]=v
end
cover[i]=t
done[c]=d
end
end
end
local function recursed(c)
local t={}
for g,d in next,c do
if type(d)=="table" then
t[indices[g]]=recursed(d)
else
t[g]=indices[d]
end
end
return t
end
local function unifythem(sequences)
if not sequences then
return
end
for i=1,#sequences do
local sequence=sequences[i]
local kind=sequence.type
local steps=sequence.steps
local features=sequence.features
if steps then
for i=1,#steps do
local step=steps[i]
if kind=="gsub_single" then
local c=step.coverage
if c then
local t1=done[c]
if not t1 then
t1={}
if duplicates then
for g1,d1 in next,c do
local ug1=indices[g1]
local ud1=indices[d1]
t1[ug1]=ud1
local dg1=duplicates[ug1]
if dg1 then
for u in next,dg1 do
t1[u]=ud1
end
end
end
else
for g1,d1 in next,c do
t1[indices[g1]]=indices[d1]
end
end
done[c]=t1
end
step.coverage=t1
end
elseif kind=="gpos_pair" then
local c=step.coverage
if c then
local t1=done[c]
if not t1 then
t1={}
for g1,d1 in next,c do
local t2=done[d1]
if not t2 then
t2={}
for g2,d2 in next,d1 do
t2[indices[g2]]=d2
end
done[d1]=t2
end
t1[indices[g1]]=t2
end
done[c]=t1
end
step.coverage=t1
end
elseif kind=="gsub_ligature" then
local c=step.coverage
if c then
step.coverage=recursed(c)
end
elseif kind=="gsub_alternate" or kind=="gsub_multiple" then
local c=step.coverage
if c then
local t1=done[c]
if not t1 then
t1={}
if duplicates then
for g1,d1 in next,c do
for i=1,#d1 do
d1[i]=indices[d1[i]]
end
local ug1=indices[g1]
t1[ug1]=d1
local dg1=duplicates[ug1]
if dg1 then
for u in next,dg1 do
t1[u]=copy(d1)
end
end
end
else
for g1,d1 in next,c do
for i=1,#d1 do
d1[i]=indices[d1[i]]
end
t1[indices[g1]]=d1
end
end
done[c]=t1
end
step.coverage=t1
end
elseif kind=="gpos_mark2base" or kind=="gpos_mark2mark" or kind=="gpos_mark2ligature" then
local c=step.coverage
if c then
local t1=done[c]
if not t1 then
t1={}
for g1,d1 in next,c do
t1[indices[g1]]=d1
end
done[c]=t1
end
step.coverage=t1
end
local c=step.baseclasses
if c then
local t1=done[c]
if not t1 then
for g1,d1 in next,c do
local t2=done[d1]
if not t2 then
t2={}
for g2,d2 in next,d1 do
t2[indices[g2]]=d2
end
done[d1]=t2
end
c[g1]=t2
end
done[c]=c
end
end
elseif kind=="gpos_single" then
local c=step.coverage
if c then
local t1=done[c]
if not t1 then
t1={}
if duplicates then
for g1,d1 in next,c do
local ug1=indices[g1]
t1[ug1]=d1
local dg1=duplicates[ug1]
if dg1 then
for u in next,dg1 do
t1[u]=d1
end
end
end
else
for g1,d1 in next,c do
t1[indices[g1]]=d1
end
end
done[c]=t1
end
step.coverage=t1
end
elseif kind=="gpos_cursive" then
local c=step.coverage
if c then
local t1=done[c]
if not t1 then
t1={}
if duplicates then
for g1,d1 in next,c do
local ug1=indices[g1]
t1[ug1]=d1
local dg1=duplicates[ug1]
if dg1 then
for u in next,dg1 do
t1[u]=copy(d1)
end
end
end
else
for g1,d1 in next,c do
t1[indices[g1]]=d1
end
end
done[c]=t1
end
step.coverage=t1
end
end
local rules=step.rules
if rules then
for i=1,#rules do
local rule=rules[i]
local before=rule.before if before then recover(before) end
local after=rule.after if after then recover(after) end
local current=rule.current if current then recover(current) end
local replacements=rule.replacements
if replacements then
if not done[replacements] then
local r={}
for k,v in next,replacements do
r[indices[k]]=indices[v]
end
rule.replacements=r
done[replacements]=r
end
end
end
end
end
end
end
end
unifythem(resources.sequences)
unifythem(resources.sublookups)
end
local function copyduplicates(fontdata)
if check_duplicates then
local descriptions=fontdata.descriptions
local resources=fontdata.resources
local duplicates=resources.duplicates
if check_soft_hyphen then
local ds=descriptions[0xAD]
if not ds or ds.width==0 then
if ds then
descriptions[0xAD]=nil
report("patching soft hyphen")
else
report("adding soft hyphen")
end
if not duplicates then
duplicates={}
resources.duplicates=duplicates
end
local dh=duplicates[0x2D]
if dh then
dh[#dh+1]={ [0xAD]=true }
else
duplicates[0x2D]={ [0xAD]=true }
end
end
end
if duplicates then
for u,d in next,duplicates do
local du=descriptions[u]
if du then
local t={ f_character_y(u),"@",f_index(du.index),"->" }
local n=0
local m=25
for u in next,d do
if descriptions[u] then
if n<m then
t[n+4]=f_character_n(u)
end
else
local c=copy(du)
c.unicode=u
descriptions[u]=c
if n<m then
t[n+4]=f_character_y(u)
end
end
n=n+1
end
if n<=m then
report("duplicates: %i : % t",n,t)
else
report("duplicates: %i : % t ...",n,t)
end
else
end
end
end
end
end
local ignore={
["notdef"]=true,
[".notdef"]=true,
["null"]=true,
[".null"]=true,
["nonmarkingreturn"]=true,
}
local function checklookups(fontdata,missing,nofmissing)
local descriptions=fontdata.descriptions
local resources=fontdata.resources
if missing and nofmissing and nofmissing<=0 then
return
end
local singles={}
local alternates={}
local ligatures={}
if not missing then
missing={}
nofmissing=0
for u,d in next,descriptions do
if not d.unicode then
nofmissing=nofmissing+1
missing[u]=true
end
end
end
local function collectthem(sequences)
if not sequences then
return
end
for i=1,#sequences do
local sequence=sequences[i]
local kind=sequence.type
local steps=sequence.steps
if steps then
for i=1,#steps do
local step=steps[i]
if kind=="gsub_single" then
local c=step.coverage
if c then
singles[#singles+1]=c
end
elseif kind=="gsub_alternate" then
local c=step.coverage
if c then
alternates[#alternates+1]=c
end
elseif kind=="gsub_ligature" then
local c=step.coverage
if c then
ligatures[#ligatures+1]=c
end
end
end
end
end
end
collectthem(resources.sequences)
collectthem(resources.sublookups)
local loops=0
while true do
loops=loops+1
local old=nofmissing
for i=1,#singles do
local c=singles[i]
for g1,g2 in next,c do
if missing[g1] then
local u2=descriptions[g2].unicode
if u2 then
missing[g1]=false
descriptions[g1].unicode=u2
nofmissing=nofmissing-1
end
end
if missing[g2] then
local u1=descriptions[g1].unicode
if u1 then
missing[g2]=false
descriptions[g2].unicode=u1
nofmissing=nofmissing-1
end
end
end
end
for i=1,#alternates do
local c=alternates[i]
for g1,d1 in next,c do
if missing[g1] then
for i=1,#d1 do
local g2=d1[i]
local u2=descriptions[g2].unicode
if u2 then
missing[g1]=false
descriptions[g1].unicode=u2
nofmissing=nofmissing-1
end
end
end
if not missing[g1] then
for i=1,#d1 do
local g2=d1[i]
if missing[g2] then
local u1=descriptions[g1].unicode
if u1 then
missing[g2]=false
descriptions[g2].unicode=u1
nofmissing=nofmissing-1
end
end
end
end
end
end
if nofmissing<=0 then
report("all done in %s loops",loops)
return
elseif old==nofmissing then
break
end
end
local t,n
local function recursed(c)
for g,d in next,c do
if g~="ligature" then
local u=descriptions[g].unicode
if u then
n=n+1
t[n]=u
recursed(d)
n=n-1
end
elseif missing[d] then
local l={}
local m=0
for i=1,n do
local u=t[i]
if type(u)=="table" then
for i=1,#u do
m=m+1
l[m]=u[i]
end
else
m=m+1
l[m]=u
end
end
missing[d]=false
descriptions[d].unicode=l
nofmissing=nofmissing-1
end
end
end
if nofmissing>0 then
t={}
n=0
local loops=0
while true do
loops=loops+1
local old=nofmissing
for i=1,#ligatures do
recursed(ligatures[i])
end
if nofmissing<=0 then
report("all done in %s loops",loops)
return
elseif old==nofmissing then
break
end
end
t=nil
n=0
end
if nofmissing>0 then
local done={}
for i,r in next,missing do
if r then
local data=descriptions[i]
local name=data and data.name or f_index(i)
if not ignore[name] then
done[name]=true
end
end
end
if next(done) then
report("not unicoded: % t",table.sortedkeys(done))
end
end
end
local function unifymissing(fontdata)
if not fonts.mappings then
require("font-map")
require("font-agl")
end
local unicodes={}
local private=fontdata.private
local resources=fontdata.resources
resources.unicodes=unicodes
for unicode,d in next,fontdata.descriptions do
if unicode<privateoffset then
local name=d.name
if name then
unicodes[name]=unicode
end
end
end
fonts.mappings.addtounicode(fontdata,fontdata.filename,checklookups)
resources.unicodes=nil
end
local function unifyglyphs(fontdata,usenames)
local private=fontdata.private or privateoffset
local glyphs=fontdata.glyphs
local indices={}
local descriptions={}
local names=usenames and {}
local resources=fontdata.resources
local zero=glyphs[0]
local zerocode=zero.unicode
if not zerocode then
zerocode=private
zero.unicode=zerocode
private=private+1
end
descriptions[zerocode]=zero
if names then
local name=glyphs[0].name or f_private(zerocode)
indices[0]=name
names[name]=zerocode
else
indices[0]=zerocode
end
for index=1,#glyphs do
local glyph=glyphs[index]
local unicode=glyph.unicode
if not unicode then
unicode=private
if names then
local name=glyph.name or f_private(unicode)
indices[index]=name
names[name]=unicode
else
indices[index]=unicode
end
private=private+1
elseif descriptions[unicode] then
report("assigning private unicode %U to glyph indexed %05X (%C)",private,index,unicode)
unicode=private
if names then
local name=glyph.name or f_private(unicode)
indices[index]=name
names[name]=unicode
else
indices[index]=unicode
end
private=private+1
else
if names then
local name=glyph.name or f_unicode(unicode)
indices[index]=name
names[name]=unicode
else
indices[index]=unicode
end
end
descriptions[unicode]=glyph
end
for index=1,#glyphs do
local math=glyphs[index].math
if math then
local list=math.vparts
if list then
for i=1,#list do local l=list[i] l.glyph=indices[l.glyph] end
end
local list=math.hparts
if list then
for i=1,#list do local l=list[i] l.glyph=indices[l.glyph] end
end
local list=math.vvariants
if list then
for i=1,#list do list[i]=indices[list[i]] end
end
local list=math.hvariants
if list then
for i=1,#list do list[i]=indices[list[i]] end
end
end
end
local colorpalettes=resources.colorpalettes
if colorpalettes then
for index=1,#glyphs do
local colors=glyphs[index].colors
if colors then
for i=1,#colors do
local c=colors[i]
c.slot=indices[c.slot]
end
end
end
end
fontdata.private=private
fontdata.glyphs=nil
fontdata.names=names
fontdata.descriptions=descriptions
fontdata.hashmethod=hashmethod
return indices,names
end
local p_bogusname=(
(P("uni")+P("UNI")+P("Uni")+P("U")+P("u"))*S("Xx")^0*R("09","AF")^1+(P("identity")+P("Identity")+P("IDENTITY"))*R("09","AF")^1+(P("index")+P("Index")+P("INDEX"))*R("09")^1
)*P(-1)
local function stripredundant(fontdata)
local descriptions=fontdata.descriptions
if descriptions then
local n=0
local c=0
for unicode,d in next,descriptions do
local name=d.name
if name and lpegmatch(p_bogusname,name) then
d.name=nil
n=n+1
end
if d.class=="base" then
d.class=nil
c=c+1
end
end
if n>0 then
report("%s bogus names removed (verbose unicode)",n)
end
if c>0 then
report("%s base class tags removed (default is base)",c)
end
end
end
function readers.getcomponents(fontdata)
local resources=fontdata.resources
if resources then
local sequences=resources.sequences
if sequences then
local collected={}
for i=1,#sequences do
local sequence=sequences[i]
if sequence.type=="gsub_ligature" then
local steps=sequence.steps
if steps then
local l={}
local function traverse(p,k,v)
if k=="ligature" then
collected[v]={ unpack(l) }
else
insert(l,k)
for k,vv in next,v do
traverse(p,k,vv)
end
remove(l)
end
end
for i=1,#steps do
local coverage=steps[i].coverage
if coverage then
for k,v in next,coverage do
traverse(k,k,v)
end
end
end
end
end
end
if next(collected) then
while true do
local done=false
for k,v in next,collected do
for i=1,#v do
local vi=v[i]
if vi==k then
collected[k]=nil
break
else
local c=collected[vi]
if c then
done=true
local t={}
local n=i-1
for j=1,n do
t[j]=v[j]
end
for j=1,#c do
n=n+1
t[n]=c[j]
end
for j=i+1,#v do
n=n+1
t[n]=v[j]
end
collected[k]=t
break
end
end
end
end
if not done then
break
end
end
return collected
end
end
end
end
readers.unifymissing=unifymissing
function readers.rehash(fontdata,hashmethod)
if not (fontdata and fontdata.glyphs) then
return
end
if hashmethod=="indices" then
fontdata.hashmethod="indices"
elseif hashmethod=="names" then
fontdata.hashmethod="names"
local indices=unifyglyphs(fontdata,true)
unifyresources(fontdata,indices)
copyduplicates(fontdata)
unifymissing(fontdata)
else
fontdata.hashmethod="unicode"
local indices=unifyglyphs(fontdata)
unifyresources(fontdata,indices)
copyduplicates(fontdata)
unifymissing(fontdata)
stripredundant(fontdata)
end
end
function readers.checkhash(fontdata)
local hashmethod=fontdata.hashmethod
if hashmethod=="unicodes" then
fontdata.names=nil
elseif hashmethod=="names" and fontdata.names then
unifyresources(fontdata,fontdata.names)
copyduplicates(fontdata)
fontdata.hashmethod="unicode"
fontdata.names=nil
else
readers.rehash(fontdata,"unicode")
end
end
function readers.addunicodetable(fontdata)
local resources=fontdata.resources
local unicodes=resources.unicodes
if not unicodes then
local descriptions=fontdata.descriptions
if descriptions then
unicodes={}
resources.unicodes=unicodes
for u,d in next,descriptions do
local n=d.name
if n then
unicodes[n]=u
end
end
end
end
end
local concat,sort=table.concat,table.sort
local next,type,tostring=next,type,tostring
local criterium=1
local threshold=0
local trace_packing=false trackers.register("otf.packing",function(v) trace_packing=v end)
local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end)
local report_otf=logs.reporter("fonts","otf loading")
local function tabstr_normal(t)
local s={}
local n=0
for k,v in next,t do
n=n+1
if type(v)=="table" then
s[n]=k..">"..tabstr_normal(v)
elseif v==true then
s[n]=k.."+"
elseif v then
s[n]=k.."="..v
else
s[n]=k.."-"
end
end
if n==0 then
return ""
elseif n==1 then
return s[1]
else
sort(s)
return concat(s,",")
end
end
local function tabstr_flat(t)
local s={}
local n=0
for k,v in next,t do
n=n+1
s[n]=k.."="..v
end
if n==0 then
return ""
elseif n==1 then
return s[1]
else
sort(s)
return concat(s,",")
end
end
local function tabstr_mixed(t)
local s={}
local n=#t
if n==0 then
return ""
elseif n==1 then
local k=t[1]
if k==true then
return "++"
elseif k==false then
return "--"
else
return tostring(k)
end
else
for i=1,n do
local k=t[i]
if k==true then
s[i]="++"
elseif k==false then
s[i]="--"
else
s[i]=k
end
end
return concat(s,",")
end
end
local function tabstr_boolean(t)
local s={}
local n=0
for k,v in next,t do
n=n+1
if v then
s[n]=k.."+"
else
s[n]=k.."-"
end
end
if n==0 then
return ""
elseif n==1 then
return s[1]
else
sort(s)
return concat(s,",")
end
end
function readers.pack(data)
if data then
local h,t,c={},{},{}
local hh,tt,cc={},{},{}
local nt,ntt=0,0
local function pack_normal(v)
local tag=tabstr_normal(v)
local ht=h[tag]
if ht then
c[ht]=c[ht]+1
return ht
else
nt=nt+1
t[nt]=v
h[tag]=nt
c[nt]=1
return nt
end
end
local function pack_flat(v)
local tag=tabstr_flat(v)
local ht=h[tag]
if ht then
c[ht]=c[ht]+1
return ht
else
nt=nt+1
t[nt]=v
h[tag]=nt
c[nt]=1
return nt
end
end
local function pack_boolean(v)
local tag=tabstr_boolean(v)
local ht=h[tag]
if ht then
c[ht]=c[ht]+1
return ht
else
nt=nt+1
t[nt]=v
h[tag]=nt
c[nt]=1
return nt
end
end
local function pack_indexed(v)
local tag=concat(v," ")
local ht=h[tag]
if ht then
c[ht]=c[ht]+1
return ht
else
nt=nt+1
t[nt]=v
h[tag]=nt
c[nt]=1
return nt
end
end
local function pack_mixed(v)
local tag=tabstr_mixed(v)
local ht=h[tag]
if ht then
c[ht]=c[ht]+1
return ht
else
nt=nt+1
t[nt]=v
h[tag]=nt
c[nt]=1
return nt
end
end
local function pack_final(v)
if c[v]<=criterium then
return t[v]
else
local hv=hh[v]
if hv then
return hv
else
ntt=ntt+1
tt[ntt]=t[v]
hh[v]=ntt
cc[ntt]=c[v]
return ntt
end
end
end
local function success(stage,pass)
if nt==0 then
if trace_loading or trace_packing then
report_otf("pack quality: nothing to pack")
end
return false
elseif nt>=threshold then
local one,two,rest=0,0,0
if pass==1 then
for k,v in next,c do
if v==1 then
one=one+1
elseif v==2 then
two=two+1
else
rest=rest+1
end
end
else
for k,v in next,cc do
if v>20 then
rest=rest+1
elseif v>10 then
two=two+1
else
one=one+1
end
end
data.tables=tt
end
if trace_loading or trace_packing then
report_otf("pack quality: stage %s, pass %s, %s packed, 1-10:%s, 11-20:%s, rest:%s (criterium: %s)",
stage,pass,one+two+rest,one,two,rest,criterium)
end
return true
else
if trace_loading or trace_packing then
report_otf("pack quality: stage %s, pass %s, %s packed, aborting pack (threshold: %s)",
stage,pass,nt,threshold)
end
return false
end
end
local function packers(pass)
if pass==1 then
return pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed
else
return pack_final,pack_final,pack_final,pack_final,pack_final
end
end
local resources=data.resources
local sequences=resources.sequences
local sublookups=resources.sublookups
local features=resources.features
local palettes=resources.colorpalettes
local chardata=characters and characters.data
local descriptions=data.descriptions or data.glyphs
if not descriptions then
return
end
for pass=1,2 do
if trace_packing then
report_otf("start packing: stage 1, pass %s",pass)
end
local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass)
for unicode,description in next,descriptions do
local boundingbox=description.boundingbox
if boundingbox then
description.boundingbox=pack_indexed(boundingbox)
end
local math=description.math
if math then
local kerns=math.kerns
if kerns then
for tag,kern in next,kerns do
kerns[tag]=pack_normal(kern)
end
end
end
end
local function packthem(sequences)
for i=1,#sequences do
local sequence=sequences[i]
local kind=sequence.type
local steps=sequence.steps
local order=sequence.order
local features=sequence.features
local flags=sequence.flags
if steps then
for i=1,#steps do
local step=steps[i]
if kind=="gpos_pair" then
local c=step.coverage
if c then
if step.format=="kern" then
for g1,d1 in next,c do
c[g1]=pack_normal(d1)
end
else
for g1,d1 in next,c do
for g2,d2 in next,d1 do
local f=d2[1] if f then d2[1]=pack_indexed(f) end
local s=d2[2] if s then d2[2]=pack_indexed(s) end
end
end
end
end
elseif kind=="gpos_single" then
local c=step.coverage
if c then
if step.format=="kern" then
step.coverage=pack_normal(c)
else
for g1,d1 in next,c do
c[g1]=pack_indexed(d1)
end
end
end
elseif kind=="gpos_cursive" then
local c=step.coverage
if c then
for g1,d1 in next,c do
local f=d1[2] if f then d1[2]=pack_indexed(f) end
local s=d1[3] if s then d1[3]=pack_indexed(s) end
end
end
elseif kind=="gpos_mark2base" or kind=="gpos_mark2mark" then
local c=step.baseclasses
if c then
for g1,d1 in next,c do
for g2,d2 in next,d1 do
d1[g2]=pack_indexed(d2)
end
end
end
local c=step.coverage
if c then
for g1,d1 in next,c do
d1[2]=pack_indexed(d1[2])
end
end
elseif kind=="gpos_mark2ligature" then
local c=step.baseclasses
if c then
for g1,d1 in next,c do
for g2,d2 in next,d1 do
for g3,d3 in next,d2 do
d2[g3]=pack_indexed(d3)
end
end
end
end
local c=step.coverage
if c then
for g1,d1 in next,c do
d1[2]=pack_indexed(d1[2])
end
end
end
local rules=step.rules
if rules then
for i=1,#rules do
local rule=rules[i]
local r=rule.before if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end
local r=rule.after if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end
local r=rule.current if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end
local r=rule.lookups if r then rule.lookups=pack_mixed (r) end
local r=rule.replacements if r then rule.replacements=pack_flat (r) end
end
end
end
end
if order then
sequence.order=pack_indexed(order)
end
if features then
for script,feature in next,features do
features[script]=pack_normal(feature)
end
end
if flags then
sequence.flags=pack_normal(flags)
end
end
end
if sequences then
packthem(sequences)
end
if sublookups then
packthem(sublookups)
end
if features then
for k,list in next,features do
for feature,spec in next,list do
list[feature]=pack_normal(spec)
end
end
end
if palettes then
for i=1,#palettes do
local p=palettes[i]
for j=1,#p do
p[j]=pack_indexed(p[j])
end
end
end
if not success(1,pass) then
return
end
end
if nt>0 then
for pass=1,2 do
if trace_packing then
report_otf("start packing: stage 2, pass %s",pass)
end
local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass)
for unicode,description in next,descriptions do
local math=description.math
if math then
local kerns=math.kerns
if kerns then
math.kerns=pack_normal(kerns)
end
end
end
local function packthem(sequences)
for i=1,#sequences do
local sequence=sequences[i]
local kind=sequence.type
local steps=sequence.steps
local features=sequence.features
if steps then
for i=1,#steps do
local step=steps[i]
if kind=="gpos_pair" then
local c=step.coverage
if c then
if step.format=="kern" then
else
for g1,d1 in next,c do
for g2,d2 in next,d1 do
d1[g2]=pack_normal(d2)
end
end
end
end
end
local rules=step.rules
if rules then
for i=1,#rules do
local rule=rules[i]
local r=rule.before if r then rule.before=pack_normal(r) end
local r=rule.after if r then rule.after=pack_normal(r) end
local r=rule.current if r then rule.current=pack_normal(r) end
end
end
end
end
if features then
sequence.features=pack_normal(features)
end
end
end
if sequences then
packthem(sequences)
end
if sublookups then
packthem(sublookups)
end
if not success(2,pass) then
end
end
for pass=1,2 do
if trace_packing then
report_otf("start packing: stage 3, pass %s",pass)
end
local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass)
local function packthem(sequences)
for i=1,#sequences do
local sequence=sequences[i]
local kind=sequence.type
local steps=sequence.steps
local features=sequence.features
if steps then
for i=1,#steps do
local step=steps[i]
if kind=="gpos_pair" then
local c=step.coverage
if c then
if step.format=="kern" then
else
for g1,d1 in next,c do
c[g1]=pack_normal(d1)
end
end
end
end
end
end
end
end
if sequences then
packthem(sequences)
end
if sublookups then
packthem(sublookups)
end
end
end
end
end
local unpacked_mt={
__index=function(t,k)
t[k]=false
return k
end
}
function readers.unpack(data)
if data then
local tables=data.tables
if tables then
local resources=data.resources
local descriptions=data.descriptions or data.glyphs
local sequences=resources.sequences
local sublookups=resources.sublookups
local features=resources.features
local palettes=resources.colorpalettes
local unpacked={}
setmetatable(unpacked,unpacked_mt)
for unicode,description in next,descriptions do
local tv=tables[description.boundingbox]
if tv then
description.boundingbox=tv
end
local math=description.math
if math then
local kerns=math.kerns
if kerns then
local tm=tables[kerns]
if tm then
math.kerns=tm
kerns=unpacked[tm]
end
if kerns then
for k,kern in next,kerns do
local tv=tables[kern]
if tv then
kerns[k]=tv
end
end
end
end
end
end
local function unpackthem(sequences)
for i=1,#sequences do
local sequence=sequences[i]
local kind=sequence.type
local steps=sequence.steps
local order=sequence.order
local features=sequence.features
local flags=sequence.flags
local markclass=sequence.markclass
if steps then
for i=1,#steps do
local step=steps[i]
if kind=="gpos_pair" then
local c=step.coverage
if c then
if step.format=="kern" then
for g1,d1 in next,c do
local tv=tables[d1]
if tv then
c[g1]=tv
end
end
else
for g1,d1 in next,c do
local tv=tables[d1]
if tv then
c[g1]=tv
d1=tv
end
for g2,d2 in next,d1 do
local tv=tables[d2]
if tv then
d1[g2]=tv
d2=tv
end
local f=tables[d2[1]] if f then d2[1]=f end
local s=tables[d2[2]] if s then d2[2]=s end
end
end
end
end
elseif kind=="gpos_single" then
local c=step.coverage
if c then
if step.format=="kern" then
local tv=tables[c]
if tv then
step.coverage=tv
end
else
for g1,d1 in next,c do
local tv=tables[d1]
if tv then
c[g1]=tv
end
end
end
end
elseif kind=="gpos_cursive" then
local c=step.coverage
if c then
for g1,d1 in next,c do
local f=tables[d1[2]] if f then d1[2]=f end
local s=tables[d1[3]] if s then d1[3]=s end
end
end
elseif kind=="gpos_mark2base" or kind=="gpos_mark2mark" then
local c=step.baseclasses
if c then
for g1,d1 in next,c do
for g2,d2 in next,d1 do
local tv=tables[d2]
if tv then
d1[g2]=tv
end
end
end
end
local c=step.coverage
if c then
for g1,d1 in next,c do
local tv=tables[d1[2]]
if tv then
d1[2]=tv
end
end
end
elseif kind=="gpos_mark2ligature" then
local c=step.baseclasses
if c then
for g1,d1 in next,c do
for g2,d2 in next,d1 do
for g3,d3 in next,d2 do
local tv=tables[d2[g3]]
if tv then
d2[g3]=tv
end
end
end
end
end
local c=step.coverage
if c then
for g1,d1 in next,c do
local tv=tables[d1[2]]
if tv then
d1[2]=tv
end
end
end
end
local rules=step.rules
if rules then
for i=1,#rules do
local rule=rules[i]
local before=rule.before
if before then
local tv=tables[before]
if tv then
rule.before=tv
before=tv
end
for i=1,#before do
local tv=tables[before[i]]
if tv then
before[i]=tv
end
end
end
local after=rule.after
if after then
local tv=tables[after]
if tv then
rule.after=tv
after=tv
end
for i=1,#after do
local tv=tables[after[i]]
if tv then
after[i]=tv
end
end
end
local current=rule.current
if current then
local tv=tables[current]
if tv then
rule.current=tv
current=tv
end
for i=1,#current do
local tv=tables[current[i]]
if tv then
current[i]=tv
end
end
end
local lookups=rule.lookups
if lookups then
local tv=tables[lookups]
if tv then
rule.lookups=tv
end
end
local replacements=rule.replacements
if replacements then
local tv=tables[replacements]
if tv then
rule.replacements=tv
end
end
end
end
end
end
if features then
local tv=tables[features]
if tv then
sequence.features=tv
features=tv
end
for script,feature in next,features do
local tv=tables[feature]
if tv then
features[script]=tv
end
end
end
if order then
local tv=tables[order]
if tv then
sequence.order=tv
end
end
if flags then
local tv=tables[flags]
if tv then
sequence.flags=tv
end
end
end
end
if sequences then
unpackthem(sequences)
end
if sublookups then
unpackthem(sublookups)
end
if features then
for k,list in next,features do
for feature,spec in next,list do
local tv=tables[spec]
if tv then
list[feature]=tv
end
end
end
end
if palettes then
for i=1,#palettes do
local p=palettes[i]
for j=1,#p do
local tv=tables[p[j]]
if tv then
p[j]=tv
end
end
end
end
data.tables=nil
end
end
end
local mt={
__index=function(t,k)
if k=="height" then
local ht=t.boundingbox[4]
return ht<0 and 0 or ht
elseif k=="depth" then
local dp=-t.boundingbox[2]
return dp<0 and 0 or dp
elseif k=="width" then
return 0
elseif k=="name" then
return forcenotdef and ".notdef"
end
end
}
local function sameformat(sequence,steps,first,nofsteps,kind)
return true
end
local function mergesteps_1(lookup,strict)
local steps=lookup.steps
local nofsteps=lookup.nofsteps
local first=steps[1]
if strict then
local f=first.format
for i=2,nofsteps do
if steps[i].format~=f then
report("not merging %a steps of %a lookup %a, different formats",nofsteps,lookup.type,lookup.name)
return 0
end
end
end
report("merging %a steps of %a lookup %a",nofsteps,lookup.type,lookup.name)
local target=first.coverage
for i=2,nofsteps do
for k,v in next,steps[i].coverage do
if not target[k] then
target[k]=v
end
end
end
lookup.nofsteps=1
lookup.merged=true
lookup.steps={ first }
return nofsteps-1
end
local function mergesteps_2(lookup,strict)
local steps=lookup.steps
local nofsteps=lookup.nofsteps
local first=steps[1]
if strict then
local f=first.format
for i=2,nofsteps do
if steps[i].format~=f then
report("not merging %a steps of %a lookup %a, different formats",nofsteps,lookup.type,lookup.name)
return 0
end
end
end
report("merging %a steps of %a lookup %a",nofsteps,lookup.type,lookup.name)
local target=first.coverage
for i=2,nofsteps do
for k,v in next,steps[i].coverage do
local tk=target[k]
if tk then
for k,v in next,v do
if not tk[k] then
tk[k]=v
end
end
else
target[k]=v
end
end
end
lookup.nofsteps=1
lookup.steps={ first }
return nofsteps-1
end
local function mergesteps_3(lookup,strict)
local steps=lookup.steps
local nofsteps=lookup.nofsteps
local first=steps[1]
report("merging %a steps of %a lookup %a",nofsteps,lookup.type,lookup.name)
local baseclasses={}
local coverage={}
local used={}
for i=1,nofsteps do
local offset=i*10
local step=steps[i]
for k,v in sortedhash(step.baseclasses) do
baseclasses[offset+k]=v
end
for k,v in next,step.coverage do
local tk=coverage[k]
if tk then
for k,v in next,v do
if not tk[k] then
tk[k]=v
local c=offset+v[1]
v[1]=c
if not used[c] then
used[c]=true
end
end
end
else
coverage[k]=v
local c=offset+v[1]
v[1]=c
if not used[c] then
used[c]=true
end
end
end
end
for k,v in next,baseclasses do
if not used[k] then
baseclasses[k]=nil
report("discarding not used baseclass %i",k)
end
end
first.baseclasses=baseclasses
first.coverage=coverage
lookup.nofsteps=1
lookup.steps={ first }
return nofsteps-1
end
local function nested(old,new)
for k,v in next,old do
if k=="ligature" then
if not new.ligature then
new.ligature=v
end
else
local n=new[k]
if n then
nested(v,n)
else
new[k]=v
end
end
end
end
local function mergesteps_4(lookup)
local steps=lookup.steps
local nofsteps=lookup.nofsteps
local first=steps[1]
report("merging %a steps of %a lookup %a",nofsteps,lookup.type,lookup.name)
local target=first.coverage
for i=2,nofsteps do
for k,v in next,steps[i].coverage do
local tk=target[k]
if tk then
nested(v,tk)
else
target[k]=v
end
end
end
lookup.nofsteps=1
lookup.steps={ first }
return nofsteps-1
end
local function checkkerns(lookup)
local steps=lookup.steps
local nofsteps=lookup.nofsteps
for i=1,nofsteps do
local step=steps[i]
if step.format=="pair" then
local coverage=step.coverage
local kerns=true
for g1,d1 in next,coverage do
if d1[1]~=0 or d1[2]~=0 or d1[4]~=0 then
kerns=false
break
end
end
if kerns then
report("turning pairs of step %a of %a lookup %a into kerns",i,lookup.type,lookup.name)
for g1,d1 in next,coverage do
coverage[g1]=d1[3]
end
step.format="kern"
end
end
end
end
local function checkpairs(lookup)
local steps=lookup.steps
local nofsteps=lookup.nofsteps
local kerned=0
for i=1,nofsteps do
local step=steps[i]
if step.format=="pair" then
local coverage=step.coverage
local kerns=true
for g1,d1 in next,coverage do
for g2,d2 in next,d1 do
if d2[2] then
kerns=false
break
else
local v=d2[1]
if v[1]~=0 or v[2]~=0 or v[4]~=0 then
kerns=false
break
end
end
end
end
if kerns then
report("turning pairs of step %a of %a lookup %a into kerns",i,lookup.type,lookup.name)
for g1,d1 in next,coverage do
for g2,d2 in next,d1 do
d1[g2]=d2[1][3]
end
end
step.format="kern"
kerned=kerned+1
end
end
end
return kerned
end
function readers.compact(data)
if not data or data.compacted then
return
else
data.compacted=true
end
local resources=data.resources
local merged=0
local kerned=0
local allsteps=0
local function compact(what)
local lookups=resources[what]
if lookups then
for i=1,#lookups do
local lookup=lookups[i]
local nofsteps=lookup.nofsteps
allsteps=allsteps+nofsteps
if nofsteps>1 then
local kind=lookup.type
if kind=="gsub_single" or kind=="gsub_alternate" or kind=="gsub_multiple" then
merged=merged+mergesteps_1(lookup)
elseif kind=="gsub_ligature" then
merged=merged+mergesteps_4(lookup)
elseif kind=="gpos_single" then
merged=merged+mergesteps_1(lookup,true)
checkkerns(lookup)
elseif kind=="gpos_pair" then
merged=merged+mergesteps_2(lookup,true)
kerned=kerned+checkpairs(lookup)
elseif kind=="gpos_cursive" then
merged=merged+mergesteps_2(lookup)
elseif kind=="gpos_mark2mark" or kind=="gpos_mark2base" or kind=="gpos_mark2ligature" then
merged=merged+mergesteps_3(lookup)
end
end
end
else
report("no lookups in %a",what)
end
end
compact("sequences")
compact("sublookups")
if merged>0 then
report("%i steps of %i removed due to merging",merged,allsteps)
end
if kerned>0 then
report("%i steps of %i steps turned from pairs into kerns",kerned,allsteps)
end
end
function readers.expand(data)
if not data or data.expanded then
return
else
data.expanded=true
end
local resources=data.resources
local sublookups=resources.sublookups
local sequences=resources.sequences
local markclasses=resources.markclasses
local descriptions=data.descriptions
if descriptions then
local defaultwidth=resources.defaultwidth or 0
local defaultheight=resources.defaultheight or 0
local defaultdepth=resources.defaultdepth or 0
local basename=trace_markwidth and file.basename(resources.filename)
for u,d in next,descriptions do
local bb=d.boundingbox
local wd=d.width
if not wd then
d.width=defaultwidth
elseif trace_markwidth and wd~=0 and d.class=="mark" then
report("mark %a with width %b found in %a",d.name or "<noname>",wd,basename)
end
if bb then
local ht=bb[4]
local dp=-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
local function expandlookups(sequences)
if sequences then
for i=1,#sequences do
local sequence=sequences[i]
local steps=sequence.steps
if steps then
local kind=sequence.type
local markclass=sequence.markclass
if markclass then
if not markclasses then
report_warning("missing markclasses")
sequence.markclass=false
else
sequence.markclass=markclasses[markclass]
end
end
for i=1,sequence.nofsteps do
local step=steps[i]
local baseclasses=step.baseclasses
if baseclasses then
local coverage=step.coverage
for k,v in next,coverage do
v[1]=baseclasses[v[1]]
end
elseif kind=="gpos_cursive" then
local coverage=step.coverage
for k,v in next,coverage do
v[1]=coverage
end
end
local rules=step.rules
if rules then
local rulehash={}
local rulesize=0
local coverage={}
local lookuptype=sequence.type
step.coverage=coverage
for nofrules=1,#rules do
local rule=rules[nofrules]
local current=rule.current
local before=rule.before
local after=rule.after
local replacements=rule.replacements or false
local sequence={}
local nofsequences=0
if before then
for n=1,#before do
nofsequences=nofsequences+1
sequence[nofsequences]=before[n]
end
end
local start=nofsequences+1
for n=1,#current do
nofsequences=nofsequences+1
sequence[nofsequences]=current[n]
end
local stop=nofsequences
if after then
for n=1,#after do
nofsequences=nofsequences+1
sequence[nofsequences]=after[n]
end
end
local lookups=rule.lookups or false
local subtype=nil
if lookups then
for k,v in next,lookups do
local lookup=sublookups[v]
if lookup then
lookups[k]=lookup
if not subtype then
subtype=lookup.type
end
else
end
end
end
if sequence[1] then
rulesize=rulesize+1
rulehash[rulesize]={
nofrules,
lookuptype,
sequence,
start,
stop,
lookups,
replacements,
subtype,
}
for unic in next,sequence[start] do
local cu=coverage[unic]
if not cu then
coverage[unic]=rulehash
end
end
end
end
end
end
end
end
end
end
expandlookups(sequences)
expandlookups(sublookups)
end
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-oup”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-otl” 2e7c8d9a331c46826211bd507f8e488a] ---
if not modules then modules={} end modules ['font-otl']={
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 gmatch,find,match,lower,strip=string.gmatch,string.find,string.match,string.lower,string.strip
local type,next,tonumber,tostring,unpack=type,next,tonumber,tostring,unpack
local abs=math.abs
local derivetable=table.derive
local formatters=string.formatters
local setmetatableindex=table.setmetatableindex
local allocate=utilities.storage.allocate
local registertracker=trackers.register
local registerdirective=directives.register
local starttiming=statistics.starttiming
local stoptiming=statistics.stoptiming
local elapsedtime=statistics.elapsedtime
local findbinfile=resolvers.findbinfile
local trace_loading=false registertracker("otf.loading",function(v) trace_loading=v end)
local trace_features=false registertracker("otf.features",function(v) trace_features=v end)
local trace_defining=false registertracker("fonts.defining",function(v) trace_defining=v end)
local report_otf=logs.reporter("fonts","otf loading")
local fonts=fonts
local otf=fonts.handlers.otf
otf.version=3.027
otf.cache=containers.define("fonts","otl",otf.version,true)
otf.svgcache=containers.define("fonts","svg",otf.version,true)
otf.pdfcache=containers.define("fonts","pdf",otf.version,true)
otf.svgenabled=false
local otfreaders=otf.readers
local hashes=fonts.hashes
local definers=fonts.definers
local readers=fonts.readers
local constructors=fonts.constructors
local otffeatures=constructors.features.otf
local registerotffeature=otffeatures.register
local otfenhancers=constructors.enhancers.otf
local registerotfenhancer=otfenhancers.register
local forceload=false
local cleanup=0
local syncspace=true
local forcenotdef=false
local applyruntimefixes=fonts.treatments and fonts.treatments.applyfixes
local wildcard="*"
local default="dflt"
local formats=fonts.formats
formats.otf="opentype"
formats.ttf="truetype"
formats.ttc="truetype"
registerdirective("fonts.otf.loader.cleanup",function(v) cleanup=tonumber(v) or (v and 1) or 0 end)
registerdirective("fonts.otf.loader.force",function(v) forceload=v end)
registerdirective("fonts.otf.loader.syncspace",function(v) syncspace=v end)
registerdirective("fonts.otf.loader.forcenotdef",function(v) forcenotdef=v end)
registerotfenhancer("check extra features",function() end)
function otf.load(filename,sub,featurefile)
local featurefile=nil
local base=file.basename(file.removesuffix(filename))
local name=file.removesuffix(base)
local attr=lfs.attributes(filename)
local size=attr and attr.size or 0
local time=attr and attr.modification or 0
if featurefile then
name=name.."@"..file.removesuffix(file.basename(featurefile))
end
if sub=="" then
sub=false
end
local hash=name
if sub then
hash=hash.."-"..sub
end
hash=containers.cleanname(hash)
local featurefiles
if featurefile then
featurefiles={}
for s in gmatch(featurefile,"[^,]+") do
local name=resolvers.findfile(file.addsuffix(s,'fea'),'fea') or ""
if name=="" then
report_otf("loading error, no featurefile %a",s)
else
local attr=lfs.attributes(name)
featurefiles[#featurefiles+1]={
name=name,
size=attr and attr.size or 0,
time=attr and attr.modification or 0,
}
end
end
if #featurefiles==0 then
featurefiles=nil
end
end
local data=containers.read(otf.cache,hash)
local reload=not data or data.size~=size or data.time~=time or data.tableversion~=otfreaders.tableversion
if forceload then
report_otf("forced reload of %a due to hard coded flag",filename)
reload=true
end
if reload then
report_otf("loading %a, hash %a",filename,hash)
starttiming(otfreaders)
data=otfreaders.loadfont(filename,sub or 1)
if data then
local resources=data.resources
local svgshapes=resources.svgshapes
if svgshapes then
resources.svgshapes=nil
if otf.svgenabled then
local timestamp=os.date()
containers.write(otf.svgcache,hash,{
svgshapes=svgshapes,
timestamp=timestamp,
})
data.properties.svg={
hash=hash,
timestamp=timestamp,
}
end
end
otfreaders.compact(data)
otfreaders.rehash(data,"unicodes")
otfreaders.addunicodetable(data)
otfreaders.extend(data)
otfreaders.pack(data)
report_otf("loading done")
report_otf("saving %a in cache",filename)
data=containers.write(otf.cache,hash,data)
if cleanup>1 then
collectgarbage("collect")
end
stoptiming(otfreaders)
if elapsedtime then
report_otf("loading, optimizing, packing and caching time %s",elapsedtime(otfreaders))
end
if cleanup>3 then
collectgarbage("collect")
end
data=containers.read(otf.cache,hash)
if cleanup>2 then
collectgarbage("collect")
end
else
data=nil
report_otf("loading failed due to read error")
end
end
if data then
if trace_defining then
report_otf("loading from cache using hash %a",hash)
end
otfreaders.unpack(data)
otfreaders.expand(data)
otfreaders.addunicodetable(data)
otfenhancers.apply(data,filename,data)
if applyruntimefixes then
applyruntimefixes(filename,data)
end
data.metadata.math=data.resources.mathconstants
end
return data
end
function otf.setfeatures(tfmdata,features)
local okay=constructors.initializefeatures("otf",tfmdata,features,trace_features,report_otf)
if okay then
return constructors.collectprocessors("otf",tfmdata,features,trace_features,report_otf)
else
return {}
end
end
local function copytotfm(data,cache_id)
if data then
local metadata=data.metadata
local properties=derivetable(data.properties)
local descriptions=derivetable(data.descriptions)
local goodies=derivetable(data.goodies)
local characters={}
local parameters={}
local mathparameters={}
local resources=data.resources
local unicodes=resources.unicodes
local spaceunits=500
local spacer="space"
local designsize=metadata.designsize or 100
local minsize=metadata.minsize or designsize
local maxsize=metadata.maxsize or designsize
local mathspecs=metadata.math
if designsize==0 then
designsize=100
minsize=100
maxsize=100
end
if mathspecs then
for name,value in next,mathspecs do
mathparameters[name]=value
end
end
for unicode in next,data.descriptions do
characters[unicode]={}
end
if mathspecs then
for unicode,character in next,characters do
local d=descriptions[unicode]
local m=d.math
if m then
local italic=m.italic
local vitalic=m.vitalic
local variants=m.hvariants
local parts=m.hparts
if variants then
local c=character
for i=1,#variants do
local un=variants[i]
c.next=un
c=characters[un]
end
c.horiz_variants=parts
elseif parts then
character.horiz_variants=parts
italic=m.hitalic
end
local variants=m.vvariants
local parts=m.vparts
if variants then
local c=character
for i=1,#variants do
local un=variants[i]
c.next=un
c=characters[un]
end
c.vert_variants=parts
elseif parts then
character.vert_variants=parts
end
if italic and italic~=0 then
character.italic=italic
end
if vitalic and vitalic~=0 then
character.vert_italic=vitalic
end
local accent=m.accent
if accent then
character.accent=accent
end
local kerns=m.kerns
if kerns then
character.mathkerns=kerns
end
end
end
end
local filename=constructors.checkedfilename(resources)
local fontname=metadata.fontname
local fullname=metadata.fullname or fontname
local psname=fontname or fullname
local units=metadata.units or 1000
if units==0 then
units=1000
metadata.units=1000
report_otf("changing %a units to %a",0,units)
end
local monospaced=metadata.monospaced
local charwidth=metadata.averagewidth
local charxheight=metadata.xheight
local italicangle=metadata.italicangle
local hasitalics=metadata.hasitalics
properties.monospaced=monospaced
properties.hasitalics=hasitalics
parameters.italicangle=italicangle
parameters.charwidth=charwidth
parameters.charxheight=charxheight
local space=0x0020
local emdash=0x2014
if monospaced then
if descriptions[space] then
spaceunits,spacer=descriptions[space].width,"space"
end
if not spaceunits and descriptions[emdash] then
spaceunits,spacer=descriptions[emdash].width,"emdash"
end
if not spaceunits and charwidth then
spaceunits,spacer=charwidth,"charwidth"
end
else
if descriptions[space] then
spaceunits,spacer=descriptions[space].width,"space"
end
if not spaceunits and descriptions[emdash] then
spaceunits,spacer=descriptions[emdash].width/2,"emdash/2"
end
if not spaceunits and charwidth then
spaceunits,spacer=charwidth,"charwidth"
end
end
spaceunits=tonumber(spaceunits) or units/2
parameters.slant=0
parameters.space=spaceunits
parameters.space_stretch=1*units/2
parameters.space_shrink=1*units/3
parameters.x_height=2*units/5
parameters.quad=units
if spaceunits<2*units/5 then
end
if italicangle and italicangle~=0 then
parameters.italicangle=italicangle
parameters.italicfactor=math.cos(math.rad(90+italicangle))
parameters.slant=- math.tan(italicangle*math.pi/180)
end
if monospaced then
parameters.space_stretch=0
parameters.space_shrink=0
elseif syncspace then
parameters.space_stretch=spaceunits/2
parameters.space_shrink=spaceunits/3
end
parameters.extra_space=parameters.space_shrink
if charxheight then
parameters.x_height=charxheight
else
local x=0x0078
if x then
local x=descriptions[x]
if x then
parameters.x_height=x.height
end
end
end
parameters.designsize=(designsize/10)*65536
parameters.minsize=(minsize/10)*65536
parameters.maxsize=(maxsize/10)*65536
parameters.ascender=abs(metadata.ascender or 0)
parameters.descender=abs(metadata.descender or 0)
parameters.units=units
properties.space=spacer
properties.encodingbytes=2
properties.format=data.format or formats.otf
properties.noglyphnames=true
properties.filename=filename
properties.fontname=fontname
properties.fullname=fullname
properties.psname=psname
properties.name=filename or fullname
return {
characters=characters,
descriptions=descriptions,
parameters=parameters,
mathparameters=mathparameters,
resources=resources,
properties=properties,
goodies=goodies,
}
end
end
local converters={
woff={
cachename="webfonts",
action=otf.readers.woff2otf,
}
}
local function checkconversion(specification)
local filename=specification.filename
local converter=converters[lower(file.suffix(filename))]
if converter then
local base=file.basename(filename)
local name=file.removesuffix(base)
local attr=lfs.attributes(filename)
local size=attr and attr.size or 0
local time=attr and attr.modification or 0
if size>0 then
local cleanname=containers.cleanname(name)
local cachename=caches.setfirstwritablefile(cleanname,converter.cachename)
if not io.exists(cachename) or (time~=lfs.attributes(cachename).modification) then
report_otf("caching font %a in %a",filename,cachename)
converter.action(filename,cachename)
lfs.touch(cachename,time,time)
end
specification.filename=cachename
end
end
end
local function otftotfm(specification)
local cache_id=specification.hash
local tfmdata=containers.read(constructors.cache,cache_id)
if not tfmdata then
checkconversion(specification)
local name=specification.name
local sub=specification.sub
local subindex=specification.subindex
local filename=specification.filename
local features=specification.features.normal
local rawdata=otf.load(filename,sub,features and features.featurefile)
if rawdata and next(rawdata) then
local descriptions=rawdata.descriptions
rawdata.lookuphash={}
tfmdata=copytotfm(rawdata,cache_id)
if tfmdata and next(tfmdata) then
local features=constructors.checkedfeatures("otf",features)
local shared=tfmdata.shared
if not shared then
shared={}
tfmdata.shared=shared
end
shared.rawdata=rawdata
shared.dynamics={}
tfmdata.changed={}
shared.features=features
shared.processes=otf.setfeatures(tfmdata,features)
end
end
containers.write(constructors.cache,cache_id,tfmdata)
end
return tfmdata
end
local function read_from_otf(specification)
local tfmdata=otftotfm(specification)
if tfmdata then
tfmdata.properties.name=specification.name
tfmdata.properties.sub=specification.sub
tfmdata=constructors.scale(tfmdata,specification)
local allfeatures=tfmdata.shared.features or specification.features.normal
constructors.applymanipulators("otf",tfmdata,allfeatures,trace_features,report_otf)
constructors.setname(tfmdata,specification)
fonts.loggers.register(tfmdata,file.suffix(specification.filename),specification)
end
return tfmdata
end
local function checkmathsize(tfmdata,mathsize)
local mathdata=tfmdata.shared.rawdata.metadata.math
local mathsize=tonumber(mathsize)
if mathdata then
local parameters=tfmdata.parameters
parameters.scriptpercentage=mathdata.ScriptPercentScaleDown
parameters.scriptscriptpercentage=mathdata.ScriptScriptPercentScaleDown
parameters.mathsize=mathsize
end
end
registerotffeature {
name="mathsize",
description="apply mathsize specified in the font",
initializers={
base=checkmathsize,
node=checkmathsize,
}
}
function otf.collectlookups(rawdata,kind,script,language)
if not kind then
return
end
if not script then
script=default
end
if not language then
language=default
end
local lookupcache=rawdata.lookupcache
if not lookupcache then
lookupcache={}
rawdata.lookupcache=lookupcache
end
local kindlookup=lookupcache[kind]
if not kindlookup then
kindlookup={}
lookupcache[kind]=kindlookup
end
local scriptlookup=kindlookup[script]
if not scriptlookup then
scriptlookup={}
kindlookup[script]=scriptlookup
end
local languagelookup=scriptlookup[language]
if not languagelookup then
local sequences=rawdata.resources.sequences
local featuremap={}
local featurelist={}
if sequences then
for s=1,#sequences do
local sequence=sequences[s]
local features=sequence.features
if features then
features=features[kind]
if features then
features=features[script] or features[wildcard]
if features then
features=features[language] or features[wildcard]
if features then
if not featuremap[sequence] then
featuremap[sequence]=true
featurelist[#featurelist+1]=sequence
end
end
end
end
end
end
if #featurelist==0 then
featuremap,featurelist=false,false
end
else
featuremap,featurelist=false,false
end
languagelookup={ featuremap,featurelist }
scriptlookup[language]=languagelookup
end
return unpack(languagelookup)
end
local function getgsub(tfmdata,k,kind,value)
local shared=tfmdata.shared
local rawdata=shared and shared.rawdata
if rawdata then
local sequences=rawdata.resources.sequences
if sequences then
local properties=tfmdata.properties
local validlookups,lookuplist=otf.collectlookups(rawdata,kind,properties.script,properties.language)
if validlookups then
for i=1,#lookuplist do
local lookup=lookuplist[i]
local steps=lookup.steps
local nofsteps=lookup.nofsteps
for i=1,nofsteps do
local coverage=steps[i].coverage
if coverage then
local found=coverage[k]
if found then
return found,lookup.type
end
end
end
end
end
end
end
end
otf.getgsub=getgsub
function otf.getsubstitution(tfmdata,k,kind,value)
local found,kind=getgsub(tfmdata,k,kind,value)
if not found then
elseif kind=="gsub_single" then
return found
elseif kind=="gsub_alternate" then
local choice=tonumber(value) or 1
return found[choice] or found[1] or k
end
return k
end
otf.getalternate=otf.getsubstitution
function otf.getmultiple(tfmdata,k,kind)
local found,kind=getgsub(tfmdata,k,kind)
if found and kind=="gsub_multiple" then
return found
end
return { k }
end
function otf.getkern(tfmdata,left,right,kind)
local kerns=getgsub(tfmdata,left,kind or "kern",true)
if kerns then
local found=kerns[right]
local kind=type(found)
if kind=="table" then
found=found[1][3]
elseif kind~="number" then
found=false
end
if found then
return found*tfmdata.parameters.factor
end
end
return 0
end
local function check_otf(forced,specification,suffix)
local name=specification.name
if forced then
name=specification.forcedname
end
local fullname=findbinfile(name,suffix) or ""
if fullname=="" then
fullname=fonts.names.getfilename(name,suffix) or ""
end
if fullname~="" and not fonts.names.ignoredfile(fullname) then
specification.filename=fullname
return read_from_otf(specification)
end
end
local function opentypereader(specification,suffix)
local forced=specification.forced or ""
if formats[forced] then
return check_otf(true,specification,forced)
else
return check_otf(false,specification,suffix)
end
end
readers.opentype=opentypereader
function readers.otf(specification) return opentypereader(specification,"otf") end
function readers.ttf(specification) return opentypereader(specification,"ttf") end
function readers.ttc(specification) return opentypereader(specification,"ttf") end
function readers.woff(specification)
checkconversion(specification)
opentypereader(specification,"")
end
function otf.scriptandlanguage(tfmdata,attr)
local properties=tfmdata.properties
return properties.script or "dflt",properties.language or "dflt"
end
local function justset(coverage,unicode,replacement)
coverage[unicode]=replacement
end
otf.coverup={
stepkey="steps",
actions={
chainsubstitution=justset,
chainposition=justset,
substitution=justset,
alternate=justset,
multiple=justset,
kern=justset,
pair=justset,
ligature=function(coverage,unicode,ligature)
local first=ligature[1]
local tree=coverage[first]
if not tree then
tree={}
coverage[first]=tree
end
for i=2,#ligature do
local l=ligature[i]
local t=tree[l]
if not t then
t={}
tree[l]=t
end
tree=t
end
tree.ligature=unicode
end,
},
register=function(coverage,featuretype,format)
return {
format=format,
coverage=coverage,
}
end
}
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-otl”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-oto” 5fbdd899624d4eef639f81b580afe9aa] ---
if not modules then modules = { } end modules ['font-oto'] = { -- original tex
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, unpack = table.concat, table.unpack
local insert, remove = 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, rawget = type, next, tonumber, tostring, rawget
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)
----- trace_ligatures_detail = false trackers.register("otf.ligatures.detail", function(v) trace_ligatures_detail = v end)
local trace_kerns = false trackers.register("otf.kerns", function(v) trace_kerns = v end)
local trace_preparing = false trackers.register("otf.preparing", function(v) trace_preparing = v end)
local report_prepare = logs.reporter("fonts","otf prepare")
local fonts = fonts
local otf = fonts.handlers.otf
local otffeatures = otf.features
local registerotffeature = otffeatures.register
otf.defaultbasealternate = "none" -- first last
local wildcard = "*"
local default = "dflt"
local formatters = string.formatters
local f_unicode = formatters["%U"]
local f_uniname = formatters["%U (%s)"]
local f_unilist = formatters["% t (% t)"]
local function gref(descriptions,n)
if type(n) == "number" then
local name = descriptions[n].name
if name then
return f_uniname(n,name)
else
return f_unicode(n)
end
elseif n then
local num, nam, j = { }, { }, 0
for i=1,#n do
local ni = n[i]
if tonumber(ni) then -- first is likely a key
j = j + 1
local di = descriptions[ni]
num[j] = f_unicode(ni)
nam[j] = di and di.name or "-"
end
end
return f_unilist(num,nam)
else
return "<error in base mode tracing>"
end
end
local function cref(feature,sequence)
return formatters["feature %a, type %a, chain lookup %a"](feature,sequence.type,sequence.name)
end
local function report_substitution(feature,sequence,descriptions,unicode,substitution)
if unicode == substitution then
report_prepare("%s: base substitution %s maps onto itself",
cref(feature,sequence),
gref(descriptions,unicode))
else
report_prepare("%s: base substitution %s => %S",
cref(feature,sequence),
gref(descriptions,unicode),
gref(descriptions,substitution))
end
end
local function report_alternate(feature,sequence,descriptions,unicode,replacement,value,comment)
if unicode == replacement then
report_prepare("%s: base alternate %s maps onto itself",
cref(feature,sequence),
gref(descriptions,unicode))
else
report_prepare("%s: base alternate %s => %s (%S => %S)",
cref(feature,sequence),
gref(descriptions,unicode),
replacement and gref(descriptions,replacement),
value,
comment)
end
end
local function report_ligature(feature,sequence,descriptions,unicode,ligature)
report_prepare("%s: base ligature %s => %S",
cref(feature,sequence),
gref(descriptions,ligature),
gref(descriptions,unicode))
end
local function report_kern(feature,sequence,descriptions,unicode,otherunicode,value)
report_prepare("%s: base kern %s + %s => %S",
cref(feature,sequence),
gref(descriptions,unicode),
gref(descriptions,otherunicode),
value)
end
-- We need to make sure that luatex sees the difference between base fonts that have
-- different glyphs in the same slots in fonts that have the same fullname (or filename).
-- LuaTeX will merge fonts eventually (and subset later on). If needed we can use a more
-- verbose name as long as we don't use <()<>[]{}/%> and the length is < 128.
local basehash, basehashes, applied = { }, 1, { }
local function registerbasehash(tfmdata)
local properties = tfmdata.properties
local hash = concat(applied," ")
local base = basehash[hash]
if not base then
basehashes = basehashes + 1
base = basehashes
basehash[hash] = base
end
properties.basehash = base
properties.fullname = (properties.fullname or properties.name) .. "-" .. base
-- report_prepare("fullname base hash '%a, featureset %a",tfmdata.properties.fullname,hash)
applied = { }
end
local function registerbasefeature(feature,value)
applied[#applied+1] = feature .. "=" .. tostring(value)
end
-- The original basemode ligature builder used the names of components and did some expression
-- juggling to get the chain right. The current variant starts with unicodes but still uses
-- names to make the chain. This is needed because we have to create intermediates when needed
-- but use predefined snippets when available. To some extend the current builder is more stupid
-- but I don't worry that much about it as ligatures are rather predicatable.
--
-- Personally I think that an ff + i == ffi rule as used in for instance latin modern is pretty
-- weird as no sane person will key that in and expect a glyph for that ligature plus the following
-- character. Anyhow, as we need to deal with this, we do, but no guarantes are given.
--
-- latin modern dejavu
--
-- f+f 102 102 102 102
-- f+i 102 105 102 105
-- f+l 102 108 102 108
-- f+f+i 102 102 105
-- f+f+l 102 102 108 102 102 108
-- ff+i 64256 105 64256 105
-- ff+l 64256 108
--
-- As you can see here, latin modern is less complete than dejavu but
-- in practice one will not notice it.
--
-- The while loop is needed because we need to resolve for instance pseudo names like
-- hyphen_hyphen to endash so in practice we end up with a bit too many definitions but the
-- overhead is neglectable. We can have changed[first] or changed[second] but it quickly becomes
-- messy if we need to take that into account.
local function makefake(tfmdata,name,present)
local resources = tfmdata.resources
local private = resources.private
local character = { intermediate = true, ligatures = { } }
resources.unicodes[name] = private
tfmdata.characters[private] = character
tfmdata.descriptions[private] = { name = name }
resources.private = private + 1
present[name] = private
return character
end
local function make_1(present,tree,name)
for k, v in next, tree do
if k == "ligature" then
present[name] = v
else
make_1(present,v,name .. "_" .. k)
end
end
end
local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,done)
for k, v in next, tree do
if k == "ligature" then
local character = characters[preceding]
if not character then
if trace_baseinit then
report_prepare("weird ligature in lookup %a, current %C, preceding %C",sequence.name,v,preceding)
end
character = makefake(tfmdata,name,present)
end
local ligatures = character.ligatures
if ligatures then
ligatures[unicode] = { char = v }
else
character.ligatures = { [unicode] = { char = v } }
end
if done then
local d = done[name]
if not d then
done[name] = { "dummy", v }
else
d[#d+1] = v
end
end
else
local code = present[name] or unicode
local name = name .. "_" .. k
make_2(present,tfmdata,characters,v,name,code,k,done)
end
end
end
local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist)
local characters = tfmdata.characters
local descriptions = tfmdata.descriptions
local resources = tfmdata.resources
local changed = tfmdata.changed
local ligatures = { }
local alternate = tonumber(value) or true and 1
local defaultalt = otf.defaultbasealternate
local trace_singles = trace_baseinit and trace_singles
local trace_alternatives = trace_baseinit and trace_alternatives
local trace_ligatures = trace_baseinit and trace_ligatures
-- A chain of changes is handled in font-con which is clesner because
-- we can have shared changes and such.
if not changed then
changed = { }
tfmdata.changed = changed
end
for i=1,#lookuplist do
local sequence = lookuplist[i]
local steps = sequence.steps
local kind = sequence.type
if kind == "gsub_single" then
for i=1,#steps do
for unicode, data in next, steps[i].coverage do
if unicode ~= data then
changed[unicode] = data
end
if trace_singles then
report_substitution(feature,sequence,descriptions,unicode,data)
end
end
end
elseif kind == "gsub_alternate" then
for i=1,#steps do
for unicode, data in next, steps[i].coverage do
local replacement = data[alternate]
if replacement then
if unicode ~= replacement then
changed[unicode] = replacement
end
if trace_alternatives then
report_alternate(feature,sequence,descriptions,unicode,replacement,value,"normal")
end
elseif defaultalt == "first" then
replacement = data[1]
if unicode ~= replacement then
changed[unicode] = replacement
end
if trace_alternatives then
report_alternate(feature,sequence,descriptions,unicode,replacement,value,defaultalt)
end
elseif defaultalt == "last" then
replacement = data[#data]
if unicode ~= replacement then
changed[unicode] = replacement
end
if trace_alternatives then
report_alternate(feature,sequence,descriptions,unicode,replacement,value,defaultalt)
end
else
if trace_alternatives then
report_alternate(feature,sequence,descriptions,unicode,replacement,value,"unknown")
end
end
end
end
elseif kind == "gsub_ligature" then
for i=1,#steps do
for unicode, data in next, steps[i].coverage do
ligatures[#ligatures+1] = { unicode, data, "" } -- lookupname }
if trace_ligatures then
report_ligature(feature,sequence,descriptions,unicode,data)
end
end
end
end
end
local nofligatures = #ligatures
if nofligatures > 0 then
local characters = tfmdata.characters
local present = { }
local done = trace_baseinit and trace_ligatures and { }
for i=1,nofligatures do
local ligature = ligatures[i]
local unicode, tree = ligature[1], ligature[2]
make_1(present,tree,"ctx_"..unicode)
end
for i=1,nofligatures do
local ligature = ligatures[i]
local unicode, tree, lookupname = ligature[1], ligature[2], ligature[3]
make_2(present,tfmdata,characters,tree,"ctx_"..unicode,unicode,unicode,done,sequence)
end
end
end
local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist)
local characters = tfmdata.characters
local descriptions = tfmdata.descriptions
local resources = tfmdata.resources
local properties = tfmdata.properties
local traceindeed = trace_baseinit and trace_kerns
-- check out this sharedkerns trickery
for i=1,#lookuplist do
local sequence = lookuplist[i]
local steps = sequence.steps
local kind = sequence.type
local format = sequence.format
if kind == "gpos_pair" then
for i=1,#steps do
local step = steps[i]
if step.format == "kern" then
for unicode, data in next, steps[i].coverage do
local character = characters[unicode]
local kerns = character.kerns
if not kerns then
kerns = { }
character.kerns = kerns
end
if traceindeed then
for otherunicode, kern in next, data do
if not kerns[otherunicode] and kern ~= 0 then
kerns[otherunicode] = kern
report_kern(feature,sequence,descriptions,unicode,otherunicode,kern)
end
end
else
for otherunicode, kern in next, data do
if not kerns[otherunicode] and kern ~= 0 then
kerns[otherunicode] = kern
end
end
end
end
else
for unicode, data in next, steps[i].coverage do
local character = characters[unicode]
local kerns = character.kerns
for otherunicode, kern in next, data do
if not kern[2] and not (kerns and kerns[otherunicode]) then
local kern = kern[1]
if kern[1] ~= 0 or kern[2] ~= 0 or kern[4] ~= 0 then
-- a complex pair not suitable for basemode
else
kern = kern[3]
if kern ~= 0 then
if kerns then
kerns[otherunicode] = kern
else
kerns = { [otherunicode] = kern }
character.kerns = kerns
end
if traceindeed then
report_kern(feature,sequence,descriptions,unicode,otherunicode,kern)
end
end
end
end
end
end
end
end
end
end
end
local function initializehashes(tfmdata)
-- already done
end
local function checkmathreplacements(tfmdata,fullname)
if tfmdata.mathparameters then
local characters = tfmdata.characters
local changed = tfmdata.changed
if next(changed) then
if trace_preparing or trace_baseinit then
report_prepare("checking math replacements for %a",fullname)
end
for unicode, replacement in next, changed do
local u = characters[unicode]
local r = characters[replacement]
local n = u.next
local v = u.vert_variants
local h = u.horiz_variants
if n and not r.next then
if trace_preparing then
report_prepare("forcing %s for %C substituted by %U","incremental step",unicode,replacement)
end
r.next = n
end
if v and not r.vert_variants then
if trace_preparing then
report_prepare("forcing %s for %C substituted by %U","vertical variants",unicode,replacement)
end
r.vert_variants = v
end
if h and not r.horiz_variants then
if trace_preparing then
report_prepare("forcing %s for %C substituted by %U","horizontal variants",unicode,replacement)
end
r.horiz_variants = h
end
end
end
end
end
local function featuresinitializer(tfmdata,value)
if true then -- value then
local starttime = trace_preparing and os.clock()
local features = tfmdata.shared.features
local fullname = tfmdata.properties.fullname or "?"
if features then
initializehashes(tfmdata)
local collectlookups = otf.collectlookups
local rawdata = tfmdata.shared.rawdata
local properties = tfmdata.properties
local script = properties.script
local language = properties.language
local rawresources = rawdata.resources
local rawfeatures = rawresources and rawresources.features
local basesubstitutions = rawfeatures and rawfeatures.gsub
local basepositionings = rawfeatures and rawfeatures.gpos
local substitutionsdone = false
local positioningsdone = false
--
if basesubstitutions or basepositionings then
local sequences = tfmdata.resources.sequences
for s=1,#sequences do
local sequence = sequences[s]
local sfeatures = sequence.features
if sfeatures then
local order = sequence.order
if order then
for i=1,#order do --
local feature = order[i]
local value = features[feature]
if value then
local validlookups, lookuplist = collectlookups(rawdata,feature,script,language)
if not validlookups then
-- skip
elseif basesubstitutions and basesubstitutions[feature] then
if trace_preparing then
report_prepare("filtering base %s feature %a for %a with value %a","sub",feature,fullname,value)
end
preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist)
registerbasefeature(feature,value)
substitutionsdone = true
elseif basepositionings and basepositionings[feature] then
if trace_preparing then
report_prepare("filtering base %a feature %a for %a with value %a","pos",feature,fullname,value)
end
preparepositionings(tfmdata,feature,value,validlookups,lookuplist)
registerbasefeature(feature,value)
positioningsdone = true
end
end
end
end
end
end
end
--
if substitutionsdone then
checkmathreplacements(tfmdata,fullname)
end
--
registerbasehash(tfmdata)
end
if trace_preparing then
report_prepare("preparation time is %0.3f seconds for %a",os.clock()-starttime,fullname)
end
end
end
registerotffeature {
name = "features",
description = "features",
default = true,
initializers = {
-- position = 1, -- after setscript (temp hack ... we need to force script / language to 1
base = featuresinitializer,
}
}
otf.basemodeinitializer = featuresinitializer
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-oto”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-otj” 5ea70db9f1990dc1459425853c79f663] ---
if not modules then modules={} end modules ['font-otj']={
version=1.001,
comment="companion to font-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files",
}
if not nodes.properties then return end
local next,rawget=next,rawget
local fastcopy=table.fastcopy
local registertracker=trackers.register
local trace_injections=false registertracker("fonts.injections",function(v) trace_injections=v end)
local trace_marks=false registertracker("fonts.injections.marks",function(v) trace_marks=v end)
local trace_cursive=false registertracker("fonts.injections.cursive",function(v) trace_cursive=v end)
local trace_spaces=false registertracker("fonts.injections.spaces",function(v) trace_spaces=v end)
local use_advance=false directives.register("fonts.injections.advance",function(v) use_advance=v end)
local report_injections=logs.reporter("fonts","injections")
local report_spaces=logs.reporter("fonts","spaces")
local attributes,nodes,node=attributes,nodes,node
fonts=fonts
local hashes=fonts.hashes
local fontdata=hashes.identifiers
nodes.injections=nodes.injections or {}
local injections=nodes.injections
local tracers=nodes.tracers
local setcolor=tracers and tracers.colors.set
local resetcolor=tracers and tracers.colors.reset
local nodecodes=nodes.nodecodes
local glyph_code=nodecodes.glyph
local disc_code=nodecodes.disc
local kern_code=nodecodes.kern
local glue_code=nodecodes.glue
local nuts=nodes.nuts
local nodepool=nuts.pool
local newkern=nodepool.kern
local tonode=nuts.tonode
local tonut=nuts.tonut
local getfield=nuts.getfield
local setfield=nuts.setfield
local getnext=nuts.getnext
local getprev=nuts.getprev
local getid=nuts.getid
local getfont=nuts.getfont
local getsubtype=nuts.getsubtype
local getchar=nuts.getchar
local getboth=nuts.getboth
local ischar=nuts.is_char
local getdisc=nuts.getdisc
local setdisc=nuts.setdisc
local traverse_id=nuts.traverse_id
local traverse_char=nuts.traverse_char
local insert_node_before=nuts.insert_before
local insert_node_after=nuts.insert_after
local properties=nodes.properties.data
function injections.installnewkern(nk)
newkern=nk or newkern
end
local nofregisteredkerns=0
local nofregisteredpairs=0
local nofregisteredmarks=0
local nofregisteredcursives=0
local keepregisteredcounts=false
function injections.keepcounts()
keepregisteredcounts=true
end
function injections.resetcounts()
nofregisteredkerns=0
nofregisteredpairs=0
nofregisteredmarks=0
nofregisteredcursives=0
keepregisteredcounts=false
end
function injections.reset(n)
local p=rawget(properties,n)
if p then
p.injections=false
else
properties[n]=false
end
end
function injections.copy(target,source)
local sp=rawget(properties,source)
if sp then
local tp=rawget(properties,target)
local si=sp.injections
if si then
si=fastcopy(si)
if tp then
tp.injections=si
else
propertydata[target]={
injections=si,
}
end
elseif tp then
tp.injections=false
else
properties[target]={ injections={} }
end
else
local tp=rawget(properties,target)
if tp then
tp.injections=false
else
properties[target]=false
end
end
end
function injections.setligaindex(n,index)
local p=rawget(properties,n)
if p then
local i=p.injections
if i then
i.ligaindex=index
else
p.injections={
ligaindex=index
}
end
else
properties[n]={
injections={
ligaindex=index
}
}
end
end
function injections.getligaindex(n,default)
local p=rawget(properties,n)
if p then
local i=p.injections
if i then
return i.ligaindex or default
end
end
return default
end
function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext)
local dx=factor*(exit[1]-entry[1])
local dy=-factor*(exit[2]-entry[2])
local ws=tfmstart.width
local wn=tfmnext.width
nofregisteredcursives=nofregisteredcursives+1
if rlmode<0 then
dx=-(dx+wn)
else
dx=dx-ws
end
if dx==0 then
dx=0
end
local p=rawget(properties,start)
if p then
local i=p.injections
if i then
i.cursiveanchor=true
else
p.injections={
cursiveanchor=true,
}
end
else
properties[start]={
injections={
cursiveanchor=true,
},
}
end
local p=rawget(properties,nxt)
if p then
local i=p.injections
if i then
i.cursivex=dx
i.cursivey=dy
else
p.injections={
cursivex=dx,
cursivey=dy,
}
end
else
properties[nxt]={
injections={
cursivex=dx,
cursivey=dy,
},
}
end
return dx,dy,nofregisteredcursives
end
function injections.setpair(current,factor,rlmode,r2lflag,spec,injection)
local x=factor*spec[1]
local y=factor*spec[2]
local w=factor*spec[3]
local h=factor*spec[4]
if x~=0 or w~=0 or y~=0 or h~=0 then
local yoffset=y-h
local leftkern=x
local rightkern=w-x
if leftkern~=0 or rightkern~=0 or yoffset~=0 then
nofregisteredpairs=nofregisteredpairs+1
if rlmode and rlmode<0 then
leftkern,rightkern=rightkern,leftkern
end
if not injection then
injection="injections"
end
local p=rawget(properties,current)
if p then
local i=rawget(p,injection)
if i then
if leftkern~=0 then
i.leftkern=(i.leftkern or 0)+leftkern
end
if rightkern~=0 then
i.rightkern=(i.rightkern or 0)+rightkern
end
if yoffset~=0 then
i.yoffset=(i.yoffset or 0)+yoffset
end
elseif leftkern~=0 or rightkern~=0 then
p[injection]={
leftkern=leftkern,
rightkern=rightkern,
yoffset=yoffset,
}
else
p[injection]={
yoffset=yoffset,
}
end
elseif leftkern~=0 or rightkern~=0 then
properties[current]={
[injection]={
leftkern=leftkern,
rightkern=rightkern,
yoffset=yoffset,
},
}
else
properties[current]={
[injection]={
yoffset=yoffset,
},
}
end
return x,y,w,h,nofregisteredpairs
end
end
return x,y,w,h
end
function injections.setkern(current,factor,rlmode,x,injection)
local dx=factor*x
if dx~=0 then
nofregisteredkerns=nofregisteredkerns+1
local p=rawget(properties,current)
if not injection then
injection="injections"
end
if p then
local i=rawget(p,injection)
if i then
i.leftkern=dx+(i.leftkern or 0)
else
p[injection]={
leftkern=dx,
}
end
else
properties[current]={
[injection]={
leftkern=dx,
},
}
end
return dx,nofregisteredkerns
else
return 0,0
end
end
function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk,checkmark)
local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2])
nofregisteredmarks=nofregisteredmarks+1
if rlmode>=0 then
dx=tfmbase.width-dx
end
local p=rawget(properties,start)
if p then
local i=p.injections
if i then
if i.markmark then
else
i.markx=dx
i.marky=dy
i.markdir=rlmode or 0
i.markbase=nofregisteredmarks
i.markbasenode=base
i.markmark=mkmk
i.checkmark=checkmark
end
else
p.injections={
markx=dx,
marky=dy,
markdir=rlmode or 0,
markbase=nofregisteredmarks,
markbasenode=base,
markmark=mkmk,
checkmark=checkmark,
}
end
else
properties[start]={
injections={
markx=dx,
marky=dy,
markdir=rlmode or 0,
markbase=nofregisteredmarks,
markbasenode=base,
markmark=mkmk,
checkmark=checkmark,
},
}
end
return dx,dy,nofregisteredmarks
end
local function dir(n)
return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset"
end
local function showchar(n,nested)
local char=getchar(n)
report_injections("%wfont %s, char %U, glyph %c",nested and 2 or 0,getfont(n),char,char)
end
local function show(n,what,nested,symbol)
if n then
local p=rawget(properties,n)
if p then
local i=rawget(p,what)
if i then
local leftkern=i.leftkern or 0
local rightkern=i.rightkern or 0
local yoffset=i.yoffset or 0
local markx=i.markx or 0
local marky=i.marky or 0
local markdir=i.markdir or 0
local markbase=i.markbase or 0
local cursivex=i.cursivex or 0
local cursivey=i.cursivey or 0
local ligaindex=i.ligaindex or 0
local cursbase=i.cursiveanchor
local margin=nested and 4 or 2
if rightkern~=0 or yoffset~=0 then
report_injections("%w%s pair: lx %p, rx %p, dy %p",margin,symbol,leftkern,rightkern,yoffset)
elseif leftkern~=0 then
report_injections("%w%s kern: dx %p",margin,symbol,leftkern)
end
if markx~=0 or marky~=0 or markbase~=0 then
report_injections("%w%s mark: dx %p, dy %p, dir %s, base %s",margin,symbol,markx,marky,markdir,markbase~=0 and "yes" or "no")
end
if cursivex~=0 or cursivey~=0 then
if cursbase then
report_injections("%w%s curs: base dx %p, dy %p",margin,symbol,cursivex,cursivey)
else
report_injections("%w%s curs: dx %p, dy %p",margin,symbol,cursivex,cursivey)
end
elseif cursbase then
report_injections("%w%s curs: base",margin,symbol)
end
if ligaindex~=0 then
report_injections("%w%s liga: index %i",margin,symbol,ligaindex)
end
end
end
end
end
local function showsub(n,what,where)
report_injections("begin subrun: %s",where)
for n in traverse_id(glyph_code,n) do
showchar(n,where)
show(n,what,where," ")
end
report_injections("end subrun")
end
local function trace(head,where)
report_injections("begin run %s: %s kerns, %s pairs, %s marks and %s cursives registered",
where or "",nofregisteredkerns,nofregisteredpairs,nofregisteredmarks,nofregisteredcursives)
local n=head
while n do
local id=getid(n)
if id==glyph_code then
showchar(n)
show(n,"injections",false," ")
show(n,"preinjections",false,"<")
show(n,"postinjections",false,">")
show(n,"replaceinjections",false,"=")
show(n,"emptyinjections",false,"*")
elseif id==disc_code then
local pre,post,replace=getdisc(n)
if pre then
showsub(pre,"preinjections","pre")
end
if post then
showsub(post,"postinjections","post")
end
if replace then
showsub(replace,"replaceinjections","replace")
end
show(n,"emptyinjections",false,"*")
end
n=getnext(n)
end
report_injections("end run")
end
local function show_result(head)
local current=head
local skipping=false
while current do
local id=getid(current)
if id==glyph_code then
report_injections("char: %C, width %p, xoffset %p, yoffset %p",
getchar(current),getfield(current,"width"),getfield(current,"xoffset"),getfield(current,"yoffset"))
skipping=false
elseif id==kern_code then
report_injections("kern: %p",getfield(current,"kern"))
skipping=false
elseif not skipping then
report_injections()
skipping=true
end
current=getnext(current)
end
end
local function inject_kerns_only(head,where)
head=tonut(head)
if trace_injections then
trace(head,"kerns")
end
local current=head
local prev=nil
local next=nil
local prevdisc=nil
local prevglyph=nil
local pre=nil
local post=nil
local replace=nil
local pretail=nil
local posttail=nil
local replacetail=nil
while current do
local id=getid(current)
local next=getnext(current)
if id==glyph_code then
if getsubtype(current)<256 then
local p=rawget(properties,current)
if p then
local i=p.injections
if i then
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
if use_advance then
setfield(current,"xoffset",leftkern)
setfield(current,"xadvance",leftkern)
else
insert_node_before(head,current,newkern(leftkern))
end
end
end
if prevdisc then
local done=false
if post then
local i=p.postinjections
if i then
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
if use_advance then
setfield(post,"xadvance",leftkern)
else
insert_node_after(post,posttail,newkern(leftkern))
done=true
end
end
end
end
if replace then
local i=p.replaceinjections
if i then
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
if use_advance then
setfield(replace,"xadvance",leftkern)
else
insert_node_after(replace,replacetail,newkern(leftkern))
done=true
end
end
end
else
local i=p.emptyinjections
if i then
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
setfield(prev,"replace",newkern(leftkern))
end
end
end
if done then
setdisc(prevdisc,pre,post,replace)
end
end
end
end
prevdisc=nil
prevglyph=current
elseif id==disc_code then
pre,post,replace,pretail,posttail,replacetail=getdisc(current,true)
local done=false
if pre then
for n in traverse_char(pre) do
local p=rawget(properties,n)
if p then
local i=p.injections or p.preinjections
if i then
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
if use_advance then
setfield(pre,"xoffset",leftkern)
setfield(pre,"xadvance",leftkern)
else
pre=insert_node_before(pre,n,newkern(leftkern))
done=true
end
end
end
end
end
end
if post then
for n in traverse_char(post) do
local p=rawget(properties,n)
if p then
local i=p.injections or p.postinjections
if i then
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
if use_advance then
setfield(post,"xoffset",leftkern)
setfield(post,"xadvance",leftkern)
else
post=insert_node_before(post,n,newkern(leftkern))
done=true
end
end
end
end
end
end
if replace then
for n in traverse_char(replace) do
local p=rawget(properties,n)
if p then
local i=p.injections or p.replaceinjections
if i then
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
if use_advance then
setfield(replace,"xoffset",leftkern)
setfield(replace,"xadvance",leftkern)
else
replace=insert_node_before(replace,n,newkern(leftkern))
done=true
end
end
end
end
end
end
if done then
setdisc(current,pre,post,replace)
end
prevglyph=nil
prevdisc=current
else
prevglyph=nil
prevdisc=nil
end
prev=current
current=next
end
if keepregisteredcounts then
keepregisteredcounts=false
else
nofregisteredkerns=0
end
return tonode(head),true
end
local function inject_pairs_only(head,where)
head=tonut(head)
if trace_injections then
trace(head,"pairs")
end
local current=head
local prev=nil
local next=nil
local prevdisc=nil
local prevglyph=nil
local pre=nil
local post=nil
local replace=nil
local pretail=nil
local posttail=nil
local replacetail=nil
while current do
local id=getid(current)
local next=getnext(current)
if id==glyph_code then
if getsubtype(current)<256 then
local p=rawget(properties,current)
if p then
local i=p.injections
if i then
local yoffset=i.yoffset
if yoffset and yoffset~=0 then
setfield(current,"yoffset",yoffset)
end
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
head=insert_node_before(head,current,newkern(leftkern))
end
local rightkern=i.rightkern
if rightkern and rightkern~=0 then
insert_node_after(head,current,newkern(rightkern))
end
else
local i=p.emptyinjections
if i then
local rightkern=i.rightkern
if rightkern and rightkern~=0 then
if next and getid(next)==disc_code then
if replace then
else
setfield(next,"replace",newkern(rightkern))
end
end
end
end
end
if prevdisc then
local done=false
if post then
local i=p.postinjections
if i then
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
insert_node_after(post,posttail,newkern(leftkern))
done=true
end
end
end
if replace then
local i=p.replaceinjections
if i then
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
insert_node_after(replace,replacetail,newkern(leftkern))
done=true
end
end
else
local i=p.emptyinjections
if i then
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
setfield(prev,"replace",newkern(leftkern))
end
end
end
if done then
setdisc(prevdisc,pre,post,replace)
end
end
end
end
prevdisc=nil
prevglyph=current
elseif id==disc_code then
pre,post,replace,pretail,posttail,replacetail=getdisc(current,true)
local done=false
if pre then
for n in traverse_char(pre) do
local p=rawget(properties,n)
if p then
local i=p.injections or p.preinjections
if i then
local yoffset=i.yoffset
if yoffset and yoffset~=0 then
setfield(n,"yoffset",yoffset)
end
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
pre=insert_node_before(pre,n,newkern(leftkern))
done=true
end
local rightkern=i.rightkern
if rightkern and rightkern~=0 then
insert_node_after(pre,n,newkern(rightkern))
done=true
end
end
end
end
end
if post then
for n in traverse_char(post) do
local p=rawget(properties,n)
if p then
local i=p.injections or p.postinjections
if i then
local yoffset=i.yoffset
if yoffset and yoffset~=0 then
setfield(n,"yoffset",yoffset)
end
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
post=insert_node_before(post,n,newkern(leftkern))
done=true
end
local rightkern=i.rightkern
if rightkern and rightkern~=0 then
insert_node_after(post,n,newkern(rightkern))
done=true
end
end
end
end
end
if replace then
for n in traverse_char(replace) do
local p=rawget(properties,n)
if p then
local i=p.injections or p.replaceinjections
if i then
local yoffset=i.yoffset
if yoffset and yoffset~=0 then
setfield(n,"yoffset",yoffset)
end
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
replace=insert_node_before(replace,n,newkern(leftkern))
done=true
end
local rightkern=i.rightkern
if rightkern and rightkern~=0 then
insert_node_after(replace,n,newkern(rightkern))
done=true
end
end
end
end
end
if prevglyph then
if pre then
local p=rawget(properties,prevglyph)
if p then
local i=p.preinjections
if i then
local rightkern=i.rightkern
if rightkern and rightkern~=0 then
pre=insert_node_before(pre,pre,newkern(rightkern))
done=true
end
end
end
end
if replace then
local p=rawget(properties,prevglyph)
if p then
local i=p.replaceinjections
if i then
local rightkern=i.rightkern
if rightkern and rightkern~=0 then
replace=insert_node_before(replace,replace,newkern(rightkern))
done=true
end
end
end
end
end
if done then
setdisc(current,pre,post,replace)
end
prevglyph=nil
prevdisc=current
else
prevglyph=nil
prevdisc=nil
end
prev=current
current=next
end
if keepregisteredcounts then
keepregisteredcounts=false
else
nofregisteredkerns=0
end
return tonode(head),true
end
local function showoffset(n,flag)
local o=getfield(n,"xoffset")
if o==0 then
o=getfield(n,"yoffset")
end
if o~=0 then
setcolor(n,flag and "darkred" or "darkgreen")
else
resetcolor(n)
end
end
local function inject_everything(head,where)
head=tonut(head)
if trace_injections then
trace(head,"everything")
end
local hascursives=nofregisteredcursives>0
local hasmarks=nofregisteredmarks>0
local current=head
local last=nil
local font=font
local markdata=nil
local prev=nil
local next=nil
local prevdisc=nil
local prevglyph=nil
local pre=nil
local post=nil
local replace=nil
local pretail=nil
local posttail=nil
local replacetail=nil
local cursiveanchor=nil
local minc=0
local maxc=0
local glyphs={}
local marks={}
local nofmarks=0
local function processmark(p,n,pn)
local px=getfield(p,"xoffset")
local ox=0
local rightkern=nil
local pp=rawget(properties,p)
if pp then
pp=pp.injections
if pp then
rightkern=pp.rightkern
end
end
if rightkern then
if pn.markdir<0 then
ox=px-pn.markx-rightkern
else
if false then
local leftkern=pp.leftkern
if leftkern then
ox=px-pn.markx-leftkern
else
ox=px-pn.markx
end
else
ox=px-pn.markx
end
end
else
ox=px-pn.markx
if pn.checkmark then
local wn=getfield(n,"width")
if wn~=0 then
wn=wn/2
if trace_injections then
report_injections("correcting non zero width mark %C",getchar(n))
end
insert_node_before(n,n,newkern(-wn))
insert_node_after(n,n,newkern(-wn))
end
end
end
local oy=getfield(n,"yoffset")+getfield(p,"yoffset")+pn.marky
setfield(n,"xoffset",ox)
setfield(n,"yoffset",oy)
if trace_marks then
showoffset(n,true)
end
end
while current do
local id=getid(current)
local next=getnext(current)
if id==glyph_code then
if getsubtype(current)<256 then
local p=rawget(properties,current)
if p then
local i=p.injections
if i then
local pm=i.markbasenode
if pm then
nofmarks=nofmarks+1
marks[nofmarks]=current
else
local yoffset=i.yoffset
if yoffset and yoffset~=0 then
setfield(current,"yoffset",yoffset)
end
if hascursives then
local cursivex=i.cursivex
if cursivex then
if cursiveanchor then
if cursivex~=0 then
i.leftkern=(i.leftkern or 0)+cursivex
end
if maxc==0 then
minc=1
maxc=1
glyphs[1]=cursiveanchor
else
maxc=maxc+1
glyphs[maxc]=cursiveanchor
end
properties[cursiveanchor].cursivedy=i.cursivey
last=current
else
maxc=0
end
elseif maxc>0 then
local ny=getfield(current,"yoffset")
for i=maxc,minc,-1 do
local ti=glyphs[i]
ny=ny+properties[ti].cursivedy
setfield(ti,"yoffset",ny)
if trace_cursive then
showoffset(ti)
end
end
maxc=0
cursiveanchor=nil
end
if i.cursiveanchor then
cursiveanchor=current
else
if maxc>0 then
local ny=getfield(current,"yoffset")
for i=maxc,minc,-1 do
local ti=glyphs[i]
ny=ny+properties[ti].cursivedy
setfield(ti,"yoffset",ny)
if trace_cursive then
showoffset(ti)
end
end
maxc=0
end
cursiveanchor=nil
end
end
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
insert_node_before(head,current,newkern(leftkern))
end
local rightkern=i.rightkern
if rightkern and rightkern~=0 then
insert_node_after(head,current,newkern(rightkern))
end
end
else
local i=p.emptyinjections
if i then
local rightkern=i.rightkern
if rightkern and rightkern~=0 then
if next and getid(next)==disc_code then
if replace then
else
setfield(next,"replace",newkern(rightkern))
end
end
end
end
end
if prevdisc then
if p then
local done=false
if post then
local i=p.postinjections
if i then
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
insert_node_after(post,posttail,newkern(leftkern))
done=true
end
end
end
if replace then
local i=p.replaceinjections
if i then
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
insert_node_after(replace,replacetail,newkern(leftkern))
done=true
end
end
else
local i=p.emptyinjections
if i then
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
setfield(prev,"replace",newkern(leftkern))
end
end
end
if done then
setdisc(prevdisc,pre,post,replace)
end
end
end
else
if hascursives and maxc>0 then
local ny=getfield(current,"yoffset")
for i=maxc,minc,-1 do
local ti=glyphs[i]
ny=ny+properties[ti].cursivedy
setfield(ti,"yoffset",getfield(ti,"yoffset")+ny)
end
maxc=0
cursiveanchor=nil
end
end
end
prevdisc=nil
prevglyph=current
elseif id==disc_code then
pre,post,replace,pretail,posttail,replacetail=getdisc(current,true)
local done=false
if pre then
for n in traverse_char(pre) do
local p=rawget(properties,n)
if p then
local i=p.injections or p.preinjections
if i then
local yoffset=i.yoffset
if yoffset and yoffset~=0 then
setfield(n,"yoffset",yoffset)
end
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
pre=insert_node_before(pre,n,newkern(leftkern))
done=true
end
local rightkern=i.rightkern
if rightkern and rightkern~=0 then
insert_node_after(pre,n,newkern(rightkern))
done=true
end
if hasmarks then
local pm=i.markbasenode
if pm then
processmark(pm,current,i)
end
end
end
end
end
end
if post then
for n in traverse_char(post) do
local p=rawget(properties,n)
if p then
local i=p.injections or p.postinjections
if i then
local yoffset=i.yoffset
if yoffset and yoffset~=0 then
setfield(n,"yoffset",yoffset)
end
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
post=insert_node_before(post,n,newkern(leftkern))
done=true
end
local rightkern=i.rightkern
if rightkern and rightkern~=0 then
insert_node_after(post,n,newkern(rightkern))
done=true
end
if hasmarks then
local pm=i.markbasenode
if pm then
processmark(pm,current,i)
end
end
end
end
end
end
if replace then
for n in traverse_char(replace) do
local p=rawget(properties,n)
if p then
local i=p.injections or p.replaceinjections
if i then
local yoffset=i.yoffset
if yoffset and yoffset~=0 then
setfield(n,"yoffset",yoffset)
end
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
replace=insert_node_before(replace,n,newkern(leftkern))
done=true
end
local rightkern=i.rightkern
if rightkern and rightkern~=0 then
insert_node_after(replace,n,newkern(rightkern))
done=true
end
if hasmarks then
local pm=i.markbasenode
if pm then
processmark(pm,current,i)
end
end
end
end
end
end
if prevglyph then
if pre then
local p=rawget(properties,prevglyph)
if p then
local i=p.preinjections
if i then
local rightkern=i.rightkern
if rightkern and rightkern~=0 then
pre=insert_node_before(pre,pre,newkern(rightkern))
done=true
end
end
end
end
if replace then
local p=rawget(properties,prevglyph)
if p then
local i=p.replaceinjections
if i then
local rightkern=i.rightkern
if rightkern and rightkern~=0 then
replace=insert_node_before(replace,replace,newkern(rightkern))
done=true
end
end
end
end
end
if done then
setdisc(current,pre,post,replace)
end
prevglyph=nil
prevdisc=current
else
prevglyph=nil
prevdisc=nil
end
prev=current
current=next
end
if hascursives and maxc>0 then
local ny=getfield(last,"yoffset")
for i=maxc,minc,-1 do
local ti=glyphs[i]
ny=ny+properties[ti].cursivedy
setfield(ti,"yoffset",ny)
if trace_cursive then
showoffset(ti)
end
end
end
if nofmarks>0 then
for i=1,nofmarks do
local m=marks[i]
local p=rawget(properties,m)
local i=p.injections
local b=i.markbasenode
processmark(b,m,i)
end
elseif hasmarks then
end
if keepregisteredcounts then
keepregisteredcounts=false
else
nofregisteredkerns=0
nofregisteredpairs=0
nofregisteredmarks=0
nofregisteredcursives=0
end
return tonode(head),true
end
local triggers=false
function nodes.injections.setspacekerns(font,sequence)
if triggers then
triggers[font]=sequence
else
triggers={ [font]=sequence }
end
end
local getthreshold
if context then
local threshold=1
local parameters=fonts.hashes.parameters
directives.register("otf.threshold",function(v) threshold=tonumber(v) or 1 end)
getthreshold=function(font)
local p=parameters[font]
local f=p.factor
local s=p.spacing
local t=threshold*(s and s.width or p.space or 0)-2
return t>0 and t or 0,f
end
else
injections.threshold=0
getthreshold=function(font)
local p=fontdata[font].parameters
local f=p.factor
local s=p.spacing
local t=injections.threshold*(s and s.width or p.space or 0)-2
return t>0 and t or 0,f
end
end
injections.getthreshold=getthreshold
function injections.isspace(n,threshold)
if getid(n)==glue_code then
local w=getfield(n,"width")
if threshold and w>threshold then
return 32
end
end
end
local function injectspaces(head)
if not triggers then
return head,false
end
local lastfont=nil
local spacekerns=nil
local leftkerns=nil
local rightkerns=nil
local factor=0
local threshold=0
local leftkern=false
local rightkern=false
local function updatefont(font,trig)
leftkerns=trig.left
rightkerns=trig.right
lastfont=font
threshold,
factor=getthreshold(font)
end
for n in traverse_id(glue_code,tonut(head)) do
local prev,next=getboth(n)
local prevchar=ischar(prev)
local nextchar=ischar(next)
if nextchar then
local font=getfont(next)
local trig=triggers[font]
if trig then
if lastfont~=font then
updatefont(font,trig)
end
if rightkerns then
rightkern=rightkerns[nextchar]
end
end
end
if prevchar then
local font=getfont(prev)
local trig=triggers[font]
if trig then
if lastfont~=font then
updatefont(font,trig)
end
if leftkerns then
leftkern=leftkerns[prevchar]
end
end
end
if leftkern then
local old=getfield(n,"width")
if old>threshold then
if rightkern then
local new=old+(leftkern+rightkern)*factor
if trace_spaces then
report_spaces("%C [%p -> %p] %C",prevchar,old,new,nextchar)
end
setfield(n,"width",new)
leftkern=false
else
local new=old+leftkern*factor
if trace_spaces then
report_spaces("%C [%p -> %p]",prevchar,old,new)
end
setfield(n,"width",new)
end
end
leftkern=false
elseif rightkern then
local old=getfield(n,"width")
if old>threshold then
local new=old+rightkern*factor
if trace_spaces then
report_spaces("[%p -> %p] %C",nextchar,old,new)
end
setfield(n,"width",new)
end
rightkern=false
end
end
triggers=false
return head,true
end
function injections.handler(head,where)
if triggers then
head=injectspaces(head)
end
if nofregisteredmarks>0 or nofregisteredcursives>0 then
if trace_injections then
report_injections("injection variant %a","everything")
end
return inject_everything(head,where)
elseif nofregisteredpairs>0 then
if trace_injections then
report_injections("injection variant %a","pairs")
end
return inject_pairs_only(head,where)
elseif nofregisteredkerns>0 then
if trace_injections then
report_injections("injection variant %a","kerns")
end
return inject_kerns_only(head,where)
else
return head,false
end
end
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-otj”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-ota” c281d18dfc89a8ca18af64f55e9fa92b] ---
if not modules then modules={} end modules ['font-ota']={
version=1.001,
comment="companion to font-otf.lua (analysing)",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local type=type
if not trackers then trackers={ register=function() end } end
local fonts,nodes,node=fonts,nodes,node
local allocate=utilities.storage.allocate
local otf=fonts.handlers.otf
local analyzers=fonts.analyzers
local initializers=allocate()
local methods=allocate()
analyzers.initializers=initializers
analyzers.methods=methods
local a_state=attributes.private('state')
local nuts=nodes.nuts
local tonut=nuts.tonut
local getfield=nuts.getfield
local getnext=nuts.getnext
local getprev=nuts.getprev
local getprev=nuts.getprev
local getprop=nuts.getprop
local setprop=nuts.setprop
local getfont=nuts.getfont
local getsubtype=nuts.getsubtype
local getchar=nuts.getchar
local ischar=nuts.is_char
local traverse_id=nuts.traverse_id
local end_of_math=nuts.end_of_math
local nodecodes=nodes.nodecodes
local disc_code=nodecodes.disc
local math_code=nodecodes.math
local fontdata=fonts.hashes.identifiers
local categories=characters and characters.categories or {}
local chardata=characters and characters.data
local otffeatures=fonts.constructors.features.otf
local registerotffeature=otffeatures.register
local s_init=1 local s_rphf=7
local s_medi=2 local s_half=8
local s_fina=3 local s_pref=9
local s_isol=4 local s_blwf=10
local s_mark=5 local s_pstf=11
local s_rest=6
local states={
init=s_init,
medi=s_medi,
med2=s_medi,
fina=s_fina,
fin2=s_fina,
fin3=s_fina,
isol=s_isol,
mark=s_mark,
rest=s_rest,
rphf=s_rphf,
half=s_half,
pref=s_pref,
blwf=s_blwf,
pstf=s_pstf,
}
local features={
init=s_init,
medi=s_medi,
med2=s_medi,
fina=s_fina,
fin2=s_fina,
fin3=s_fina,
isol=s_isol,
rphf=s_rphf,
half=s_half,
pref=s_pref,
blwf=s_blwf,
pstf=s_pstf,
}
analyzers.states=states
analyzers.features=features
analyzers.useunicodemarks=false
function analyzers.setstate(head,font)
local useunicodemarks=analyzers.useunicodemarks
local tfmdata=fontdata[font]
local descriptions=tfmdata.descriptions
local first,last,current,n,done=nil,nil,head,0,false
current=tonut(current)
while current do
local char,id=ischar(current,font)
if char and not getprop(current,a_state) then
done=true
local d=descriptions[char]
if d then
if d.class=="mark" then
done=true
setprop(current,a_state,s_mark)
elseif useunicodemarks and categories[char]=="mn" then
done=true
setprop(current,a_state,s_mark)
elseif n==0 then
first,last,n=current,current,1
setprop(current,a_state,s_init)
else
last,n=current,n+1
setprop(current,a_state,s_medi)
end
else
if first and first==last then
setprop(last,a_state,s_isol)
elseif last then
setprop(last,a_state,s_fina)
end
first,last,n=nil,nil,0
end
elseif char==false then
if first and first==last then
setprop(last,a_state,s_isol)
elseif last then
setprop(last,a_state,s_fina)
end
first,last,n=nil,nil,0
if id==math_code then
current=end_of_math(current)
end
elseif id==disc_code then
setprop(current,a_state,s_medi)
last=current
else
if first and first==last then
setprop(last,a_state,s_isol)
elseif last then
setprop(last,a_state,s_fina)
end
first,last,n=nil,nil,0
if id==math_code then
current=end_of_math(current)
end
end
current=getnext(current)
end
if first and first==last then
setprop(last,a_state,s_isol)
elseif last then
setprop(last,a_state,s_fina)
end
return head,done
end
local function analyzeinitializer(tfmdata,value)
local script,language=otf.scriptandlanguage(tfmdata)
local action=initializers[script]
if not action then
elseif type(action)=="function" then
return action(tfmdata,value)
else
local action=action[language]
if action then
return action(tfmdata,value)
end
end
end
local function analyzeprocessor(head,font,attr)
local tfmdata=fontdata[font]
local script,language=otf.scriptandlanguage(tfmdata,attr)
local action=methods[script]
if not action then
elseif type(action)=="function" then
return action(head,font,attr)
else
action=action[language]
if action then
return action(head,font,attr)
end
end
return head,false
end
registerotffeature {
name="analyze",
description="analysis of character classes",
default=true,
initializers={
node=analyzeinitializer,
},
processors={
position=1,
node=analyzeprocessor,
}
}
methods.latn=analyzers.setstate
local arab_warned={}
local function warning(current,what)
local char=getchar(current)
if not arab_warned[char] then
log.report("analyze","arab: character %C has no %a class",char,what)
arab_warned[char]=true
end
end
local mappers={
l=s_init,
d=s_medi,
c=s_medi,
r=s_fina,
u=s_isol,
}
local classifiers=characters.classifiers
if not classifiers then
local f_arabic,l_arabic=characters.blockrange("arabic")
local f_syriac,l_syriac=characters.blockrange("syriac")
local f_mandiac,l_mandiac=characters.blockrange("mandiac")
local f_nko,l_nko=characters.blockrange("nko")
local f_ext_a,l_ext_a=characters.blockrange("arabicextendeda")
classifiers=table.setmetatableindex(function(t,k)
if type(k)=="number" then
local c=chardata[k]
local v=false
if c then
local arabic=c.arabic
if arabic then
v=mappers[arabic]
if not v then
log.report("analyze","error in mapping arabic %C",k)
v=false
end
elseif (k>=f_arabic and k<=l_arabic) or
(k>=f_syriac and k<=l_syriac) or
(k>=f_mandiac and k<=l_mandiac) or
(k>=f_nko and k<=l_nko) or
(k>=f_ext_a and k<=l_ext_a) then
if categories[k]=="mn" then
v=s_mark
else
v=s_rest
end
end
end
t[k]=v
return v
end
end)
characters.classifiers=classifiers
end
function methods.arab(head,font,attr)
local first,last=nil,nil
local c_first,c_last=nil,nil
local current,done=head,false
current=tonut(current)
while current do
local char,id=ischar(current,font)
if char and not getprop(current,a_state) then
done=true
local classifier=classifiers[char]
if not classifier then
if last then
if c_last==s_medi or c_last==s_fina then
setprop(last,a_state,s_fina)
else
warning(last,"fina")
setprop(last,a_state,s_error)
end
first,last=nil,nil
elseif first then
if c_first==s_medi or c_first==s_fina then
setprop(first,a_state,s_isol)
else
warning(first,"isol")
setprop(first,a_state,s_error)
end
first=nil
end
elseif classifier==s_mark then
setprop(current,a_state,s_mark)
elseif classifier==s_isol then
if last then
if c_last==s_medi or c_last==s_fina then
setprop(last,a_state,s_fina)
else
warning(last,"fina")
setprop(last,a_state,s_error)
end
first,last=nil,nil
elseif first then
if c_first==s_medi or c_first==s_fina then
setprop(first,a_state,s_isol)
else
warning(first,"isol")
setprop(first,a_state,s_error)
end
first=nil
end
setprop(current,a_state,s_isol)
elseif classifier==s_medi then
if first then
last=current
c_last=classifier
setprop(current,a_state,s_medi)
else
setprop(current,a_state,s_init)
first=current
c_first=classifier
end
elseif classifier==s_fina then
if last then
if getprop(last,a_state)~=s_init then
setprop(last,a_state,s_medi)
end
setprop(current,a_state,s_fina)
first,last=nil,nil
elseif first then
setprop(current,a_state,s_fina)
first=nil
else
setprop(current,a_state,s_isol)
end
else
setprop(current,a_state,s_rest)
if last then
if c_last==s_medi or c_last==s_fina then
setprop(last,a_state,s_fina)
else
warning(last,"fina")
setprop(last,a_state,s_error)
end
first,last=nil,nil
elseif first then
if c_first==s_medi or c_first==s_fina then
setprop(first,a_state,s_isol)
else
warning(first,"isol")
setprop(first,a_state,s_error)
end
first=nil
end
end
else
if last then
if c_last==s_medi or c_last==s_fina then
setprop(last,a_state,s_fina)
else
warning(last,"fina")
setprop(last,a_state,s_error)
end
first,last=nil,nil
elseif first then
if c_first==s_medi or c_first==s_fina then
setprop(first,a_state,s_isol)
else
warning(first,"isol")
setprop(first,a_state,s_error)
end
first=nil
end
if id==math_code then
current=end_of_math(current)
end
end
current=getnext(current)
end
if last then
if c_last==s_medi or c_last==s_fina then
setprop(last,a_state,s_fina)
else
warning(last,"fina")
setprop(last,a_state,s_error)
end
elseif first then
if c_first==s_medi or c_first==s_fina then
setprop(first,a_state,s_isol)
else
warning(first,"isol")
setprop(first,a_state,s_error)
end
end
return head,done
end
methods.syrc=methods.arab
methods.mand=methods.arab
methods.nko=methods.arab
directives.register("otf.analyze.useunicodemarks",function(v)
analyzers.useunicodemarks=v
end)
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-ota”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-ots” 7e1e55f9f728474372665e4a64a43f5a] ---
if not modules then modules={} end modules ['font-ots']={
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 type,next,tonumber=type,next,tonumber
local random=math.random
local formatters=string.formatters
local insert=table.insert
local logs,trackers,nodes,attributes=logs,trackers,nodes,attributes
local registertracker=trackers.register
local registerdirective=directives.register
local fonts=fonts
local otf=fonts.handlers.otf
local trace_lookups=false registertracker("otf.lookups",function(v) trace_lookups=v end)
local trace_singles=false registertracker("otf.singles",function(v) trace_singles=v end)
local trace_multiples=false registertracker("otf.multiples",function(v) trace_multiples=v end)
local trace_alternatives=false registertracker("otf.alternatives",function(v) trace_alternatives=v end)
local trace_ligatures=false registertracker("otf.ligatures",function(v) trace_ligatures=v end)
local trace_contexts=false registertracker("otf.contexts",function(v) trace_contexts=v end)
local trace_marks=false registertracker("otf.marks",function(v) trace_marks=v end)
local trace_kerns=false registertracker("otf.kerns",function(v) trace_kerns=v end)
local trace_cursive=false registertracker("otf.cursive",function(v) trace_cursive=v end)
local trace_preparing=false registertracker("otf.preparing",function(v) trace_preparing=v end)
local trace_bugs=false registertracker("otf.bugs",function(v) trace_bugs=v end)
local trace_details=false registertracker("otf.details",function(v) trace_details=v end)
local trace_steps=false registertracker("otf.steps",function(v) trace_steps=v end)
local trace_skips=false registertracker("otf.skips",function(v) trace_skips=v end)
local trace_directions=false registertracker("otf.directions",function(v) trace_directions=v end)
local trace_plugins=false registertracker("otf.plugins",function(v) trace_plugins=v end)
local trace_kernruns=false registertracker("otf.kernruns",function(v) trace_kernruns=v end)
local trace_discruns=false registertracker("otf.discruns",function(v) trace_discruns=v end)
local trace_compruns=false registertracker("otf.compruns",function(v) trace_compruns=v end)
local trace_testruns=false registertracker("otf.testruns",function(v) trace_testruns=v end)
local optimizekerns=true
local alwaysdisc=true registerdirective("otf.alwaysdisc",function(v) alwaysdisc=v end)
local report_direct=logs.reporter("fonts","otf direct")
local report_subchain=logs.reporter("fonts","otf subchain")
local report_chain=logs.reporter("fonts","otf chain")
local report_process=logs.reporter("fonts","otf process")
local report_warning=logs.reporter("fonts","otf warning")
local report_run=logs.reporter("fonts","otf run")
registertracker("otf.replacements","otf.singles,otf.multiples,otf.alternatives,otf.ligatures")
registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive")
registertracker("otf.actions","otf.replacements,otf.positions")
registertracker("otf.injections","nodes.injections")
registertracker("*otf.sample","otf.steps,otf.actions,otf.analyzing")
local nuts=nodes.nuts
local tonode=nuts.tonode
local tonut=nuts.tonut
local getfield=nuts.getfield
local setfield=nuts.setfield
local getnext=nuts.getnext
local setnext=nuts.setnext
local getprev=nuts.getprev
local setprev=nuts.setprev
local getboth=nuts.getboth
local setboth=nuts.setboth
local getid=nuts.getid
local getattr=nuts.getattr
local setattr=nuts.setattr
local getprop=nuts.getprop
local setprop=nuts.setprop
local getfont=nuts.getfont
local getsubtype=nuts.getsubtype
local setsubtype=nuts.setsubtype
local getchar=nuts.getchar
local setchar=nuts.setchar
local getdisc=nuts.getdisc
local setdisc=nuts.setdisc
local setlink=nuts.setlink
local ischar=nuts.is_char
local insert_node_after=nuts.insert_after
local copy_node=nuts.copy
local copy_node_list=nuts.copy_list
local find_node_tail=nuts.tail
local flush_node_list=nuts.flush_list
local flush_node=nuts.flush_node
local end_of_math=nuts.end_of_math
local traverse_nodes=nuts.traverse
local traverse_id=nuts.traverse_id
local remove_node=nuts.remove
local setmetatableindex=table.setmetatableindex
local zwnj=0x200C
local zwj=0x200D
local wildcard="*"
local default="dflt"
local nodecodes=nodes.nodecodes
local glyphcodes=nodes.glyphcodes
local disccodes=nodes.disccodes
local glyph_code=nodecodes.glyph
local glue_code=nodecodes.glue
local disc_code=nodecodes.disc
local math_code=nodecodes.math
local dir_code=nodecodes.dir
local localpar_code=nodecodes.localpar
local discretionary_code=disccodes.discretionary
local ligature_code=glyphcodes.ligature
local privateattribute=attributes.private
local a_state=privateattribute('state')
local injections=nodes.injections
local setmark=injections.setmark
local setcursive=injections.setcursive
local setkern=injections.setkern
local setpair=injections.setpair
local resetinjection=injections.reset
local copyinjection=injections.copy
local setligaindex=injections.setligaindex
local getligaindex=injections.getligaindex
local cursonce=true
local fonthashes=fonts.hashes
local fontdata=fonthashes.identifiers
local fontfeatures=fonthashes.features
local otffeatures=fonts.constructors.features.otf
local registerotffeature=otffeatures.register
local onetimemessage=fonts.loggers.onetimemessage or function() end
local getrandom=utilities and utilities.randomizer and utilities.randomizer.get
otf.defaultnodealternate="none"
local tfmdata=false
local characters=false
local descriptions=false
local marks=false
local currentfont=false
local factor=0
local threshold=0
local checkmarks=false
local sweepnode=nil
local sweepprev=nil
local sweepnext=nil
local sweephead={}
local notmatchpre={}
local notmatchpost={}
local notmatchreplace={}
local handlers={}
local isspace=injections.isspace
local getthreshold=injections.getthreshold
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 checkdisccontent(d)
local pre,post,replace=getdisc(d)
if pre then for n in traverse_id(glue_code,pre) do print("pre",nodes.idstostring(pre)) break end end
if post then for n in traverse_id(glue_code,post) do print("pos",nodes.idstostring(post)) break end end
if replace then for n in traverse_id(glue_code,replace) do print("rep",nodes.idstostring(replace)) break end end
end
local function logprocess(...)
if trace_steps then
registermessage(...)
end
report_direct(...)
end
local function logwarning(...)
report_direct(...)
end
local f_unicode=formatters["%U"]
local f_uniname=formatters["%U (%s)"]
local f_unilist=formatters["% t (% t)"]
local function gref(n)
if type(n)=="number" then
local description=descriptions[n]
local name=description and description.name
if name then
return f_uniname(n,name)
else
return f_unicode(n)
end
elseif n then
local num,nam={},{}
for i=1,#n do
local ni=n[i]
if tonumber(ni) then
local di=descriptions[ni]
num[i]=f_unicode(ni)
nam[i]=di and di.name or "-"
end
end
return f_unilist(num,nam)
else
return "<error in node mode tracing>"
end
end
local function cref(dataset,sequence,index)
if not dataset then
return "no valid dataset"
elseif index then
return formatters["feature %a, type %a, chain lookup %a, index %a"](dataset[4],sequence.type,sequence.name,index)
else
return formatters["feature %a, type %a, chain lookup %a"](dataset[4],sequence.type,sequence.name)
end
end
local function pref(dataset,sequence)
return formatters["feature %a, type %a, lookup %a"](dataset[4],sequence.type,sequence.name)
end
local function mref(rlmode)
if not rlmode or rlmode==0 then
return "---"
elseif rlmode==-1 or rlmode=="+TRT" then
return "r2l"
else
return "l2r"
end
end
local function copy_glyph(g)
local components=getfield(g,"components")
if components then
setfield(g,"components")
local n=copy_node(g)
copyinjection(n,g)
setfield(g,"components",components)
return n
else
local n=copy_node(g)
copyinjection(n,g)
return n
end
end
local function flattendisk(head,disc)
local pre,post,replace,pretail,posttail,replacetail=getdisc(disc,true)
local prev,next=getboth(disc)
local ishead=head==disc
setdisc(disc)
flush_node(disc)
if pre then
flush_node_list(pre)
end
if post then
flush_node_list(post)
end
if ishead then
if replace then
if next then
setlink(replacetail,next)
end
return replace,replace
elseif next then
return next,next
else
return
end
else
if replace then
if next then
setlink(replacetail,next)
end
setlink(prev,replace)
return head,replace
else
setlink(prev,next)
return head,next
end
end
end
local function appenddisc(disc,list)
local pre,post,replace,pretail,posttail,replacetail=getdisc(disc,true)
local posthead=list
local replacehead=copy_node_list(list)
if post then
setlink(posttail,posthead)
else
post=phead
end
if replace then
setlink(replacetail,replacehead)
else
replace=rhead
end
setdisc(disc,pre,post,replace)
end
local function markstoligature(head,start,stop,char)
if start==stop and getchar(start)==char then
return head,start
else
local prev=getprev(start)
local next=getnext(stop)
setprev(start)
setnext(stop)
local base=copy_glyph(start)
if head==start then
head=base
end
resetinjection(base)
setchar(base,char)
setsubtype(base,ligature_code)
setfield(base,"components",start)
setlink(prev,base)
setlink(base,next)
return head,base
end
end
local function getcomponentindex(start)
if getid(start)~=glyph_code then
return 0
elseif getsubtype(start)==ligature_code then
local i=0
local components=getfield(start,"components")
while components do
i=i+getcomponentindex(components)
components=getnext(components)
end
return i
elseif not marks[getchar(start)] then
return 1
else
return 0
end
end
local a_noligature=attributes.private("noligature")
local function toligature(head,start,stop,char,dataset,sequence,markflag,discfound)
if getattr(start,a_noligature)==1 then
return head,start
end
if start==stop and getchar(start)==char then
resetinjection(start)
setchar(start,char)
return head,start
end
local components=getfield(start,"components")
if components then
end
local prev=getprev(start)
local next=getnext(stop)
local comp=start
setprev(start)
setnext(stop)
local base=copy_glyph(start)
if start==head then
head=base
end
resetinjection(base)
setchar(base,char)
setsubtype(base,ligature_code)
setfield(base,"components",comp)
if prev then
setnext(prev,base)
end
if next then
setprev(next,base)
end
setboth(base,prev,next)
if not discfound then
local deletemarks=markflag~="mark"
local components=start
local baseindex=0
local componentindex=0
local head=base
local current=base
while start do
local char=getchar(start)
if not marks[char] then
baseindex=baseindex+componentindex
componentindex=getcomponentindex(start)
elseif not deletemarks then
setligaindex(start,baseindex+getligaindex(start,componentindex))
if trace_marks then
logwarning("%s: keep mark %s, gets index %s",pref(dataset,sequence),gref(char),getligaindex(start))
end
local n=copy_node(start)
copyinjection(n,start)
head,current=insert_node_after(head,current,n)
elseif trace_marks then
logwarning("%s: delete mark %s",pref(dataset,sequence),gref(char))
end
start=getnext(start)
end
local start=getnext(current)
while start do
local char=ischar(start)
if char then
if marks[char] then
setligaindex(start,baseindex+getligaindex(start,componentindex))
if trace_marks then
logwarning("%s: set mark %s, gets index %s",pref(dataset,sequence),gref(char),getligaindex(start))
end
start=getnext(start)
else
break
end
else
break
end
end
else
local discprev,discnext=getboth(discfound)
if discprev and discnext then
local pre,post,replace,pretail,posttail,replacetail=getdisc(discfound,true)
if not replace then
local prev=getprev(base)
local current=comp
local previous=nil
local copied=nil
while current do
if getid(current)==glyph_code then
local n=copy_node(current)
if copied then
setlink(previous,n)
else
copied=n
end
previous=n
end
current=getnext(current)
end
setprev(discnext)
setnext(discprev)
if pre then
setlink(discprev,pre)
end
pre=comp
if post then
setlink(posttail,discnext)
setprev(post)
else
post=discnext
end
setlink(prev,discfound)
setlink(discfound,next)
setboth(base)
setfield(base,"components",copied)
setdisc(discfound,pre,post,base)
base=prev
end
end
end
return head,base
end
local function multiple_glyphs(head,start,multiple,ignoremarks,what)
local nofmultiples=#multiple
if nofmultiples>0 then
resetinjection(start)
setchar(start,multiple[1])
if nofmultiples>1 then
local sn=getnext(start)
for k=2,nofmultiples do
local n=copy_node(start)
resetinjection(n)
setchar(n,multiple[k])
insert_node_after(head,start,n)
start=n
end
if what==true then
elseif what>1 then
local m=multiple[nofmultiples]
for i=2,what do
local n=copy_node(start)
resetinjection(n)
setchar(n,m)
insert_node_after(head,start,n)
start=n
end
end
end
return head,start,true
else
if trace_multiples then
logprocess("no multiple for %s",gref(getchar(start)))
end
return head,start,false
end
end
local function get_alternative_glyph(start,alternatives,value)
local n=#alternatives
if value=="random" then
local r=getrandom and getrandom("glyph",1,n) or random(1,n)
return alternatives[r],trace_alternatives and formatters["value %a, taking %a"](value,r)
elseif value=="first" then
return alternatives[1],trace_alternatives and formatters["value %a, taking %a"](value,1)
elseif value=="last" then
return alternatives[n],trace_alternatives and formatters["value %a, taking %a"](value,n)
end
value=value==true and 1 or tonumber(value)
if type(value)~="number" then
return alternatives[1],trace_alternatives and formatters["invalid value %s, taking %a"](value,1)
end
if value>n then
local defaultalt=otf.defaultnodealternate
if defaultalt=="first" then
return alternatives[n],trace_alternatives and formatters["invalid value %s, taking %a"](value,1)
elseif defaultalt=="last" then
return alternatives[1],trace_alternatives and formatters["invalid value %s, taking %a"](value,n)
else
return false,trace_alternatives and formatters["invalid value %a, %s"](value,"out of range")
end
elseif value==0 then
return getchar(start),trace_alternatives and formatters["invalid value %a, %s"](value,"no change")
elseif value<1 then
return alternatives[1],trace_alternatives and formatters["invalid value %a, taking %a"](value,1)
else
return alternatives[value],trace_alternatives and formatters["value %a, taking %a"](value,value)
end
end
function handlers.gsub_single(head,start,dataset,sequence,replacement)
if trace_singles then
logprocess("%s: replacing %s by single %s",pref(dataset,sequence),gref(getchar(start)),gref(replacement))
end
resetinjection(start)
setchar(start,replacement)
return head,start,true
end
function handlers.gsub_alternate(head,start,dataset,sequence,alternative)
local kind=dataset[4]
local what=dataset[1]
local value=what==true and tfmdata.shared.features[kind] or what
local choice,comment=get_alternative_glyph(start,alternative,value)
if choice then
if trace_alternatives then
logprocess("%s: replacing %s by alternative %a to %s, %s",pref(dataset,sequence),gref(getchar(start)),gref(choice),comment)
end
resetinjection(start)
setchar(start,choice)
else
if trace_alternatives then
logwarning("%s: no variant %a for %s, %s",pref(dataset,sequence),value,gref(getchar(start)),comment)
end
end
return head,start,true
end
function handlers.gsub_multiple(head,start,dataset,sequence,multiple)
if trace_multiples then
logprocess("%s: replacing %s by multiple %s",pref(dataset,sequence),gref(getchar(start)),gref(multiple))
end
return multiple_glyphs(head,start,multiple,sequence.flags[1],dataset[1])
end
function handlers.gsub_ligature(head,start,dataset,sequence,ligature)
local current=getnext(start)
if not current then
return head,start,false,nil
end
local stop=nil
local startchar=getchar(start)
if marks[startchar] then
while current do
local char=ischar(current,currentfont)
if char then
local lg=ligature[char]
if lg then
stop=current
ligature=lg
current=getnext(current)
else
break
end
else
break
end
end
if stop then
local lig=ligature.ligature
if lig then
if trace_ligatures then
local stopchar=getchar(stop)
head,start=markstoligature(head,start,stop,lig)
logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(getchar(start)))
else
head,start=markstoligature(head,start,stop,lig)
end
return head,start,true,false
else
end
end
else
local skipmark=sequence.flags[1]
local discfound=false
local lastdisc=nil
while current do
local char,id=ischar(current,currentfont)
if char then
if skipmark and marks[char] then
current=getnext(current)
else
local lg=ligature[char]
if lg then
if not discfound and lastdisc then
discfound=lastdisc
lastdisc=nil
end
stop=current
ligature=lg
current=getnext(current)
else
break
end
end
elseif char==false then
break
elseif id==disc_code then
local replace=getfield(current,"replace")
if replace then
while replace do
local char,id=ischar(replace,currentfont)
if char then
local lg=ligature[char]
if lg then
ligature=lg
replace=getnext(replace)
else
return head,start,false,false
end
else
return head,start,false,false
end
end
stop=current
end
lastdisc=current
current=getnext(current)
else
break
end
end
local lig=ligature.ligature
if lig then
if stop then
if trace_ligatures then
local stopchar=getchar(stop)
head,start=toligature(head,start,stop,lig,dataset,sequence,skipmark,discfound)
logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(lig))
else
head,start=toligature(head,start,stop,lig,dataset,sequence,skipmark,discfound)
end
else
resetinjection(start)
setchar(start,lig)
if trace_ligatures then
logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(lig))
end
end
return head,start,true,discfound
else
end
end
return head,start,false,discfound
end
function handlers.gpos_single(head,start,dataset,sequence,kerns,rlmode,step,i,injection)
local startchar=getchar(start)
if step.format=="pair" then
local dx,dy,w,h=setpair(start,factor,rlmode,sequence.flags[4],kerns,injection)
if trace_kerns then
logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(dataset,sequence),gref(startchar),dx,dy,w,h)
end
else
local k=setkern(start,factor,rlmode,kerns,injection)
if trace_kerns then
logprocess("%s: shifting single %s by %p",pref(dataset,sequence),gref(startchar),k)
end
end
return head,start,false
end
function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,step,i,injection)
local snext=getnext(start)
if not snext then
return head,start,false
else
local prev=start
local done=false
while snext do
local nextchar=ischar(snext,currentfont)
if nextchar then
local krn=kerns[nextchar]
if not krn and marks[nextchar] then
prev=snext
snext=getnext(snext)
elseif not krn then
break
elseif step.format=="pair" then
local a,b=krn[1],krn[2]
if optimizekerns then
if not b and a[1]==0 and a[2]==0 and a[4]==0 then
local k=setkern(snext,factor,rlmode,a[3],injection)
if trace_kerns then
logprocess("%s: shifting single %s by %p",pref(dataset,sequence),gref(nextchar),k)
end
done=true
break
end
end
if a and #a>0 then
local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,injection)
if trace_kerns then
local startchar=getchar(start)
logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections")
end
end
if b and #b>0 then
local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,injection)
if trace_kerns then
local startchar=getchar(snext)
logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections")
end
end
done=true
break
elseif krn~=0 then
local k=setkern(snext,factor,rlmode,krn,injection)
if trace_kerns then
logprocess("%s: inserting kern %p between %s and %s as %s",pref(dataset,sequence),k,gref(getchar(prev)),gref(nextchar),injection or "injections")
end
done=true
break
else
break
end
else
break
end
end
return head,start,done
end
end
function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode)
local markchar=getchar(start)
if marks[markchar] then
local base=getprev(start)
if base then
local basechar=ischar(base,currentfont)
if basechar then
if marks[basechar] then
while base do
base=getprev(base)
if base then
basechar=ischar(base,currentfont)
if basechar then
if not marks[basechar] then
break
end
else
if trace_bugs then
logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),1)
end
return head,start,false
end
else
if trace_bugs then
logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),2)
end
return head,start,false
end
end
end
local ba=markanchors[1][basechar]
if ba then
local ma=markanchors[2]
local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
if trace_marks then
logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)",
pref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy)
end
return head,start,true
end
elseif trace_bugs then
logwarning("%s: nothing preceding, case %i",pref(dataset,sequence),1)
end
elseif trace_bugs then
logwarning("%s: nothing preceding, case %i",pref(dataset,sequence),2)
end
elseif trace_bugs then
logwarning("%s: mark %s is no mark",pref(dataset,sequence),gref(markchar))
end
return head,start,false
end
function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlmode)
local markchar=getchar(start)
if marks[markchar] then
local base=getprev(start)
if base then
local basechar=ischar(base,currentfont)
if basechar then
if marks[basechar] then
while base do
base=getprev(base)
if base then
basechar=ischar(base,currentfont)
if basechar then
if not marks[basechar] then
break
end
else
if trace_bugs then
logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),1)
end
return head,start,false
end
else
if trace_bugs then
logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),2)
end
return head,start,false
end
end
end
local ba=markanchors[1][basechar]
if ba then
local ma=markanchors[2]
if ma then
local index=getligaindex(start)
ba=ba[index]
if ba then
local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
if trace_marks then
logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)",
pref(dataset,sequence),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy)
end
return head,start,true
else
if trace_bugs then
logwarning("%s: no matching anchors for mark %s and baselig %s with index %a",pref(dataset,sequence),gref(markchar),gref(basechar),index)
end
end
end
elseif trace_bugs then
onetimemessage(currentfont,basechar,"no base anchors",report_fonts)
end
elseif trace_bugs then
logwarning("%s: prev node is no char, case %i",pref(dataset,sequence),1)
end
elseif trace_bugs then
logwarning("%s: prev node is no char, case %i",pref(dataset,sequence),2)
end
elseif trace_bugs then
logwarning("%s: mark %s is no mark",pref(dataset,sequence),gref(markchar))
end
return head,start,false
end
function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode)
local markchar=getchar(start)
if marks[markchar] then
local base=getprev(start)
local slc=getligaindex(start)
if slc then
while base do
local blc=getligaindex(base)
if blc and blc~=slc then
base=getprev(base)
else
break
end
end
end
if base then
local basechar=ischar(base,currentfont)
if basechar then
local ba=markanchors[1][basechar]
if ba then
local ma=markanchors[2]
local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true,checkmarks)
if trace_marks then
logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
pref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy)
end
return head,start,true
end
end
end
elseif trace_bugs then
logwarning("%s: mark %s is no mark",pref(dataset,sequence),gref(markchar))
end
return head,start,false
end
function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,step,i)
local done=false
local startchar=getchar(start)
if marks[startchar] then
if trace_cursive then
logprocess("%s: ignoring cursive for mark %s",pref(dataset,sequence),gref(startchar))
end
else
local nxt=getnext(start)
while not done and nxt do
local nextchar=ischar(nxt,currentfont)
if not nextchar then
break
elseif marks[nextchar] then
nxt=getnext(nxt)
else
local exit=exitanchors[3]
if exit then
local entry=exitanchors[1][nextchar]
if entry then
entry=entry[2]
if entry then
local dx,dy,bound=setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar])
if trace_cursive then
logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in %s mode",pref(dataset,sequence),gref(startchar),gref(nextchar),dx,dy,anchor,bound,mref(rlmode))
end
done=true
end
end
end
break
end
end
end
return head,start,done
end
local chainprocs={}
local function logprocess(...)
if trace_steps then
registermessage(...)
end
report_subchain(...)
end
local logwarning=report_subchain
local function logprocess(...)
if trace_steps then
registermessage(...)
end
report_chain(...)
end
local logwarning=report_chain
local function reversesub(head,start,stop,dataset,sequence,replacements,rlmode)
local char=getchar(start)
local replacement=replacements[char]
if replacement then
if trace_singles then
logprocess("%s: single reverse replacement of %s by %s",cref(dataset,sequence),gref(char),gref(replacement))
end
resetinjection(start)
setchar(start,replacement)
return head,start,true
else
return head,start,false
end
end
chainprocs.reversesub=reversesub
local function reportmoresteps(dataset,sequence)
logwarning("%s: more than 1 step",cref(dataset,sequence))
end
function chainprocs.gsub_single(head,start,stop,dataset,sequence,currentlookup,chainindex)
local steps=currentlookup.steps
local nofsteps=currentlookup.nofsteps
if nofsteps>1 then
reportmoresteps(dataset,sequence)
end
local current=start
while current do
local currentchar=ischar(current)
if currentchar then
local replacement=steps[1].coverage[currentchar]
if not replacement or replacement=="" then
if trace_bugs then
logwarning("%s: no single for %s",cref(dataset,sequence,chainindex),gref(currentchar))
end
else
if trace_singles then
logprocess("%s: replacing single %s by %s",cref(dataset,sequence,chainindex),gref(currentchar),gref(replacement))
end
resetinjection(current)
setchar(current,replacement)
end
return head,start,true
elseif currentchar==false then
break
elseif current==stop then
break
else
current=getnext(current)
end
end
return head,start,false
end
function chainprocs.gsub_multiple(head,start,stop,dataset,sequence,currentlookup)
local steps=currentlookup.steps
local nofsteps=currentlookup.nofsteps
if nofsteps>1 then
reportmoresteps(dataset,sequence)
end
local startchar=getchar(start)
local replacement=steps[1].coverage[startchar]
if not replacement or replacement=="" then
if trace_bugs then
logwarning("%s: no multiple for %s",cref(dataset,sequence),gref(startchar))
end
else
if trace_multiples then
logprocess("%s: replacing %s by multiple characters %s",cref(dataset,sequence),gref(startchar),gref(replacement))
end
return multiple_glyphs(head,start,replacement,sequence.flags[1],dataset[1])
end
return head,start,false
end
function chainprocs.gsub_alternate(head,start,stop,dataset,sequence,currentlookup)
local steps=currentlookup.steps
local nofsteps=currentlookup.nofsteps
if nofsteps>1 then
reportmoresteps(dataset,sequence)
end
local kind=dataset[4]
local what=dataset[1]
local value=what==true and tfmdata.shared.features[kind] or what
local current=start
while current do
local currentchar=ischar(current)
if currentchar then
local alternatives=steps[1].coverage[currentchar]
if alternatives then
local choice,comment=get_alternative_glyph(current,alternatives,value)
if choice then
if trace_alternatives then
logprocess("%s: replacing %s by alternative %a to %s, %s",cref(dataset,sequence),gref(char),choice,gref(choice),comment)
end
resetinjection(start)
setchar(start,choice)
else
if trace_alternatives then
logwarning("%s: no variant %a for %s, %s",cref(dataset,sequence),value,gref(char),comment)
end
end
end
return head,start,true
elseif currentchar==false then
break
elseif current==stop then
break
else
current=getnext(current)
end
end
return head,start,false
end
function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup,chainindex)
local steps=currentlookup.steps
local nofsteps=currentlookup.nofsteps
if nofsteps>1 then
reportmoresteps(dataset,sequence)
end
local startchar=getchar(start)
local ligatures=steps[1].coverage[startchar]
if not ligatures then
if trace_bugs then
logwarning("%s: no ligatures starting with %s",cref(dataset,sequence,chainindex),gref(startchar))
end
else
local current=getnext(start)
local discfound=false
local last=stop
local nofreplacements=1
local skipmark=currentlookup.flags[1]
while current do
local id=getid(current)
if id==disc_code then
if not discfound then
discfound=current
end
if current==stop then
break
else
current=getnext(current)
end
else
local schar=getchar(current)
if skipmark and marks[schar] then
current=getnext(current)
else
local lg=ligatures[schar]
if lg then
ligatures=lg
last=current
nofreplacements=nofreplacements+1
if current==stop then
break
else
current=getnext(current)
end
else
break
end
end
end
end
local ligature=ligatures.ligature
if ligature then
if chainindex then
stop=last
end
if trace_ligatures then
if start==stop then
logprocess("%s: replacing character %s by ligature %s case 3",cref(dataset,sequence,chainindex),gref(startchar),gref(ligature))
else
logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(dataset,sequence,chainindex),gref(startchar),gref(getchar(stop)),gref(ligature))
end
end
head,start=toligature(head,start,stop,ligature,dataset,sequence,skipmark,discfound)
return head,start,true,nofreplacements,discfound
elseif trace_bugs then
if start==stop then
logwarning("%s: replacing character %s by ligature fails",cref(dataset,sequence,chainindex),gref(startchar))
else
logwarning("%s: replacing character %s upto %s by ligature fails",cref(dataset,sequence,chainindex),gref(startchar),gref(getchar(stop)))
end
end
end
return head,start,false,0,false
end
function chainprocs.gpos_single(head,start,stop,dataset,sequence,currentlookup,rlmode,chainindex)
local steps=currentlookup.steps
local nofsteps=currentlookup.nofsteps
if nofsteps>1 then
reportmoresteps(dataset,sequence)
end
local startchar=getchar(start)
local step=steps[1]
local kerns=step.coverage[startchar]
if not kerns then
elseif step.format=="pair" then
local dx,dy,w,h=setpair(start,factor,rlmode,sequence.flags[4],kerns)
if trace_kerns then
logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),dx,dy,w,h)
end
else
local k=setkern(start,factor,rlmode,kerns,injection)
if trace_kerns then
logprocess("%s: shifting single %s by %p",cref(dataset,sequence),gref(startchar),k)
end
end
return head,start,false
end
function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlmode,chainindex)
local steps=currentlookup.steps
local nofsteps=currentlookup.nofsteps
if nofsteps>1 then
reportmoresteps(dataset,sequence)
end
local snext=getnext(start)
if snext then
local startchar=getchar(start)
local step=steps[1]
local kerns=step.coverage[startchar]
if kerns then
local prev=start
local done=false
while snext do
local nextchar=ischar(snext,currentfont)
if not nextchar then
break
end
local krn=kerns[nextchar]
if not krn and marks[nextchar] then
prev=snext
snext=getnext(snext)
elseif not krn then
break
elseif step.format=="pair" then
local a,b=krn[1],krn[2]
if optimizekerns then
if not b and a[1]==0 and a[2]==0 and a[4]==0 then
local k=setkern(snext,factor,rlmode,a[3],"injections")
if trace_kerns then
logprocess("%s: shifting single %s by %p",cref(dataset,sequence),gref(startchar),k)
end
done=true
break
end
end
if a and #a>0 then
local startchar=getchar(start)
local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,"injections")
if trace_kerns then
logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h)
end
end
if b and #b>0 then
local startchar=getchar(start)
local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,"injections")
if trace_kerns then
logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h)
end
end
done=true
break
elseif krn~=0 then
local k=setkern(snext,factor,rlmode,krn)
if trace_kerns then
logprocess("%s: inserting kern %s between %s and %s",cref(dataset,sequence),k,gref(getchar(prev)),gref(nextchar))
end
done=true
break
else
break
end
end
return head,start,done
end
end
return head,start,false
end
function chainprocs.gpos_mark2base(head,start,stop,dataset,sequence,currentlookup,rlmode)
local steps=currentlookup.steps
local nofsteps=currentlookup.nofsteps
if nofsteps>1 then
reportmoresteps(dataset,sequence)
end
local markchar=getchar(start)
if marks[markchar] then
local markanchors=steps[1].coverage[markchar]
if markanchors then
local base=getprev(start)
if base then
local basechar=ischar(base,currentfont)
if basechar then
if marks[basechar] then
while base do
base=getprev(base)
if base then
local basechar=ischar(base,currentfont)
if basechar then
if not marks[basechar] then
break
end
else
if trace_bugs then
logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),1)
end
return head,start,false
end
else
if trace_bugs then
logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),2)
end
return head,start,false
end
end
end
local ba=markanchors[1][basechar]
if ba then
local ma=markanchors[2]
if ma then
local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
if trace_marks then
logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)",
cref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy)
end
return head,start,true
end
end
elseif trace_bugs then
logwarning("%s: prev node is no char, case %i",cref(dataset,sequence),1)
end
elseif trace_bugs then
logwarning("%s: prev node is no char, case %i",cref(dataset,sequence),2)
end
elseif trace_bugs then
logwarning("%s: mark %s has no anchors",cref(dataset,sequence),gref(markchar))
end
elseif trace_bugs then
logwarning("%s: mark %s is no mark",cref(dataset,sequence),gref(markchar))
end
return head,start,false
end
function chainprocs.gpos_mark2ligature(head,start,stop,dataset,sequence,currentlookup,rlmode)
local steps=currentlookup.steps
local nofsteps=currentlookup.nofsteps
if nofsteps>1 then
reportmoresteps(dataset,sequence)
end
local markchar=getchar(start)
if marks[markchar] then
local markanchors=steps[1].coverage[markchar]
if markanchors then
local base=getprev(start)
if base then
local basechar=ischar(base,currentfont)
if basechar then
if marks[basechar] then
while base do
base=getprev(base)
if base then
local basechar=ischar(base,currentfont)
if basechar then
if not marks[basechar] then
break
end
else
if trace_bugs then
logwarning("%s: no base for mark %s, case %i",cref(dataset,sequence),markchar,1)
end
return head,start,false
end
else
if trace_bugs then
logwarning("%s: no base for mark %s, case %i",cref(dataset,sequence),markchar,2)
end
return head,start,false
end
end
end
local ba=markanchors[1][basechar]
if ba then
local ma=markanchors[2]
if ma then
local index=getligaindex(start)
ba=ba[index]
if ba then
local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
if trace_marks then
logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)",
cref(dataset,sequence),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy)
end
return head,start,true
end
end
end
elseif trace_bugs then
logwarning("%s, prev node is no char, case %i",cref(dataset,sequence),1)
end
elseif trace_bugs then
logwarning("%s, prev node is no char, case %i",cref(dataset,sequence),2)
end
elseif trace_bugs then
logwarning("%s, mark %s has no anchors",cref(dataset,sequence),gref(markchar))
end
elseif trace_bugs then
logwarning("%s, mark %s is no mark",cref(dataset,sequence),gref(markchar))
end
return head,start,false
end
function chainprocs.gpos_mark2mark(head,start,stop,dataset,sequence,currentlookup,rlmode)
local steps=currentlookup.steps
local nofsteps=currentlookup.nofsteps
if nofsteps>1 then
reportmoresteps(dataset,sequence)
end
local markchar=getchar(start)
if marks[markchar] then
local markanchors=steps[1].coverage[markchar]
if markanchors then
local base=getprev(start)
local slc=getligaindex(start)
if slc then
while base do
local blc=getligaindex(base)
if blc and blc~=slc then
base=getprev(base)
else
break
end
end
end
if base then
local basechar=ischar(base,currentfont)
if basechar then
local ba=markanchors[1][basechar]
if ba then
local ma=markanchors[2]
if ma then
local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true,checkmarks)
if trace_marks then
logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
cref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy)
end
return head,start,true
end
end
elseif trace_bugs then
logwarning("%s: prev node is no mark, case %i",cref(dataset,sequence),1)
end
elseif trace_bugs then
logwarning("%s: prev node is no mark, case %i",cref(dataset,sequence),2)
end
elseif trace_bugs then
logwarning("%s: mark %s has no anchors",cref(dataset,sequence),gref(markchar))
end
elseif trace_bugs then
logwarning("%s: mark %s is no mark",cref(dataset,sequence),gref(markchar))
end
return head,start,false
end
function chainprocs.gpos_cursive(head,start,stop,dataset,sequence,currentlookup,rlmode)
local steps=currentlookup.steps
local nofsteps=currentlookup.nofsteps
if nofsteps>1 then
reportmoresteps(dataset,sequence)
end
local startchar=getchar(start)
local exitanchors=steps[1].coverage[startchar]
if exitanchors then
local done=false
if marks[startchar] then
if trace_cursive then
logprocess("%s: ignoring cursive for mark %s",pref(dataset,sequence),gref(startchar))
end
else
local nxt=getnext(start)
while not done and nxt do
local nextchar=ischar(nxt,currentfont)
if not nextchar then
break
elseif marks[nextchar] then
nxt=getnext(nxt)
else
local exit=exitanchors[3]
if exit then
local entry=exitanchors[1][nextchar]
if entry then
entry=entry[2]
if entry then
local dx,dy,bound=setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar])
if trace_cursive then
logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in %s mode",pref(dataset,sequence),gref(startchar),gref(nextchar),dx,dy,anchor,bound,mref(rlmode))
end
done=true
break
end
end
elseif trace_bugs then
onetimemessage(currentfont,startchar,"no entry anchors",report_fonts)
end
break
end
end
end
return head,start,done
else
if trace_cursive and trace_details then
logprocess("%s, cursive %s is already done",pref(dataset,sequence),gref(getchar(start)),alreadydone)
end
return head,start,false
end
end
local function show_skip(dataset,sequence,char,ck,class)
logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(dataset,sequence),gref(char),class,ck[1],ck[8] or ck[2])
end
local new_kern=nuts.pool.kern
local function checked(head)
local current=head
while current do
if getid(current)==glue_code then
local kern=new_kern(getfield(current,"width"))
if head==current then
local next=getnext(current)
if next then
setlink(kern,next)
end
flush_node(current)
head=kern
current=next
else
local prev,next=getboth(current)
setlink(prev,kern)
setlink(kern,next)
flush_node(current)
current=next
end
else
current=getnext(current)
end
end
return head
end
local function setdiscchecked(d,pre,post,replace)
if pre then pre=checked(pre) end
if post then post=checked(post) end
if replace then replace=checked(replace) end
setdisc(d,pre,post,replace)
end
local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,ck,chainproc)
if not start then
return head,start,false
end
local startishead=start==head
local seq=ck[3]
local f=ck[4]
local l=ck[5]
local s=#seq
local done=false
local sweepnode=sweepnode
local sweeptype=sweeptype
local sweepoverflow=false
local checkdisc=getprev(head)
local keepdisc=not sweepnode
local lookaheaddisc=nil
local backtrackdisc=nil
local current=start
local last=start
local prev=getprev(start)
local hasglue=false
local i=f
while i<=l do
local id=getid(current)
if id==glyph_code then
i=i+1
last=current
current=getnext(current)
elseif id==glue_code then
i=i+1
last=current
current=getnext(current)
hasglue=true
elseif id==disc_code then
if keepdisc then
keepdisc=false
if notmatchpre[current]~=notmatchreplace[current] then
lookaheaddisc=current
end
local replace=getfield(current,"replace")
while replace and i<=l do
if getid(replace)==glyph_code then
i=i+1
end
replace=getnext(replace)
end
last=current
current=getnext(c)
else
head,current=flattendisk(head,current)
end
else
last=current
current=getnext(current)
end
if current then
elseif sweepoverflow then
break
elseif sweeptype=="post" or sweeptype=="replace" then
current=getnext(sweepnode)
if current then
sweeptype=nil
sweepoverflow=true
else
break
end
else
break
end
end
if sweepoverflow then
local prev=current and getprev(current)
if not current or prev~=sweepnode then
local head=getnext(sweepnode)
local tail=nil
if prev then
tail=prev
setprev(current,sweepnode)
else
tail=find_node_tail(head)
end
setnext(sweepnode,current)
setprev(head)
setnext(tail)
appenddisc(sweepnode,head)
end
end
if l<s then
local i=l
local t=sweeptype=="post" or sweeptype=="replace"
while current and i<s do
local id=getid(current)
if id==glyph_code then
i=i+1
current=getnext(current)
elseif id==glue_code then
i=i+1
current=getnext(current)
hasglue=true
elseif id==disc_code then
if keepdisc then
keepdisc=false
if notmatchpre[current]~=notmatchreplace[current] then
lookaheaddisc=current
end
local replace=getfield(c,"replace")
while replace and i<s do
if getid(replace)==glyph_code then
i=i+1
end
replace=getnext(replace)
end
current=getnext(current)
elseif notmatchpre[current]~=notmatchreplace[current] then
head,current=flattendisk(head,current)
else
current=getnext(current)
end
else
current=getnext(current)
end
if not current and t then
current=getnext(sweepnode)
if current then
sweeptype=nil
end
end
end
end
if f>1 then
local current=prev
local i=f
local t=sweeptype=="pre" or sweeptype=="replace"
if not current and t and current==checkdisk then
current=getprev(sweepnode)
end
while current and i>1 do
local id=getid(current)
if id==glyph_code then
i=i-1
elseif id==glue_code then
i=i-1
hasglue=true
elseif id==disc_code then
if keepdisc then
keepdisc=false
if notmatchpost[current]~=notmatchreplace[current] then
backtrackdisc=current
end
local replace=getfield(current,"replace")
while replace and i>1 do
if getid(replace)==glyph_code then
i=i-1
end
replace=getnext(replace)
end
elseif notmatchpost[current]~=notmatchreplace[current] then
head,current=flattendisk(head,current)
end
end
current=getprev(current)
if t and current==checkdisk then
current=getprev(sweepnode)
end
end
end
local ok=false
if lookaheaddisc then
local cf=start
local cl=getprev(lookaheaddisc)
local cprev=getprev(start)
local insertedmarks=0
while cprev do
local char=ischar(cf,currentfont)
if char and marks[char] then
insertedmarks=insertedmarks+1
cf=cprev
startishead=cf==head
cprev=getprev(cprev)
else
break
end
end
setprev(lookaheaddisc,cprev)
if cprev then
setnext(cprev,lookaheaddisc)
end
setprev(cf)
setnext(cl)
if startishead then
head=lookaheaddisc
end
local pre,post,replace=getdisc(lookaheaddisc)
local new=copy_node_list(cf)
local cnew=new
for i=1,insertedmarks do
cnew=getnext(cnew)
end
local clast=cnew
for i=f,l do
clast=getnext(clast)
end
if not notmatchpre[lookaheaddisc] then
cf,start,ok=chainproc(cf,start,last,dataset,sequence,chainlookup,rlmode,k)
end
if not notmatchreplace[lookaheaddisc] then
new,cnew,ok=chainproc(new,cnew,clast,dataset,sequence,chainlookup,rlmode,k)
end
if pre then
setlink(cl,pre)
end
if replace then
local tail=find_node_tail(new)
setlink(tail,replace)
end
if hasglue then
setdiscchecked(lookaheaddisc,cf,post,new)
else
setdisc(lookaheaddisc,cf,post,new)
end
start=getprev(lookaheaddisc)
sweephead[cf]=getnext(clast)
sweephead[new]=getnext(last)
elseif backtrackdisc then
local cf=getnext(backtrackdisc)
local cl=start
local cnext=getnext(start)
local insertedmarks=0
while cnext do
local char=ischar(cnext,currentfont)
if char and marks[char] then
insertedmarks=insertedmarks+1
cl=cnext
cnext=getnext(cnext)
else
break
end
end
if cnext then
setprev(cnext,backtrackdisc)
end
setnext(backtrackdisc,cnext)
setprev(cf)
setnext(cl)
local pre,post,replace,pretail,posttail,replacetail=getdisc(backtrackdisc,true)
local new=copy_node_list(cf)
local cnew=find_node_tail(new)
for i=1,insertedmarks do
cnew=getprev(cnew)
end
local clast=cnew
for i=f,l do
clast=getnext(clast)
end
if not notmatchpost[backtrackdisc] then
cf,start,ok=chainproc(cf,start,last,dataset,sequence,chainlookup,rlmode,k)
end
if not notmatchreplace[backtrackdisc] then
new,cnew,ok=chainproc(new,cnew,clast,dataset,sequence,chainlookup,rlmode,k)
end
if post then
setlink(posttail,cf)
else
post=cf
end
if replace then
setlink(replacetail,new)
else
replace=new
end
if hasglue then
setdiscchecked(backtrackdisc,pre,post,replace)
else
setdisc(backtrackdisc,pre,post,replace)
end
start=getprev(backtrackdisc)
sweephead[post]=getnext(clast)
sweephead[replace]=getnext(last)
else
head,start,ok=chainproc(head,start,last,dataset,sequence,chainlookup,rlmode,k)
end
return head,start,ok
end
local noflags={ false,false,false,false }
local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode)
local sweepnode=sweepnode
local sweeptype=sweeptype
local currentfont=currentfont
local diskseen=false
local checkdisc=getprev(head)
local flags=sequence.flags or noflags
local done=false
local skipmark=flags[1]
local skipligature=flags[2]
local skipbase=flags[3]
local markclass=sequence.markclass
local skipped=false
for k=1,#contexts do
local match=true
local current=start
local last=start
local ck=contexts[k]
local seq=ck[3]
local s=#seq
local size=1
if s==1 then
local char=ischar(current,currentfont)
if char then
match=seq[1][char]
end
else
local f=ck[4]
local l=ck[5]
size=l-f+1
if size>1 then
local discfound=nil
local n=f+1
last=getnext(last)
while n<=l do
if not last and (sweeptype=="post" or sweeptype=="replace") then
last=getnext(sweepnode)
sweeptype=nil
end
if last then
local char,id=ischar(last,currentfont)
if char then
local ccd=descriptions[char]
if ccd then
local class=ccd.class or "base"
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(dataset,sequence,char,ck,class)
end
last=getnext(last)
elseif seq[n][char] then
if n<l then
last=getnext(last)
end
n=n+1
else
if discfound then
notmatchreplace[discfound]=true
match=not notmatchpre[discfound]
else
match=false
end
break
end
else
if discfound then
notmatchreplace[discfound]=true
match=not notmatchpre[discfound]
else
match=false
end
break
end
elseif char==false then
if discfound then
notmatchreplace[discfound]=true
match=not notmatchpre[discfound]
else
match=false
end
break
elseif id==disc_code then
diskseen=true
discfound=last
notmatchpre[last]=nil
notmatchpost[last]=true
notmatchreplace[last]=nil
local pre,post,replace=getdisc(last)
if pre then
local n=n
while pre do
if seq[n][getchar(pre)] then
n=n+1
pre=getnext(pre)
if n>l then
break
end
else
notmatchpre[last]=true
break
end
end
if n<=l then
notmatchpre[last]=true
end
else
notmatchpre[last]=true
end
if replace then
while replace do
if seq[n][getchar(replace)] then
n=n+1
replace=getnext(replace)
if n>l then
break
end
else
notmatchreplace[last]=true
match=not notmatchpre[last]
break
end
end
match=not notmatchpre[last]
end
last=getnext(last)
else
match=false
break
end
else
match=false
break
end
end
end
if match and f>1 then
local prev=getprev(start)
if prev then
if prev==checkdisc and (sweeptype=="pre" or sweeptype=="replace") then
prev=getprev(sweepnode)
end
if prev then
local discfound=nil
local n=f-1
while n>=1 do
if prev then
local char,id=ischar(prev,currentfont)
if char then
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(dataset,sequence,char,ck,class)
end
prev=getprev(prev)
elseif seq[n][char] then
if n>1 then
prev=getprev(prev)
end
n=n-1
else
if discfound then
notmatchreplace[discfound]=true
match=not notmatchpost[discfound]
else
match=false
end
break
end
else
if discfound then
notmatchreplace[discfound]=true
match=not notmatchpost[discfound]
else
match=false
end
break
end
elseif char==false then
if discfound then
notmatchreplace[discfound]=true
match=not notmatchpost[discfound]
else
match=false
end
break
elseif id==disc_code then
diskseen=true
discfound=prev
notmatchpre[prev]=true
notmatchpost[prev]=nil
notmatchreplace[prev]=nil
local pre,post,replace,pretail,posttail,replacetail=getdisc(prev,true)
if pre~=start and post~=start and replace~=start then
if post then
local n=n
while posttail do
if seq[n][getchar(posttail)] then
n=n-1
if posttail==post then
break
else
posttail=getprev(posttail)
if n<1 then
break
end
end
else
notmatchpost[prev]=true
break
end
end
if n>=1 then
notmatchpost[prev]=true
end
else
notmatchpost[prev]=true
end
if replace then
while replacetail do
if seq[n][getchar(replacetail)] then
n=n-1
if replacetail==replace then
break
else
replacetail=getprev(replacetail)
if n<1 then
break
end
end
else
notmatchreplace[prev]=true
match=not notmatchpost[prev]
break
end
end
if not match then
break
end
end
end
prev=getprev(prev)
elseif seq[n][32] and isspace(prev,threshold) then
n=n-1
prev=getprev(prev)
else
match=false
break
end
else
match=false
break
end
end
else
match=false
end
else
match=false
end
end
if match and s>l then
local current=last and getnext(last)
if not current then
if sweeptype=="post" or sweeptype=="replace" then
current=getnext(sweepnode)
end
end
if current then
local discfound=nil
local n=l+1
while n<=s do
if current then
local char,id=ischar(current,currentfont)
if char then
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(dataset,sequence,char,ck,class)
end
current=getnext(current)
elseif seq[n][char] then
if n<s then
current=getnext(current)
end
n=n+1
else
if discfound then
notmatchreplace[discfound]=true
match=not notmatchpre[discfound]
else
match=false
end
break
end
else
if discfound then
notmatchreplace[discfound]=true
match=not notmatchpre[discfound]
else
match=false
end
break
end
elseif char==false then
if discfound then
notmatchreplace[discfound]=true
match=not notmatchpre[discfound]
else
match=false
end
break
elseif id==disc_code then
diskseen=true
discfound=current
notmatchpre[current]=nil
notmatchpost[current]=true
notmatchreplace[current]=nil
local pre,post,replace=getdisc(current)
if pre then
local n=n
while pre do
if seq[n][getchar(pre)] then
n=n+1
pre=getnext(pre)
if n>s then
break
end
else
notmatchpre[current]=true
break
end
end
if n<=s then
notmatchpre[current]=true
end
else
notmatchpre[current]=true
end
if replace then
while replace do
if seq[n][getchar(replace)] then
n=n+1
replace=getnext(replace)
if n>s then
break
end
else
notmatchreplace[current]=true
match=notmatchpre[current]
break
end
end
if not match then
break
end
else
end
current=getnext(current)
elseif seq[n][32] and isspace(current,threshold) then
n=n+1
current=getnext(current)
else
match=false
break
end
else
match=false
break
end
end
else
match=false
end
end
end
if match then
local diskchain=diskseen or sweepnode
if trace_contexts then
local rule=ck[1]
local lookuptype=ck[8] or ck[2]
local first=ck[4]
local last=ck[5]
local char=getchar(start)
logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a",
cref(dataset,sequence),rule,gref(char),first-1,last-first+1,s-last,lookuptype)
end
local chainlookups=ck[6]
if chainlookups then
local nofchainlookups=#chainlookups
if size==1 then
local chainlookup=chainlookups[1]
local chainkind=chainlookup.type
local chainproc=chainprocs[chainkind]
if chainproc then
local ok
if diskchain then
head,start,ok=chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,1,ck,chainproc)
else
head,start,ok=chainproc(head,start,last,dataset,sequence,chainlookup,rlmode,1)
end
if ok then
done=true
end
else
logprocess("%s: %s is not yet supported (1)",cref(dataset,sequence),chainkind)
end
else
local i=1
while start do
if skipped then
while start do
local char=getchar(start)
local ccd=descriptions[char]
if ccd then
local class=ccd.class or "base"
if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
start=getnext(start)
else
break
end
else
break
end
end
end
local chainlookup=chainlookups[i]
if chainlookup then
local chainkind=chainlookup.type
local chainproc=chainprocs[chainkind]
if chainproc then
local ok,n
if diskchain then
head,start,ok=chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,i,ck,chainproc)
else
head,start,ok,n=chainproc(head,start,last,dataset,sequence,chainlookup,rlmode,i)
end
if ok then
done=true
if n and n>1 and i+n>nofchainlookups then
break
end
end
else
logprocess("%s: %s is not yet supported (2)",cref(dataset,sequence),chainkind)
end
end
i=i+1
if i>size or not start then
break
elseif start then
start=getnext(start)
end
end
end
else
local replacements=ck[7]
if replacements then
head,start,done=reversesub(head,start,last,dataset,sequence,replacements,rlmode)
else
done=true
if trace_contexts then
logprocess("%s: skipping match",cref(dataset,sequence))
end
end
end
if done then
break
end
end
end
if diskseen then
notmatchpre={}
notmatchpost={}
notmatchreplace={}
end
return head,start,done
end
handlers.gsub_context=handle_contextchain
handlers.gsub_contextchain=handle_contextchain
handlers.gsub_reversecontextchain=handle_contextchain
handlers.gpos_contextchain=handle_contextchain
handlers.gpos_context=handle_contextchain
local function chained_contextchain(head,start,stop,dataset,sequence,currentlookup,rlmode)
local steps=currentlookup.steps
local nofsteps=currentlookup.nofsteps
if nofsteps>1 then
reportmoresteps(dataset,sequence)
end
return handle_contextchain(head,start,dataset,sequence,currentlookup,rlmode)
end
chainprocs.gsub_context=chained_contextchain
chainprocs.gsub_contextchain=chained_contextchain
chainprocs.gsub_reversecontextchain=chained_contextchain
chainprocs.gpos_contextchain=chained_contextchain
chainprocs.gpos_context=chained_contextchain
local missing=setmetatableindex("table")
local function logprocess(...)
if trace_steps then
registermessage(...)
end
report_process(...)
end
local logwarning=report_process
local function report_missing_coverage(dataset,sequence)
local t=missing[currentfont]
if not t[sequence] then
t[sequence]=true
logwarning("missing coverage for feature %a, lookup %a, type %a, font %a, name %a",
dataset[4],sequence.name,sequence.type,currentfont,tfmdata.properties.fullname)
end
end
local resolved={}
local sequencelists=setmetatableindex(function(t,font)
local sequences=fontdata[font].resources.sequences
if not sequences or not next(sequences) then
sequences=false
end
t[font]=sequences
return sequences
end)
local autofeatures=fonts.analyzers.features
local featuretypes=otf.tables.featuretypes
local defaultscript=otf.features.checkeddefaultscript
local defaultlanguage=otf.features.checkeddefaultlanguage
local function initialize(sequence,script,language,enabled,autoscript,autolanguage)
local features=sequence.features
if features then
local order=sequence.order
if order then
local featuretype=featuretypes[sequence.type or "unknown"]
for i=1,#order do
local kind=order[i]
local valid=enabled[kind]
if valid then
local scripts=features[kind]
local languages=scripts and (
scripts[script] or
scripts[wildcard] or
(autoscript and defaultscript(featuretype,autoscript,scripts))
)
local enabled=languages and (
languages[language] or
languages[wildcard] or
(autolanguage and defaultlanguage(featuretype,autolanguage,languages))
)
if enabled then
return { valid,autofeatures[kind] or false,sequence,kind }
end
end
end
else
end
end
return false
end
function otf.dataset(tfmdata,font)
local shared=tfmdata.shared
local properties=tfmdata.properties
local language=properties.language or "dflt"
local script=properties.script or "dflt"
local enabled=shared.features
local autoscript=enabled and enabled.autoscript
local autolanguage=enabled and enabled.autolanguage
local res=resolved[font]
if not res then
res={}
resolved[font]=res
end
local rs=res[script]
if not rs then
rs={}
res[script]=rs
end
local rl=rs[language]
if not rl then
rl={
}
rs[language]=rl
local sequences=tfmdata.resources.sequences
if sequences then
for s=1,#sequences do
local v=enabled and initialize(sequences[s],script,language,enabled,autoscript,autolanguage)
if v then
rl[#rl+1]=v
end
end
end
end
return rl
end
local function report_disc(what,n)
report_run("%s: %s > %s",what,n,languages.serializediscretionary(n))
end
local function kernrun(disc,k_run,font,attr,...)
if trace_kernruns then
report_disc("kern",disc)
end
local prev,next=getboth(disc)
local nextstart=next
local done=false
local pre,post,replace,pretail,posttail,replacetail=getdisc(disc,true)
local prevmarks=prev
while prevmarks do
local char=ischar(prevmarks,font)
if char and marks[char] then
prevmarks=getprev(prevmarks)
else
break
end
end
if prev and not ischar(prev,font) then
prev=false
end
if next and not ischar(next,font) then
next=false
end
if pre then
if k_run(pre,"injections",nil,font,attr,...) then
done=true
end
if prev then
local nest=getprev(pre)
setlink(prev,pre)
if k_run(prevmarks,"preinjections",pre,font,attr,...) then
done=true
end
setprev(pre,nest)
setnext(prev,disc)
end
end
if post then
if k_run(post,"injections",nil,font,attr,...) then
done=true
end
if next then
setlink(posttail,next)
if k_run(posttail,"postinjections",next,font,attr,...) then
done=true
end
setnext(posttail)
setprev(next,disc)
end
end
if replace then
if k_run(replace,"injections",nil,font,attr,...) then
done=true
end
if prev then
local nest=getprev(replace)
setlink(prev,replace)
if k_run(prevmarks,"replaceinjections",replace,font,attr,...) then
done=true
end
setprev(replace,nest)
setnext(prev,disc)
end
if next then
setlink(replacetail,next)
if k_run(replacetail,"replaceinjections",next,font,attr,...) then
done=true
end
setnext(replacetail)
setprev(next,disc)
end
elseif prev and next then
setlink(prev,next)
if k_run(prevmarks,"emptyinjections",next,font,attr,...) then
done=true
end
setlink(prev,disc)
setlink(disc,next)
end
return nextstart,done
end
local function comprun(disc,c_run,...)
if trace_compruns then
report_disc("comp",disc)
end
local pre,post,replace=getdisc(disc)
local renewed=false
if pre then
sweepnode=disc
sweeptype="pre"
local new,done=c_run(pre,...)
if done then
pre=new
renewed=true
end
end
if post then
sweepnode=disc
sweeptype="post"
local new,done=c_run(post,...)
if done then
post=new
renewed=true
end
end
if replace then
sweepnode=disc
sweeptype="replace"
local new,done=c_run(replace,...)
if done then
replace=new
renewed=true
end
end
sweepnode=nil
sweeptype=nil
if renewed then
setdisc(disc,pre,post,replace)
end
return getnext(disc),renewed
end
local function testrun(disc,t_run,c_run,...)
if trace_testruns then
report_disc("test",disc)
end
local prev,next=getboth(disc)
if not next then
return
end
local pre,post,replace,pretail,posttail,replacetail=getdisc(disc,true)
local done=false
if replace and prev then
setlink(replacetail,next)
local ok,overflow=t_run(replace,next,...)
if ok and overflow then
setfield(disc,"replace")
setlink(prev,replace)
setboth(disc)
flush_node_list(disc)
return replace,true
else
setnext(replacetail)
setprev(next,disc)
end
end
local renewed=false
if pre then
sweepnode=disc
sweeptype="pre"
local new,ok=c_run(pre,...)
if ok then
pre=new
renewed=true
end
end
if post then
sweepnode=disc
sweeptype="post"
local new,ok=c_run(post,...)
if ok then
post=new
renewed=true
end
end
if replace then
sweepnode=disc
sweeptype="replace"
local new,ok=c_run(replace,...)
if ok then
replace=new
renewed=true
end
end
sweepnode=nil
sweeptype=nil
if renewed then
setdisc(disc,pre,post,replace)
return next,true
else
return next,done
end
end
local nesting=0
local function c_run_single(head,font,attr,lookupcache,step,dataset,sequence,rlmode,handler)
local done=false
local sweep=sweephead[head]
if sweep then
start=sweep
sweephead[head]=nil
else
start=head
end
while start do
local char=ischar(start,font)
if char then
local a=attr and getattr(start,0)
if not a or (a==attr) then
local lookupmatch=lookupcache[char]
if lookupmatch then
local ok
head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1)
if ok then
done=true
end
end
if start then
start=getnext(start)
end
else
start=getnext(start)
end
elseif char==false then
return head,done
elseif sweep then
return head,done
else
start=getnext(start)
end
end
return head,done
end
local function t_run_single(start,stop,font,attr,lookupcache)
while start~=stop do
local char=ischar(start,font)
if char then
local a=attr and getattr(start,0)
if not a or (a==attr) then
local lookupmatch=lookupcache[char]
if lookupmatch then
local s=getnext(start)
local l=nil
local d=0
while s do
if s==stop then
d=1
elseif d>0 then
d=d+1
end
local lg=lookupmatch[getchar(s)]
if lg then
l=lg
s=getnext(s)
else
break
end
end
if l and l.ligature then
return true,d>1
end
end
else
end
start=getnext(start)
else
break
end
end
end
local function k_run_single(sub,injection,last,font,attr,lookupcache,step,dataset,sequence,rlmode,handler)
local a=attr and getattr(sub,0)
if not a or (a==attr) then
for n in traverse_nodes(sub) do
if n==last then
break
end
local char=ischar(n)
if char then
local lookupmatch=lookupcache[char]
if lookupmatch then
local h,d,ok=handler(sub,n,dataset,sequence,lookupmatch,rlmode,step,1,injection)
if ok then
return true
end
end
end
end
end
end
local function c_run_multiple(head,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler)
local done=false
local sweep=sweephead[head]
if sweep then
start=sweep
sweephead[head]=nil
else
start=head
end
while start do
local char=ischar(start,font)
if char then
local a=attr and getattr(start,0)
if not a or (a==attr) then
for i=1,nofsteps do
local step=steps[i]
local lookupcache=step.coverage
if lookupcache then
local lookupmatch=lookupcache[char]
if lookupmatch then
local ok
head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i)
if ok then
done=true
break
elseif not start then
break
end
end
else
report_missing_coverage(dataset,sequence)
end
end
if start then
start=getnext(start)
end
else
start=getnext(start)
end
elseif char==false then
return head,done
elseif sweep then
return head,done
else
start=getnext(start)
end
end
return head,done
end
local function t_run_multiple(start,stop,font,attr,steps,nofsteps)
while start~=stop do
local char=ischar(start,font)
if char then
local a=attr and getattr(start,0)
if not a or (a==attr) then
for i=1,nofsteps do
local step=steps[i]
local lookupcache=step.coverage
if lookupcache then
local lookupmatch=lookupcache[char]
if lookupmatch then
local s=getnext(start)
local l=nil
local d=0
while s do
if s==stop then
d=1
elseif d>0 then
d=d+1
end
local lg=lookupmatch[getchar(s)]
if lg then
l=lg
s=getnext(s)
else
break
end
end
if l and l.ligature then
return true,d>1
end
end
else
report_missing_coverage(dataset,sequence)
end
end
else
end
start=getnext(start)
else
break
end
end
end
local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler)
local a=attr and getattr(sub,0)
if not a or (a==attr) then
for n in traverse_nodes(sub) do
if n==last then
break
end
local char=ischar(n)
if char then
for i=1,nofsteps do
local step=steps[i]
local lookupcache=step.coverage
if lookupcache then
local lookupmatch=lookupcache[char]
if lookupmatch then
local h,d,ok=handler(head,n,dataset,sequence,lookupmatch,step,rlmode,i,injection)
if ok then
return true
end
end
else
report_missing_coverage(dataset,sequence)
end
end
end
end
end
end
local function txtdirstate(start,stack,top,rlparmode)
local dir=getfield(start,"dir")
local new=1
if dir=="+TRT" then
top=top+1
stack[top]=dir
new=-1
elseif dir=="+TLT" then
top=top+1
stack[top]=dir
elseif dir=="-TRT" or dir=="-TLT" then
top=top-1
if stack[top]=="+TRT" then
new=-1
end
else
new=rlparmode
end
if trace_directions then
report_process("directions after txtdir %a: parmode %a, txtmode %a, level %a",dir,mref(rlparmode),mref(new),top)
end
return getnext(start),top,new
end
local function pardirstate(start)
local dir=getfield(start,"dir")
local new=0
if dir=="TLT" then
new=1
elseif dir=="TRT" then
new=-1
end
if trace_directions then
report_process("directions after pardir %a: parmode %a",dir,mref(new))
end
return getnext(start),new,new
end
otf.helpers=otf.helpers or {}
otf.helpers.txtdirstate=txtdirstate
otf.helpers.pardirstate=pardirstate
local function featuresprocessor(head,font,attr)
local sequences=sequencelists[font]
if not sequencelists then
return head,false
end
nesting=nesting+1
if nesting==1 then
currentfont=font
tfmdata=fontdata[font]
descriptions=tfmdata.descriptions
characters=tfmdata.characters
marks=tfmdata.resources.marks
threshold,
factor=getthreshold(font)
checkmarks=tfmdata.properties.checkmarks
elseif currentfont~=font then
report_warning("nested call with a different font, level %s, quitting",nesting)
nesting=nesting-1
return head,false
end
head=tonut(head)
if trace_steps then
checkstep(head)
end
local rlmode=0
local done=false
local datasets=otf.dataset(tfmdata,font,attr)
local forcedisc=alwaysdisc or not attr
local dirstack={}
sweephead={}
for s=1,#datasets do
local dataset=datasets[s]
local attribute=dataset[2]
local sequence=dataset[3]
local rlparmode=0
local topstack=0
local success=false
local typ=sequence.type
local gpossing=typ=="gpos_single" or typ=="gpos_pair"
local handler=handlers[typ]
local steps=sequence.steps
local nofsteps=sequence.nofsteps
if not steps then
local h,d,ok=handler(head,head,dataset,sequence,nil,nil,nil,0,font,attr)
if ok then
success=true
if h then
head=h
end
end
elseif typ=="gsub_reversecontextchain" then
local start=find_node_tail(head)
while start do
local char=ischar(start,font)
if char then
local a=attr and getattr(start,0)
if not a or (a==attr) then
for i=1,nofsteps do
local step=steps[i]
local lookupcache=step.coverage
if lookupcache then
local lookupmatch=lookupcache[char]
if lookupmatch then
local ok
head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i)
if ok then
success=true
break
end
end
else
report_missing_coverage(dataset,sequence)
end
end
if start then
start=getprev(start)
end
else
start=getprev(start)
end
else
start=getprev(start)
end
end
else
local start=head
rlmode=0
if nofsteps==1 then
local step=steps[1]
local lookupcache=step.coverage
if not lookupcache then
report_missing_coverage(dataset,sequence)
else
while start do
local char,id=ischar(start,font)
if char then
local a=attr and getattr(start,0)
if a then
a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
else
a=not attribute or getprop(start,a_state)==attribute
end
if a then
local lookupmatch=lookupcache[char]
if lookupmatch then
local ok
head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1)
if ok then
success=true
end
end
if start then
start=getnext(start)
end
else
start=getnext(start)
end
elseif char==false then
start=getnext(start)
elseif id==disc_code then
local a=forcedisc or getsubtype(start)==discretionary_code or getattr(start,0)==attr
if a then
local ok
if gpossing then
start,ok=kernrun(start,k_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler)
elseif typ=="gsub_ligature" then
start,ok=testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler)
else
start,ok=comprun(start,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler)
end
if ok then
success=true
end
else
start=getnext(start)
end
elseif id==math_code then
start=getnext(end_of_math(start))
elseif id==dir_code then
start,topstack,rlmode=txtdirstate(start,dirstack,topstack,rlparmode)
elseif id==localpar_code then
start,rlparmode,rlmode=pardirstate(start)
else
start=getnext(start)
end
end
end
else
while start do
local char,id=ischar(start,font)
if char then
local a=attr and getattr(start,0)
if a then
a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
else
a=not attribute or getprop(start,a_state)==attribute
end
if a then
for i=1,nofsteps do
local step=steps[i]
local lookupcache=step.coverage
if lookupcache then
local lookupmatch=lookupcache[char]
if lookupmatch then
local ok
head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i)
if ok then
success=true
break
elseif not start then
break
end
end
else
report_missing_coverage(dataset,sequence)
end
end
if start then
start=getnext(start)
end
else
start=getnext(start)
end
elseif char==false then
start=getnext(start)
elseif id==disc_code then
local a=forcedisc or getsubtype(start)==discretionary_code or getattr(start,0)==attr
if a then
local ok
if gpossing then
start,ok=kernrun(start,k_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler)
elseif typ=="gsub_ligature" then
start,ok=testrun(start,t_run_multiple,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler)
else
start,ok=comprun(start,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler)
end
if ok then
success=true
end
else
start=getnext(start)
end
elseif id==math_code then
start=getnext(end_of_math(start))
elseif id==dir_code then
start,topstack,rlmode=txtdirstate(start,dirstack,topstack,rlparmode)
elseif id==localpar_code then
start,rlparmode,rlmode=pardirstate(start)
else
start=getnext(start)
end
end
end
end
if success then
done=true
end
if trace_steps then
registerstep(head)
end
end
nesting=nesting-1
head=tonode(head)
return head,done
end
local plugins={}
otf.plugins=plugins
function otf.registerplugin(name,f)
if type(name)=="string" and type(f)=="function" then
plugins[name]={ name,f }
end
end
local function plugininitializer(tfmdata,value)
if type(value)=="string" then
tfmdata.shared.plugin=plugins[value]
end
end
local function pluginprocessor(head,font)
local s=fontdata[font].shared
local p=s and s.plugin
if p then
if trace_plugins then
report_process("applying plugin %a",p[1])
end
return p[2](head,font)
else
return head,false
end
end
local function featuresinitializer(tfmdata,value)
end
registerotffeature {
name="features",
description="features",
default=true,
initializers={
position=1,
node=featuresinitializer,
plug=plugininitializer,
},
processors={
node=featuresprocessor,
plug=pluginprocessor,
}
}
otf.nodemodeinitializer=featuresinitializer
otf.featuresprocessor=featuresprocessor
otf.handlers=handlers
local setspacekerns=nodes.injections.setspacekerns if not setspacekerns then os.exit() end
if fontfeatures then
function otf.handlers.trigger_space_kerns(head,start,dataset,sequence,_,_,_,_,font,attr)
local features=fontfeatures[font]
local enabled=features and features.spacekern and features.kern
if enabled then
setspacekerns(font,sequence)
end
return head,start,enabled
end
else
function otf.handlers.trigger_space_kerns(head,start,dataset,sequence,_,_,_,_,font,attr)
local shared=fontdata[font].shared
local features=shared and shared.features
local enabled=features and features.spacekern and features.kern
if enabled then
setspacekerns(font,sequence)
end
return head,start,enabled
end
end
local function hasspacekerns(data)
local sequences=data.resources.sequences
for i=1,#sequences do
local sequence=sequences[i]
local steps=sequence.steps
if steps and sequence.features.kern then
for i=1,#steps do
local coverage=steps[i].coverage
if not coverage then
elseif coverage[32] then
return true
else
for k,v in next,coverage do
if v[32] then
return true
end
end
end
end
end
end
return false
end
otf.readers.registerextender {
name="spacekerns",
action=function(data)
data.properties.hasspacekerns=hasspacekerns(data)
end
}
local function spaceinitializer(tfmdata,value)
local resources=tfmdata.resources
local spacekerns=resources and resources.spacekerns
local properties=tfmdata.properties
if value and spacekerns==nil then
if properties and properties.hasspacekerns then
local sequences=resources.sequences
local left={}
local right={}
local last=0
local feat=nil
for i=1,#sequences do
local sequence=sequences[i]
local steps=sequence.steps
if steps then
local kern=sequence.features.kern
if kern then
if feat then
for script,languages in next,kern do
local f=feat[script]
if f then
for l in next,languages do
f[l]=true
end
else
feat[script]=languages
end
end
else
feat=kern
end
for i=1,#steps do
local step=steps[i]
local coverage=step.coverage
local rules=step.rules
local format=step.format
if rules then
elseif coverage then
local single=format==gpos_single
local kerns=coverage[32]
if kerns then
for k,v in next,kerns do
if type(v)~="table" then
right[k]=v
elseif single then
right[k]=v[3]
else
local one=v[1]
if one then
right[k]=one[3]
end
end
end
end
for k,v in next,coverage do
local kern=v[32]
if kern then
if type(kern)~="table" then
left[k]=kern
elseif single then
left[k]=v[3]
else
local one=v[1]
if one then
left[k]=one[3]
end
end
end
end
end
end
last=i
end
else
end
end
left=next(left) and left or false
right=next(right) and right or false
if left or right then
spacekerns={
left=left,
right=right,
}
if last>0 then
local triggersequence={
features={ kern=feat or { dflt={ dflt=true,} } },
flags=noflags,
name="trigger_space_kerns",
order={ "kern" },
type="trigger_space_kerns",
left=left,
right=right,
}
insert(sequences,last,triggersequence)
end
else
spacekerns=false
end
else
spacekerns=false
end
resources.spacekerns=spacekerns
end
return spacekerns
end
registerotffeature {
name="spacekern",
description="space kern injection",
default=true,
initializers={
node=spaceinitializer,
},
}
local function markinitializer(tfmdata,value)
local properties=tfmdata.properties
properties.checkmarks=value
end
registerotffeature {
name="checkmarks",
description="check mark widths",
default=true,
initializers={
node=markinitializer,
},
}
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-ots”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-osd” 10ecd4b375680b011e7c6a25e5ad74f7] ---
if not modules then modules={} end modules ['font-osd']={
version=1.001,
comment="companion to font-ini.mkiv",
author="Kai Eigner, TAT Zetwerk / Hans Hagen, PRAGMA ADE",
copyright="TAT Zetwerk / PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local insert,imerge,copy=table.insert,table.imerge,table.copy
local next,type=next,type
local report_devanagari=logs.reporter("otf","devanagari")
fonts=fonts or {}
fonts.analyzers=fonts.analyzers or {}
fonts.analyzers.methods=fonts.analyzers.methods or { node={ otf={} } }
local otf=fonts.handlers.otf
local handlers=otf.handlers
local methods=fonts.analyzers.methods
local otffeatures=fonts.constructors.features.otf
local registerotffeature=otffeatures.register
local nuts=nodes.nuts
local tonode=nuts.tonode
local tonut=nuts.tonut
local getnext=nuts.getnext
local getprev=nuts.getprev
local getboth=nuts.getboth
local getid=nuts.getid
local getchar=nuts.getchar
local getfont=nuts.getfont
local getsubtype=nuts.getsubtype
local setlink=nuts.setlink
local setnext=nuts.setnext
local setprev=nuts.setprev
local setchar=nuts.setchar
local getprop=nuts.getprop
local setprop=nuts.setprop
local ischar=nuts.is_char
local insert_node_after=nuts.insert_after
local copy_node=nuts.copy
local remove_node=nuts.remove
local flush_list=nuts.flush_list
local flush_node=nuts.flush_node
local copyinjection=nodes.injections.copy
local unsetvalue=attributes.unsetvalue
local fontdata=fonts.hashes.identifiers
local a_state=attributes.private('state')
local a_syllabe=attributes.private('syllabe')
local dotted_circle=0x25CC
local states=fonts.analyzers.states
local s_rphf=states.rphf
local s_half=states.half
local s_pref=states.pref
local s_blwf=states.blwf
local s_pstf=states.pstf
local replace_all_nbsp=nil
replace_all_nbsp=function(head)
replace_all_nbsp=typesetters and typesetters.characters and typesetters.characters.replacenbspaces or function(head)
return head
end
return replace_all_nbsp(head)
end
local xprocesscharacters=nil
if context then
xprocesscharacters=function(head,font)
xprocesscharacters=nodes.handlers.characters
return xprocesscharacters(head,font)
end
else
xprocesscharacters=function(head,font)
xprocesscharacters=nodes.handlers.nodepass
return xprocesscharacters(head,font)
end
end
local function processcharacters(head,font)
return tonut(xprocesscharacters(tonode(head)))
end
local consonant={
[0x0915]=true,[0x0916]=true,[0x0917]=true,[0x0918]=true,
[0x0919]=true,[0x091A]=true,[0x091B]=true,[0x091C]=true,
[0x091D]=true,[0x091E]=true,[0x091F]=true,[0x0920]=true,
[0x0921]=true,[0x0922]=true,[0x0923]=true,[0x0924]=true,
[0x0925]=true,[0x0926]=true,[0x0927]=true,[0x0928]=true,
[0x0929]=true,[0x092A]=true,[0x092B]=true,[0x092C]=true,
[0x092D]=true,[0x092E]=true,[0x092F]=true,[0x0930]=true,
[0x0931]=true,[0x0932]=true,[0x0933]=true,[0x0934]=true,
[0x0935]=true,[0x0936]=true,[0x0937]=true,[0x0938]=true,
[0x0939]=true,[0x0958]=true,[0x0959]=true,[0x095A]=true,
[0x095B]=true,[0x095C]=true,[0x095D]=true,[0x095E]=true,
[0x095F]=true,[0x0979]=true,[0x097A]=true,
[0x0C95]=true,[0x0C96]=true,[0x0C97]=true,[0x0C98]=true,
[0x0C99]=true,[0x0C9A]=true,[0x0C9B]=true,[0x0C9C]=true,
[0x0C9D]=true,[0x0C9E]=true,[0x0C9F]=true,[0x0CA0]=true,
[0x0CA1]=true,[0x0CA2]=true,[0x0CA3]=true,[0x0CA4]=true,
[0x0CA5]=true,[0x0CA6]=true,[0x0CA7]=true,[0x0CA8]=true,
[0x0CA9]=true,[0x0CAA]=true,[0x0CAB]=true,[0x0CAC]=true,
[0x0CAD]=true,[0x0CAE]=true,[0x0CAF]=true,[0x0CB0]=true,
[0x0CB1]=true,[0x0CB2]=true,[0x0CB3]=true,[0x0CB4]=true,
[0x0CB5]=true,[0x0CB6]=true,[0x0CB7]=true,[0x0CB8]=true,
[0x0CB9]=true,
[0x0CDE]=true,
[0x0D15]=true,[0x0D16]=true,[0x0D17]=true,[0x0D18]=true,
[0x0D19]=true,[0x0D1A]=true,[0x0D1B]=true,[0x0D1C]=true,
[0x0D1D]=true,[0x0D1E]=true,[0x0D1F]=true,[0x0D20]=true,
[0x0D21]=true,[0x0D22]=true,[0x0D23]=true,[0x0D24]=true,
[0x0D25]=true,[0x0D26]=true,[0x0D27]=true,[0x0D28]=true,
[0x0D29]=true,[0x0D2A]=true,[0x0D2B]=true,[0x0D2C]=true,
[0x0D2D]=true,[0x0D2E]=true,[0x0D2F]=true,[0x0D30]=true,
[0x0D31]=true,[0x0D32]=true,[0x0D33]=true,[0x0D34]=true,
[0x0D35]=true,[0x0D36]=true,[0x0D37]=true,[0x0D38]=true,
[0x0D39]=true,[0x0D3A]=true,
}
local independent_vowel={
[0x0904]=true,[0x0905]=true,[0x0906]=true,[0x0907]=true,
[0x0908]=true,[0x0909]=true,[0x090A]=true,[0x090B]=true,
[0x090C]=true,[0x090D]=true,[0x090E]=true,[0x090F]=true,
[0x0910]=true,[0x0911]=true,[0x0912]=true,[0x0913]=true,
[0x0914]=true,[0x0960]=true,[0x0961]=true,[0x0972]=true,
[0x0973]=true,[0x0974]=true,[0x0975]=true,[0x0976]=true,
[0x0977]=true,
[0x0C85]=true,[0x0C86]=true,[0x0C87]=true,[0x0C88]=true,
[0x0C89]=true,[0x0C8A]=true,[0x0C8B]=true,[0x0C8C]=true,
[0x0C8D]=true,[0x0C8E]=true,[0x0C8F]=true,[0x0C90]=true,
[0x0C91]=true,[0x0C92]=true,[0x0C93]=true,[0x0C94]=true,
[0x0D05]=true,[0x0D06]=true,[0x0D07]=true,[0x0D08]=true,
[0x0D09]=true,[0x0D0A]=true,[0x0D0B]=true,[0x0D0C]=true,
[0x0D0E]=true,[0x0D0F]=true,[0x0D10]=true,[0x0D12]=true,
[0x0D13]=true,[0x0D14]=true,
}
local dependent_vowel={
[0x093A]=true,[0x093B]=true,[0x093E]=true,[0x093F]=true,
[0x0940]=true,[0x0941]=true,[0x0942]=true,[0x0943]=true,
[0x0944]=true,[0x0945]=true,[0x0946]=true,[0x0947]=true,
[0x0948]=true,[0x0949]=true,[0x094A]=true,[0x094B]=true,
[0x094C]=true,[0x094E]=true,[0x094F]=true,[0x0955]=true,
[0x0956]=true,[0x0957]=true,[0x0962]=true,[0x0963]=true,
[0x0CBE]=true,[0x0CBF]=true,[0x0CC0]=true,[0x0CC1]=true,
[0x0CC2]=true,[0x0CC3]=true,[0x0CC4]=true,[0x0CC5]=true,
[0x0CC6]=true,[0x0CC7]=true,[0x0CC8]=true,[0x0CC9]=true,
[0x0CCA]=true,[0x0CCB]=true,[0x0CCC]=true,
[0x0D3E]=true,[0x0D3F]=true,[0x0D40]=true,[0x0D41]=true,
[0x0D42]=true,[0x0D43]=true,[0x0D44]=true,[0x0D46]=true,
[0x0D47]=true,[0x0D48]=true,[0x0D4A]=true,[0x0D4B]=true,
[0x0D4C]=true,[0x0D57]=true,
}
local vowel_modifier={
[0x0900]=true,[0x0901]=true,[0x0902]=true,[0x0903]=true,
[0xA8E0]=true,[0xA8E1]=true,[0xA8E2]=true,[0xA8E3]=true,
[0xA8E4]=true,[0xA8E5]=true,[0xA8E6]=true,[0xA8E7]=true,
[0xA8E8]=true,[0xA8E9]=true,[0xA8EA]=true,[0xA8EB]=true,
[0xA8EC]=true,[0xA8ED]=true,[0xA8EE]=true,[0xA8EF]=true,
[0xA8F0]=true,[0xA8F1]=true,
[0x0D02]=true,[0x0D03]=true,
}
local stress_tone_mark={
[0x0951]=true,[0x0952]=true,[0x0953]=true,[0x0954]=true,
[0x0CCD]=true,
[0x0D4D]=true,
}
local nukta={
[0x093C]=true,
[0x0CBC]=true,
}
local halant={
[0x094D]=true,
[0x0CCD]=true,
[0x0D4D]=true,
}
local ra={
[0x0930]=true,
[0x0CB0]=true,
[0x0D30]=true,
}
local c_anudatta=0x0952
local c_nbsp=0x00A0
local c_zwnj=0x200C
local c_zwj=0x200D
local zw_char={
[0x200C]=true,
[0x200D]=true,
}
local pre_mark={
[0x093F]=true,[0x094E]=true,
[0x0D46]=true,[0x0D47]=true,[0x0D48]=true,
}
local above_mark={
[0x0900]=true,[0x0901]=true,[0x0902]=true,[0x093A]=true,
[0x0945]=true,[0x0946]=true,[0x0947]=true,[0x0948]=true,
[0x0951]=true,[0x0953]=true,[0x0954]=true,[0x0955]=true,
[0xA8E0]=true,[0xA8E1]=true,[0xA8E2]=true,[0xA8E3]=true,
[0xA8E4]=true,[0xA8E5]=true,[0xA8E6]=true,[0xA8E7]=true,
[0xA8E8]=true,[0xA8E9]=true,[0xA8EA]=true,[0xA8EB]=true,
[0xA8EC]=true,[0xA8ED]=true,[0xA8EE]=true,[0xA8EF]=true,
[0xA8F0]=true,[0xA8F1]=true,
[0x0D4E]=true,
}
local below_mark={
[0x093C]=true,[0x0941]=true,[0x0942]=true,[0x0943]=true,
[0x0944]=true,[0x094D]=true,[0x0952]=true,[0x0956]=true,
[0x0957]=true,[0x0962]=true,[0x0963]=true,
}
local post_mark={
[0x0903]=true,[0x093B]=true,[0x093E]=true,[0x0940]=true,
[0x0949]=true,[0x094A]=true,[0x094B]=true,[0x094C]=true,
[0x094F]=true,
}
local twopart_mark={
[0x0D4A]={ 0x0D46,0x0D3E,},
[0x0D4B]={ 0x0D47,0x0D3E,},
[0x0D4C]={ 0x0D46,0x0D57,},
}
local mark_four={}
for k,v in next,pre_mark do mark_four[k]=pre_mark end
for k,v in next,above_mark do mark_four[k]=above_mark end
for k,v in next,below_mark do mark_four[k]=below_mark end
for k,v in next,post_mark do mark_four[k]=post_mark end
local mark_above_below_post={}
for k,v in next,above_mark do mark_above_below_post[k]=above_mark end
for k,v in next,below_mark do mark_above_below_post[k]=below_mark end
for k,v in next,post_mark do mark_above_below_post[k]=post_mark end
local reorder_class={
[0x0930]="before postscript",
[0x093F]="before half",
[0x0940]="after subscript",
[0x0941]="after subscript",
[0x0942]="after subscript",
[0x0943]="after subscript",
[0x0944]="after subscript",
[0x0945]="after subscript",
[0x0946]="after subscript",
[0x0947]="after subscript",
[0x0948]="after subscript",
[0x0949]="after subscript",
[0x094A]="after subscript",
[0x094B]="after subscript",
[0x094C]="after subscript",
[0x0962]="after subscript",
[0x0963]="after subscript",
[0x093E]="after subscript",
[0x0CB0]="after postscript",
[0x0CBF]="before subscript",
[0x0CC6]="before subscript",
[0x0CCC]="before subscript",
[0x0CBE]="before subscript",
[0x0CE2]="before subscript",
[0x0CE3]="before subscript",
[0x0CC1]="before subscript",
[0x0CC2]="before subscript",
[0x0CC3]="after subscript",
[0x0CC4]="after subscript",
[0x0CD5]="after subscript",
[0x0CD6]="after subscript",
}
local dflt_true={
dflt=true
}
local dev2_defaults={
dev2=dflt_true,
}
local deva_defaults={
dev2=dflt_true,
deva=dflt_true,
}
local false_flags={ false,false,false,false }
local both_joiners_true={
[0x200C]=true,
[0x200D]=true,
}
local sequence_reorder_matras={
features={ dv01=dev2_defaults },
flags=false_flags,
name="dv01_reorder_matras",
order={ "dv01" },
type="devanagari_reorder_matras",
nofsteps=1,
steps={
{
osdstep=true,
coverage=pre_mark,
}
}
}
local sequence_reorder_reph={
features={ dv02=dev2_defaults },
flags=false_flags,
name="dv02_reorder_reph",
order={ "dv02" },
type="devanagari_reorder_reph",
nofsteps=1,
steps={
{
osdstep=true,
coverage={},
}
}
}
local sequence_reorder_pre_base_reordering_consonants={
features={ dv03=dev2_defaults },
flags=false_flags,
name="dv03_reorder_pre_base_reordering_consonants",
order={ "dv03" },
type="devanagari_reorder_pre_base_reordering_consonants",
nofsteps=1,
steps={
{
osdstep=true,
coverage={},
}
}
}
local sequence_remove_joiners={
features={ dv04=deva_defaults },
flags=false_flags,
name="dv04_remove_joiners",
order={ "dv04" },
type="devanagari_remove_joiners",
nofsteps=1,
steps={
{ osdstep=true,
coverage=both_joiners_true,
},
}
}
local basic_shaping_forms={
nukt=true,
akhn=true,
rphf=true,
pref=true,
rkrf=true,
blwf=true,
half=true,
pstf=true,
vatu=true,
cjct=true,
}
local valid={
akhn=true,
rphf=true,
pref=true,
half=true,
blwf=true,
pstf=true,
pres=true,
blws=true,
psts=true,
}
local function initializedevanagi(tfmdata)
local script,language=otf.scriptandlanguage(tfmdata,attr)
if script=="deva" or script=="dev2" or script=="mlym" or script=="mlm2" then
local resources=tfmdata.resources
local devanagari=resources.devanagari
if not devanagari then
report_devanagari("adding devanagari features to font")
local gsubfeatures=resources.features.gsub
local sequences=resources.sequences
local sharedfeatures=tfmdata.shared.features
local lastmatch=0
for s=1,#sequences do
local features=sequences[s].features
if features then
for k,v in next,features do
if basic_shaping_forms[k] then
lastmatch=s
end
end
end
end
local insertindex=lastmatch+1
gsubfeatures["dv01"]=dev2_defaults
gsubfeatures["dv02"]=dev2_defaults
gsubfeatures["dv03"]=dev2_defaults
gsubfeatures["dv04"]=deva_defaults
local reorder_pre_base_reordering_consonants=copy(sequence_reorder_pre_base_reordering_consonants)
local reorder_reph=copy(sequence_reorder_reph)
local reorder_matras=copy(sequence_reorder_matras)
local remove_joiners=copy(sequence_remove_joiners)
insert(sequences,insertindex,reorder_pre_base_reordering_consonants)
insert(sequences,insertindex,reorder_reph)
insert(sequences,insertindex,reorder_matras)
insert(sequences,insertindex,remove_joiners)
local blwfcache={}
local seqsubset={}
local rephstep={
coverage={}
}
local devanagari={
reph=false,
vattu=false,
blwfcache=blwfcache,
seqsubset=seqsubset,
reorderreph=rephstep,
}
reorder_reph.steps={ rephstep }
local pre_base_reordering_consonants={}
reorder_pre_base_reordering_consonants.steps[1].coverage=pre_base_reordering_consonants
resources.devanagari=devanagari
for s=1,#sequences do
local sequence=sequences[s]
local steps=sequence.steps
local nofsteps=sequence.nofsteps
local features=sequence.features
local has_rphf=features.rphf
local has_blwf=features.blwf
if has_rphf and has_rphf.deva then
devanagari.reph=true
elseif has_blwf and has_blwf.deva then
devanagari.vattu=true
for i=1,nofsteps do
local step=steps[i]
local coverage=step.coverage
if coverage then
for k,v in next,coverage do
if not blwfcache[k] then
blwfcache[k]=v
end
end
end
end
end
for kind,spec in next,features do
if spec.dev2 and valid[kind] then
for i=1,nofsteps do
local step=steps[i]
local coverage=step.coverage
if coverage then
local reph=false
if kind=="rphf" then
if true then
for k,v in next,ra do
local r=coverage[k]
if r then
local h=false
for k,v in next,halant do
local h=r[k]
if h then
reph=h.ligature or false
break
end
end
if reph then
break
end
end
end
else
end
end
seqsubset[#seqsubset+1]={ kind,coverage,reph }
end
end
end
if kind=="pref" then
local steps=sequence.steps
local nofsteps=sequence.nofsteps
for i=1,nofsteps do
local step=steps[i]
local coverage=step.coverage
if coverage then
for k,v in next,halant do
local h=coverage[k]
if h then
local found=false
for k,v in next,h do
found=v and v.ligature
if found then
pre_base_reordering_consonants[k]=found
break
end
end
if found then
break
end
end
end
end
end
end
end
end
if script=="deva" then
sharedfeatures["dv04"]=true
elseif script=="dev2" then
sharedfeatures["dv01"]=true
sharedfeatures["dv02"]=true
sharedfeatures["dv03"]=true
sharedfeatures["dv04"]=true
elseif script=="mlym" then
sharedfeatures["pstf"]=true
elseif script=="mlm2" then
sharedfeatures["pstf"]=true
sharedfeatures["pref"]=true
sharedfeatures["dv03"]=true
gsubfeatures ["dv03"]=dev2_defaults
insert(sequences,insertindex,sequence_reorder_pre_base_reordering_consonants)
end
end
end
end
registerotffeature {
name="devanagari",
description="inject additional features",
default=true,
initializers={
node=initializedevanagi,
},
}
local function deva_initialize(font,attr)
local tfmdata=fontdata[font]
local datasets=otf.dataset(tfmdata,font,attr)
local devanagaridata=datasets.devanagari
if not devanagaridata then
devanagaridata={
reph=false,
vattu=false,
blwfcache={},
}
datasets.devanagari=devanagaridata
local resources=tfmdata.resources
local devanagari=resources.devanagari
for s=1,#datasets do
local dataset=datasets[s]
if dataset and dataset[1] then
local kind=dataset[4]
if kind=="rphf" then
devanagaridata.reph=true
elseif kind=="blwf" then
devanagaridata.vattu=true
devanagaridata.blwfcache=devanagari.blwfcache
end
end
end
end
return devanagaridata.reph,devanagaridata.vattu,devanagaridata.blwfcache
end
local function deva_reorder(head,start,stop,font,attr,nbspaces)
local reph,vattu,blwfcache=deva_initialize(font,attr)
local current=start
local n=getnext(start)
local base=nil
local firstcons=nil
local lastcons=nil
local basefound=false
if reph and ra[getchar(start)] and halant[getchar(n)] then
if n==stop then
return head,stop,nbspaces
end
if getchar(getnext(n))==c_zwj then
current=start
else
current=getnext(n)
setprop(start,a_state,s_rphf)
end
end
if getchar(current)==c_nbsp then
if current==stop then
stop=getprev(stop)
head=remove_node(head,current)
flush_node(current)
return head,stop,nbspaces
else
nbspaces=nbspaces+1
base=current
firstcons=current
lastcons=current
current=getnext(current)
if current~=stop then
if nukta[getchar(current)] then
current=getnext(current)
end
if getchar(current)==c_zwj then
if current~=stop then
local next=getnext(current)
if next~=stop and halant[getchar(next)] then
current=next
next=getnext(current)
local tmp=next and getnext(next) or nil
local changestop=next==stop
local tempcurrent=copy_node(next)
copyinjection(tempcurrent,next)
local nextcurrent=copy_node(current)
copyinjection(nextcurrent,current)
setlink(tempcurrent,nextcurrent)
setprop(tempcurrent,a_state,s_blwf)
tempcurrent=processcharacters(tempcurrent,font)
setprop(tempcurrent,a_state,unsetvalue)
if getchar(next)==getchar(tempcurrent) then
flush_list(tempcurrent)
local n=copy_node(current)
copyinjection(n,current)
setchar(current,dotted_circle)
head=insert_node_after(head,current,n)
else
setchar(current,getchar(tempcurrent))
local freenode=getnext(current)
setlink(current,tmp)
flush_node(freenode)
flush_list(tempcurrent)
if changestop then
stop=current
end
end
end
end
end
end
end
end
while not basefound do
local char=getchar(current)
if consonant[char] then
setprop(current,a_state,s_half)
if not firstcons then
firstcons=current
end
lastcons=current
if not base then
base=current
elseif blwfcache[char] then
setprop(current,a_state,s_blwf)
else
base=current
end
end
basefound=current==stop
current=getnext(current)
end
if base~=lastcons then
local np=base
local n=getnext(base)
local ch=getchar(n)
if nukta[ch] then
np=n
n=getnext(n)
ch=getchar(n)
end
if halant[ch] then
if lastcons~=stop then
local ln=getnext(lastcons)
if nukta[getchar(ln)] then
lastcons=ln
end
end
local nn=getnext(n)
local ln=getnext(lastcons)
setlink(np,nn)
setnext(lastcons,n)
if ln then
setprev(ln,n)
end
setnext(n,ln)
setprev(n,lastcons)
if lastcons==stop then
stop=n
end
end
end
n=getnext(start)
if n~=stop and ra[getchar(start)] and halant[getchar(n)] and not zw_char[getchar(getnext(n))] then
local matra=base
if base~=stop then
local next=getnext(base)
if dependent_vowel[getchar(next)] then
matra=next
end
end
local sp=getprev(start)
local nn=getnext(n)
local mn=getnext(matra)
setlink(sp,nn)
setlink(matra,start)
setlink(n,mn)
if head==start then
head=nn
end
start=nn
if matra==stop then
stop=n
end
end
local current=start
while current~=stop do
local next=getnext(current)
if next~=stop and halant[getchar(next)] and getchar(getnext(next))==c_zwnj then
setprop(current,a_state,unsetvalue)
end
current=next
end
if base~=stop and getprop(base,a_state) then
local next=getnext(base)
if halant[getchar(next)] and not (next~=stop and getchar(getnext(next))==c_zwj) then
setprop(base,a_state,unsetvalue)
end
end
local current,allreordered,moved=start,false,{ [base]=true }
local a,b,p,bn=base,base,base,getnext(base)
if base~=stop and nukta[getchar(bn)] then
a,b,p=bn,bn,bn
end
while not allreordered do
local c=current
local n=getnext(current)
local l=nil
if c~=stop then
local ch=getchar(n)
if nukta[ch] then
c=n
n=getnext(n)
ch=getchar(n)
end
if c~=stop then
if halant[ch] then
c=n
n=getnext(n)
ch=getchar(n)
end
while c~=stop and dependent_vowel[ch] do
c=n
n=getnext(n)
ch=getchar(n)
end
if c~=stop then
if vowel_modifier[ch] then
c=n
n=getnext(n)
ch=getchar(n)
end
if c~=stop and stress_tone_mark[ch] then
c=n
n=getnext(n)
end
end
end
end
local bp=getprev(firstcons)
local cn=getnext(current)
local last=getnext(c)
while cn~=last do
if pre_mark[getchar(cn)] then
if bp then
setnext(bp,cn)
end
local prev,next=getboth(cn)
if next then
setprev(next,prev)
end
setnext(prev,next)
if cn==stop then
stop=prev
end
setprev(cn,bp)
setlink(cn,firstcons)
if firstcons==start then
if head==start then
head=cn
end
start=cn
end
break
end
cn=getnext(cn)
end
allreordered=c==stop
current=getnext(c)
end
if reph or vattu then
local current,cns=start,nil
while current~=stop do
local c=current
local n=getnext(current)
if ra[getchar(current)] and halant[getchar(n)] then
c=n
n=getnext(n)
local b,bn=base,base
while bn~=stop do
local next=getnext(bn)
if dependent_vowel[getchar(next)] then
b=next
end
bn=next
end
if getprop(current,a_state)==s_rphf then
if b~=current then
if current==start then
if head==start then
head=n
end
start=n
end
if b==stop then
stop=c
end
local prev=getprev(current)
setlink(prev,n)
local next=getnext(b)
setlink(c,next)
setlink(b,current)
end
elseif cns and getnext(cns)~=current then
local cp=getprev(current)
local cnsn=getnext(cns)
setlink(cp,n)
setlink(cns,current)
setlink(c,cnsn)
if c==stop then
stop=cp
break
end
current=getprev(n)
end
else
local char=getchar(current)
if consonant[char] then
cns=current
local next=getnext(cns)
if halant[getchar(next)] then
cns=next
end
elseif char==c_nbsp then
nbspaces=nbspaces+1
cns=current
local next=getnext(cns)
if halant[getchar(next)] then
cns=next
end
end
end
current=getnext(current)
end
end
if getchar(base)==c_nbsp then
nbspaces=nbspaces-1
head=remove_node(head,base)
flush_node(base)
end
return head,stop,nbspaces
end
function handlers.devanagari_reorder_matras(head,start)
local current=start
local startfont=getfont(start)
local startattr=getprop(start,a_syllabe)
while current do
local char=ischar(current,startfont)
local next=getnext(current)
if char and getprop(current,a_syllabe)==startattr then
if halant[char] and not getprop(current,a_state) then
if next then
local char=ischar(next,startfont)
if char and zw_char[char] and getprop(next,a_syllabe)==startattr then
current=next
next=getnext(current)
end
end
local startnext=getnext(start)
head=remove_node(head,start)
setlink(start,next)
setlink(current,start)
start=startnext
break
end
else
break
end
current=next
end
return head,start,true
end
function handlers.devanagari_reorder_reph(head,start)
local current=getnext(start)
local startnext=nil
local startprev=nil
local startfont=getfont(start)
local startattr=getprop(start,a_syllabe)
while current do
local char=ischar(current,startfont)
if char and getprop(current,a_syllabe)==startattr then
if halant[char] and not getprop(current,a_state) then
local next=getnext(current)
if next then
local nextchar=ischar(next,startfont)
if nextchar and zw_char[nextchar] and getprop(next,a_syllabe)==startattr then
current=next
next=getnext(current)
end
end
startnext=getnext(start)
head=remove_node(head,start)
setlink(start,next)
setlink(current,start)
start=startnext
startattr=getprop(start,a_syllabe)
break
end
current=getnext(current)
else
break
end
end
if not startnext then
current=getnext(start)
while current do
local char=ischar(current,startfont)
if char and getprop(current,a_syllabe)==startattr then
if getprop(current,a_state)==s_pstf then
startnext=getnext(start)
head=remove_node(head,start)
local prev=getprev(current)
setlink(prev,start)
setlink(start,current)
start=startnext
startattr=getprop(start,a_syllabe)
break
end
current=getnext(current)
else
break
end
end
end
if not startnext then
current=getnext(start)
local c=nil
while current do
local char=ischar(current,startfont)
if char and getprop(current,a_syllabe)==startattr then
if not c and mark_above_below_post[char] and reorder_class[char]~="after subscript" then
c=current
end
current=getnext(current)
else
break
end
end
if c then
startnext=getnext(start)
head=remove_node(head,start)
local prev=getprev(c)
setlink(prev,start)
setlink(start,c)
start=startnext
startattr=getprop(start,a_syllabe)
end
end
if not startnext then
current=start
local next=getnext(current)
while next do
local nextchar=ischar(next,startfont)
if nextchar and getprop(next,a_syllabe)==startattr then
current=next
next=getnext(current)
else
break
end
end
if start~=current then
startnext=getnext(start)
head=remove_node(head,start)
local next=getnext(current)
setlink(start,next)
setlink(current,start)
start=startnext
end
end
return head,start,true
end
function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start)
local current=start
local startnext=nil
local startprev=nil
local startfont=getfont(start)
local startattr=getprop(start,a_syllabe)
while current do
local char=ischar(current,startfont)
if char and getprop(current,a_syllabe)==startattr then
local next=getnext(current)
if halant[char] and not getprop(current,a_state) then
if next then
local nextchar=ischar(next,startfont)
if nextchar and getprop(next,a_syllabe)==startattr then
if nextchar==c_zwnj or nextchar==c_zwj then
current=next
next=getnext(current)
end
end
end
startnext=getnext(start)
removenode(start,start)
setlink(start,next)
setlink(current,start)
start=startnext
break
end
current=next
else
break
end
end
if not startnext then
current=getnext(start)
startattr=getprop(start,a_syllabe)
while current do
local char=ischar(current,startfont)
if char and getprop(current,a_syllabe)==startattr then
if not consonant[char] and getprop(current,a_state) then
startnext=getnext(start)
removenode(start,start)
local prev=getprev(current)
setlink(prev,start)
setlink(start,current)
start=startnext
break
end
current=getnext(current)
else
break
end
end
end
return head,start,true
end
function handlers.devanagari_remove_joiners(head,start,kind,lookupname,replacement)
local stop=getnext(start)
local font=getfont(start)
local last=start
while stop do
local char=ischar(stop,font)
if char and (char==c_zwnj or char==c_zwj) then
last=stop
stop=getnext(stop)
else
break
end
end
local prev=getprev(start)
if stop then
setnext(last)
setlink(prev,stop)
elseif prev then
setnext(prev)
end
if head==start then
head=stop
end
flush_list(start)
return head,stop,true
end
local function dev2_initialize(font,attr)
local devanagari=fontdata[font].resources.devanagari
if devanagari then
return devanagari.seqsubset or {},devanagari.reorderreph or {}
else
return {},{}
end
end
local function dev2_reorder(head,start,stop,font,attr,nbspaces)
local seqsubset,reorderreph=dev2_initialize(font,attr)
local reph=false
local halfpos=nil
local basepos=nil
local subpos=nil
local postpos=nil
local locl={}
for i=1,#seqsubset do
local subset=seqsubset[i]
local kind=subset[1]
local lookupcache=subset[2]
if kind=="rphf" then
reph=subset[3]
local current=start
local last=getnext(stop)
while current~=last do
if current~=stop then
local c=locl[current] or getchar(current)
local found=lookupcache[c]
if found then
local next=getnext(current)
local n=locl[next] or getchar(next)
if found[n] then
local afternext=next~=stop and getnext(next)
if afternext and zw_char[getchar(afternext)] then
current=next
current=getnext(current)
elseif current==start then
setprop(current,a_state,s_rphf)
current=next
else
current=next
end
end
end
end
current=getnext(current)
end
elseif kind=="pref" then
local current=start
local last=getnext(stop)
while current~=last do
if current~=stop then
local c=locl[current] or getchar(current)
local found=lookupcache[c]
if found then
local next=getnext(current)
local n=locl[next] or getchar(next)
if found[n] then
setprop(current,a_state,s_pref)
setprop(next,a_state,s_pref)
current=next
end
end
end
current=getnext(current)
end
elseif kind=="half" then
local current=start
local last=getnext(stop)
while current~=last do
if current~=stop then
local c=locl[current] or getchar(current)
local found=lookupcache[c]
if found then
local next=getnext(current)
local n=locl[next] or getchar(next)
if found[n] then
if next~=stop and getchar(getnext(next))==c_zwnj then
current=next
else
setprop(current,a_state,s_half)
if not halfpos then
halfpos=current
end
end
current=getnext(current)
end
end
end
current=getnext(current)
end
elseif kind=="blwf" then
local current=start
local last=getnext(stop)
while current~=last do
if current~=stop then
local c=locl[current] or getchar(current)
local found=lookupcache[c]
if found then
local next=getnext(current)
local n=locl[next] or getchar(next)
if found[n] then
setprop(current,a_state,s_blwf)
setprop(next,a_state,s_blwf)
current=next
subpos=current
end
end
end
current=getnext(current)
end
elseif kind=="pstf" then
local current=start
local last=getnext(stop)
while current~=last do
if current~=stop then
local c=locl[current] or getchar(current)
local found=lookupcache[c]
if found then
local next=getnext(current)
local n=locl[next] or getchar(next)
if found[n] then
setprop(current,a_state,s_pstf)
setprop(next,a_state,s_pstf)
current=next
postpos=current
end
end
end
current=getnext(current)
end
end
end
reorderreph.coverage={ [reph]=true }
local current,base,firstcons=start,nil,nil
if getprop(start,a_state)==s_rphf then
current=getnext(getnext(start))
end
if current~=getnext(stop) and getchar(current)==c_nbsp then
if current==stop then
stop=getprev(stop)
head=remove_node(head,current)
flush_node(current)
return head,stop,nbspaces
else
nbspaces=nbspaces+1
base=current
current=getnext(current)
if current~=stop then
local char=getchar(current)
if nukta[char] then
current=getnext(current)
char=getchar(current)
end
if char==c_zwj then
local next=getnext(current)
if current~=stop and next~=stop and halant[getchar(next)] then
current=next
next=getnext(current)
local tmp=getnext(next)
local changestop=next==stop
setnext(next,nil)
setprop(current,a_state,s_pref)
current=processcharacters(current,font)
setprop(current,a_state,s_blwf)
current=processcharacters(current,font)
setprop(current,a_state,s_pstf)
current=processcharacters(current,font)
setprop(current,a_state,unsetvalue)
if halant[getchar(current)] then
setnext(getnext(current),tmp)
local nc=copy_node(current)
copyinjection(nc,current)
setchar(current,dotted_circle)
head=insert_node_after(head,current,nc)
else
setnext(current,tmp)
if changestop then
stop=current
end
end
end
end
end
end
else
local last=getnext(stop)
while current~=last do
local next=getnext(current)
if consonant[getchar(current)] then
if not (current~=stop and next~=stop and halant[getchar(next)] and getchar(getnext(next))==c_zwj) then
if not firstcons then
firstcons=current
end
local a=getprop(current,a_state)
if not (a==s_pref or a==s_blwf or a==s_pstf) then
base=current
end
end
end
current=next
end
if not base then
base=firstcons
end
end
if not base then
if getprop(start,a_state)==s_rphf then
setprop(start,a_state,unsetvalue)
end
return head,stop,nbspaces
else
if getprop(base,a_state) then
setprop(base,a_state,unsetvalue)
end
basepos=base
end
if not halfpos then
halfpos=base
end
if not subpos then
subpos=base
end
if not postpos then
postpos=subpos or base
end
local moved={}
local current=start
local last=getnext(stop)
while current~=last do
local char,target,cn=locl[current] or getchar(current),nil,getnext(current)
local tpm=twopart_mark[char]
if tpm then
local extra=copy_node(current)
copyinjection(extra,current)
char=tpm[1]
setchar(current,char)
setchar(extra,tpm[2])
head=insert_node_after(head,current,extra)
end
if not moved[current] and dependent_vowel[char] then
if pre_mark[char] then
moved[current]=true
local prev,next=getboth(current)
setlink(prev,next)
if current==stop then
stop=getprev(current)
end
if halfpos==start then
if head==start then
head=current
end
start=current
end
local prev=getprev(halfpos)
setlink(prev,current)
setlink(current,halfpos)
halfpos=current
elseif above_mark[char] then
target=basepos
if subpos==basepos then
subpos=current
end
if postpos==basepos then
postpos=current
end
basepos=current
elseif below_mark[char] then
target=subpos
if postpos==subpos then
postpos=current
end
subpos=current
elseif post_mark[char] then
target=postpos
postpos=current
end
if mark_above_below_post[char] then
local prev=getprev(current)
if prev~=target then
local next=getnext(current)
setlink(prev,next)
if current==stop then
stop=prev
end
local next=getnext(target)
setlink(current,next)
setlink(target,current)
end
end
end
current=cn
end
local current,c=start,nil
while current~=stop do
local char=getchar(current)
if halant[char] or stress_tone_mark[char] then
if not c then
c=current
end
else
c=nil
end
local next=getnext(current)
if c and nukta[getchar(next)] then
if head==c then
head=next
end
if stop==next then
stop=current
end
local prev=getprev(c)
setlink(prev,next)
local nextnext=getnext(next)
setnext(current,nextnext)
local nextnextnext=getnext(nextnext)
if nextnextnext then
setprev(nextnextnext,current)
end
setlink(nextnext,c)
end
if stop==current then break end
current=getnext(current)
end
if getchar(base)==c_nbsp then
if base==stop then
stop=getprev(stop)
end
nbspaces=nbspaces-1
head=remove_node(head,base)
flush_node(base)
end
return head,stop,nbspaces
end
local separator={}
imerge(separator,consonant)
imerge(separator,independent_vowel)
imerge(separator,dependent_vowel)
imerge(separator,vowel_modifier)
imerge(separator,stress_tone_mark)
for k,v in next,nukta do separator[k]=true end
for k,v in next,halant do separator[k]=true end
local function analyze_next_chars_one(c,font,variant)
local n=getnext(c)
if not n then
return c
end
if variant==1 then
local v=ischar(n,font)
if v and nukta[v] then
n=getnext(n)
if n then
v=ischar(n,font)
end
end
if n and v then
local nn=getnext(n)
if nn then
local vv=ischar(nn,font)
if vv then
local nnn=getnext(nn)
if nnn then
local vvv=ischar(nnn,font)
if vvv then
if vv==c_zwj and consonant[vvv] then
c=nnn
elseif (vv==c_zwnj or vv==c_zwj) and halant[vvv] then
local nnnn=getnext(nnn)
if nnnn then
local vvvv=ischar(nnnn,font)
if vvvv and consonant[vvvv] then
c=nnnn
end
end
end
end
end
end
end
end
elseif variant==2 then
local v=ischar(n,font)
if v and nukta[v] then
c=n
end
n=getnext(c)
if n then
v=ischar(n,font)
if v then
local nn=getnext(n)
if nn then
local vv=ischar(nn,font)
if vv and zw_char[v] then
n=nn
v=vv
nn=getnext(nn)
vv=nn and ischar(nn,font)
end
if vv and halant[v] and consonant[vv] then
c=nn
end
end
end
end
end
local n=getnext(c)
if not n then
return c
end
local v=ischar(n,font)
if not v then
return c
end
if dependent_vowel[v] then
c=getnext(c)
n=getnext(c)
if not n then
return c
end
v=ischar(n,font)
if not v then
return c
end
end
if nukta[v] then
c=getnext(c)
n=getnext(c)
if not n then
return c
end
v=ischar(n,font)
if not v then
return c
end
end
if halant[v] then
c=getnext(c)
n=getnext(c)
if not n then
return c
end
v=ischar(n,font)
if not v then
return c
end
end
if vowel_modifier[v] then
c=getnext(c)
n=getnext(c)
if not n then
return c
end
v=ischar(n,font)
if not v then
return c
end
end
if stress_tone_mark[v] then
c=getnext(c)
n=getnext(c)
if not n then
return c
end
v=ischar(n,font)
if not v then
return c
end
end
if stress_tone_mark[v] then
return n
else
return c
end
end
local function analyze_next_chars_two(c,font)
local n=getnext(c)
if not n then
return c
end
local v=ischar(n,font)
if v and nukta[v] then
c=n
end
n=c
while true do
local nn=getnext(n)
if nn then
local vv=ischar(nn,font)
if vv then
if halant[vv] then
n=nn
local nnn=getnext(nn)
if nnn then
local vvv=ischar(nnn,font)
if vvv and zw_char[vvv] then
n=nnn
end
end
elseif vv==c_zwnj or vv==c_zwj then
local nnn=getnext(nn)
if nnn then
local vvv=ischar(nnn,font)
if vvv and halant[vvv] then
n=nnn
end
end
else
break
end
local nn=getnext(n)
if nn then
local vv=ischar(nn,font)
if vv and consonant[vv] then
n=nn
local nnn=getnext(nn)
if nnn then
local vvv=ischar(nnn,font)
if vvv and nukta[vvv] then
n=nnn
end
end
c=n
else
break
end
else
break
end
else
break
end
else
break
end
end
if not c then
return
end
local n=getnext(c)
if not n then
return c
end
local v=ischar(n,font)
if not v then
return c
end
if v==c_anudatta then
c=n
n=getnext(c)
if not n then
return c
end
v=ischar(n,font)
if not v then
return c
end
end
if halant[v] then
c=n
n=getnext(c)
if not n then
return c
end
v=ischar(n,font)
if not v then
return c
end
if v==c_zwnj or v==c_zwj then
c=n
n=getnext(c)
if not n then
return c
end
v=ischar(n,font)
if not v then
return c
end
end
else
if dependent_vowel[v] then
c=n
n=getnext(c)
if not n then
return c
end
v=ischar(n,font)
if not v then
return c
end
end
if nukta[v] then
c=n
n=getnext(c)
if not n then
return c
end
v=ischar(n,font)
if not v then
return c
end
end
if halant[v] then
c=n
n=getnext(c)
if not n then
return c
end
v=ischar(n,font)
if not v then
return c
end
end
end
if vowel_modifier[v] then
c=n
n=getnext(c)
if not n then
return c
end
v=ischar(n,font)
if not v then
return c
end
end
if stress_tone_mark[v] then
c=n
n=getnext(c)
if not n then
return c
end
v=ischar(n,font)
if not v then
return c
end
end
if stress_tone_mark[v] then
return n
else
return c
end
end
local function inject_syntax_error(head,current,mark)
local signal=copy_node(current)
copyinjection(signal,current)
if mark==pre_mark then
setchar(signal,dotted_circle)
else
setchar(current,dotted_circle)
end
return insert_node_after(head,current,signal)
end
function methods.deva(head,font,attr)
head=tonut(head)
local current=head
local start=true
local done=false
local nbspaces=0
while current do
local char=ischar(current,font)
if char then
done=true
local syllablestart=current
local syllableend=nil
local c=current
local n=getnext(c)
local first=char
if n and ra[first] then
local second=ischar(n,font)
if second and halant[second] then
local n=getnext(n)
if n then
local third=ischar(n,font)
if third then
c=n
first=third
end
end
end
end
local standalone=first==c_nbsp
if standalone then
local prev=getprev(current)
if prev then
local prevchar=ischar(prev,font)
if not prevchar then
elseif not separator[prevchar] then
else
standalone=false
end
else
end
end
if standalone then
local syllableend=analyze_next_chars_one(c,font,2)
current=getnext(syllableend)
if syllablestart~=syllableend then
head,current,nbspaces=deva_reorder(head,syllablestart,syllableend,font,attr,nbspaces)
current=getnext(current)
end
else
if consonant[char] then
local prevc=true
while prevc do
prevc=false
local n=getnext(current)
if not n then
break
end
local v=ischar(n,font)
if not v then
break
end
if nukta[v] then
n=getnext(n)
if not n then
break
end
v=ischar(n,font)
if not v then
break
end
end
if halant[v] then
n=getnext(n)
if not n then
break
end
v=ischar(n,font)
if not v then
break
end
if v==c_zwnj or v==c_zwj then
n=getnext(n)
if not n then
break
end
v=ischar(n,font)
if not v then
break
end
end
if consonant[v] then
prevc=true
current=n
end
end
end
local n=getnext(current)
if n then
local v=ischar(n,font)
if v and nukta[v] then
current=n
n=getnext(current)
end
end
syllableend=current
current=n
if current then
local v=ischar(current,font)
if not v then
elseif halant[v] then
local n=getnext(current)
if n then
local v=ischar(n,font)
if v and zw_char[v] then
syllableend=n
current=getnext(n)
else
syllableend=current
current=n
end
else
syllableend=current
current=n
end
else
if dependent_vowel[v] then
syllableend=current
current=getnext(current)
v=ischar(current,font)
end
if v and vowel_modifier[v] then
syllableend=current
current=getnext(current)
v=ischar(current,font)
end
if v and stress_tone_mark[v] then
syllableend=current
current=getnext(current)
end
end
end
if syllablestart~=syllableend then
head,current,nbspaces=deva_reorder(head,syllablestart,syllableend,font,attr,nbspaces)
current=getnext(current)
end
elseif independent_vowel[char] then
syllableend=current
current=getnext(current)
if current then
local v=ischar(current,font)
if v then
if vowel_modifier[v] then
syllableend=current
current=getnext(current)
v=ischar(current,font)
end
if v and stress_tone_mark[v] then
syllableend=current
current=getnext(current)
end
end
end
else
local mark=mark_four[char]
if mark then
head,current=inject_syntax_error(head,current,mark)
end
current=getnext(current)
end
end
else
current=getnext(current)
end
start=false
end
if nbspaces>0 then
head=replace_all_nbsp(head)
end
head=tonode(head)
return head,done
end
function methods.dev2(head,font,attr)
head=tonut(head)
local current=head
local start=true
local done=false
local syllabe=0
local nbspaces=0
while current do
local syllablestart=nil
local syllableend=nil
local char=ischar(current,font)
if char then
done=true
syllablestart=current
local c=current
local n=getnext(current)
if n and ra[char] then
local nextchar=ischar(n,font)
if nextchar and halant[nextchar] then
local n=getnext(n)
if n then
local nextnextchar=ischar(n,font)
if nextnextchar then
c=n
char=nextnextchar
end
end
end
end
if independent_vowel[char] then
current=analyze_next_chars_one(c,font,1)
syllableend=current
else
local standalone=char==c_nbsp
if standalone then
nbspaces=nbspaces+1
local p=getprev(current)
if not p then
elseif ischar(p,font) then
elseif not separator[getchar(p)] then
else
standalone=false
end
end
if standalone then
current=analyze_next_chars_one(c,font,2)
syllableend=current
elseif consonant[getchar(current)] then
current=analyze_next_chars_two(current,font)
syllableend=current
end
end
end
if syllableend then
syllabe=syllabe+1
local c=syllablestart
local n=getnext(syllableend)
while c~=n do
setprop(c,a_syllabe,syllabe)
c=getnext(c)
end
end
if syllableend and syllablestart~=syllableend then
head,current,nbspaces=dev2_reorder(head,syllablestart,syllableend,font,attr,nbspaces)
end
if not syllableend then
local char=ischar(current,font)
if char and not getprop(current,a_state) then
local mark=mark_four[char]
if mark then
head,current=inject_syntax_error(head,current,mark)
end
end
end
start=false
current=getnext(current)
end
if nbspaces>0 then
head=replace_all_nbsp(head)
end
head=tonode(head)
return head,done
end
methods.mlym=methods.deva
methods.mlm2=methods.dev2
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-osd”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-ocl” fbc00782e4efb24a7569f99cd1574ffb] ---
if not modules then modules={} end modules ['font-ocl']={
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 tostring,next,format=tostring,next,string.format
local round,max=math.round,math.round
local formatters=string.formatters
local tounicode=fonts.mappings.tounicode
local otf=fonts.handlers.otf
local f_color=formatters["pdf:direct:%f %f %f rg"]
local f_gray=formatters["pdf:direct:%f g"]
local s_black="pdf:direct:0 g"
if context then
local startactualtext=nil
local stopactualtext=nil
function otf.getactualtext(s)
if not startactualtext then
startactualtext=backends.codeinjections.startunicodetoactualtextdirect
stopactualtext=backends.codeinjections.stopunicodetoactualtextdirect
end
return startactualtext(s),stopactualtext()
end
else
local tounicode=fonts.mappings.tounicode16
function otf.getactualtext(s)
return
"/Span << /ActualText <feff"..n.."> >> BDC",
"EMC"
end
end
local sharedpalettes={}
if context then
local graytorgb=attributes.colors.graytorgb
local cmyktorgb=attributes.colors.cmyktorgb
function otf.registerpalette(name,values)
sharedpalettes[name]=values
for i=1,#values do
local v=values[i]
local r,g,b
local s=v.s
if s then
r,g,b=graytorgb(s)
else
local c,m,y,k=v.c,v.m,v.y,v.k
if c or m or y or k then
r,g,b=cmyktorgb(c or 0,m or 0,y or 0,k or 0)
else
r,g,b=v.r,v.g,v.b
end
end
values[i]={
max(r and round(r*255) or 0,255),
max(g and round(g*255) or 0,255),
max(b and round(b*255) or 0,255)
}
end
end
else
function otf.registerpalette(name,values)
sharedpalettes[name]=values
for i=1,#values do
local v=values[i]
values[i]={
max(round((v.r or 0)*255),255),
max(round((v.g or 0)*255),255),
max(round((v.b or 0)*255),255)
}
end
end
end
local function initializecolr(tfmdata,kind,value)
if value then
local palettes=tfmdata.resources.colorpalettes
if palettes then
local palette=sharedpalettes[value] or palettes[tonumber(value) or 1] or palettes[1] or {}
local classes=#palette
if classes==0 then
return
end
local characters=tfmdata.characters
local descriptions=tfmdata.descriptions
local properties=tfmdata.properties
local colorvalues={}
properties.virtualized=true
tfmdata.fonts={
{ id=0 }
}
for i=1,classes do
local p=palette[i]
local r,g,b=p[1],p[2],p[3]
if r==g and g==b then
colorvalues[i]={ "special",f_gray(r/255) }
else
colorvalues[i]={ "special",f_color(r/255,g/255,b/255) }
end
end
local getactualtext=otf.getactualtext
for unicode,character in next,characters do
local description=descriptions[unicode]
if description then
local colorlist=description.colors
if colorlist then
local b,e=getactualtext(tounicode(characters[unicode].unicode or 0xFFFD))
local w=character.width or 0
local s=#colorlist
local t={
{ "special","pdf:page:q" },
{ "special","pdf:raw:"..b }
}
local n=#t
for i=1,s do
local entry=colorlist[i]
n=n+1 t[n]=colorvalues[entry.class] or s_black
n=n+1 t[n]={ "char",entry.slot }
if s>1 and i<s and w~=0 then
n=n+1 t[n]={ "right",-w }
end
end
n=n+1 t[n]={ "special","pdf:page:"..e }
n=n+1 t[n]={ "special","pdf:raw:Q" }
character.commands=t
end
end
end
end
end
end
fonts.handlers.otf.features.register {
name="colr",
description="color glyphs",
manipulators={
base=initializecolr,
node=initializecolr,
}
}
local otfsvg=otf.svg or {}
otf.svg=otfsvg
otf.svgenabled=true
do
local nofstreams=0
local f_name=formatters[ [[svg-glyph-%05i]] ]
local f_used=context and formatters[ [[original:///%s]] ] or formatters[ [[%s]] ]
local cache={}
function otfsvg.storepdfdata(pdf)
nofstreams=nofstreams+1
local o,n=epdf.openMemStream(pdf,#pdf,f_name(nofstreams))
cache[n]=o
return nil,f_used(n),nil
end
if context then
local storepdfdata=otfsvg.storepdfdata
local initialized=false
function otfsvg.storepdfdata(pdf)
if not initialized then
if resolvers.setmemstream then
local f_setstream=formatters[ [[resolvers.setmemstream("svg-glyph-%05i",%q,true)]] ]
local f_getstream=formatters[ [[memstream:///svg-glyph-%05i]] ]
local f_nilstream=formatters[ [[resolvers.resetmemstream("svg-glyph-%05i",true)]] ]
storepdfdata=function(pdf)
nofstreams=nofstreams+1
return
f_setstream(nofstreams,pdf),
f_getstream(nofstreams),
f_nilstream(nofstreams)
end
otfsvg.storepdfdata=storepdfdata
end
initialized=true
end
return storepdfdata(pdf)
end
end
end
do
local report_svg=logs.reporter("fonts","svg conversion")
local loaddata=io.loaddata
local savedata=io.savedata
local remove=os.remove
if context and xml.convert then
local xmlconvert=xml.convert
local xmlfirst=xml.first
function otfsvg.filterglyph(entry,index)
local svg=xmlconvert(entry.data)
local root=svg and xmlfirst(svg,"/svg[@id='glyph"..index.."']")
local data=root and tostring(root)
return data
end
else
function otfsvg.filterglyph(entry,index)
return entry.data
end
end
function otfsvg.topdf(svgshapes)
local inkscape=io.popen("inkscape --shell > temp-otf-svg-shape.log","w")
local pdfshapes={}
local nofshapes=#svgshapes
local f_svgfile=formatters["temp-otf-svg-shape-%i.svg"]
local f_pdffile=formatters["temp-otf-svg-shape-%i.pdf"]
local f_convert=formatters["%s --export-pdf=%s\n"]
local filterglyph=otfsvg.filterglyph
report_svg("processing %i svg containers",nofshapes)
statistics.starttiming()
for i=1,nofshapes do
local entry=svgshapes[i]
for index=entry.first,entry.last do
local data=filterglyph(entry,index)
if data and data~="" then
local svgfile=f_svgfile(index)
local pdffile=f_pdffile(index)
savedata(svgfile,data)
inkscape:write(f_convert(svgfile,pdffile))
pdfshapes[index]=true
end
end
end
inkscape:write("quit\n")
inkscape:close()
report_svg("processing %i pdf results",nofshapes)
for index in next,pdfshapes do
local svgfile=f_svgfile(index)
local pdffile=f_pdffile(index)
pdfshapes[index]=loaddata(pdffile)
remove(svgfile)
remove(pdffile)
end
statistics.stoptiming()
if statistics.elapsedseconds then
report_svg("svg conversion time %s",statistics.elapsedseconds())
end
return pdfshapes
end
end
local function initializesvg(tfmdata,kind,value)
if value and otf.svgenabled then
local characters=tfmdata.characters
local descriptions=tfmdata.descriptions
local properties=tfmdata.properties
local svg=properties.svg
local hash=svg and svg.hash
local timestamp=svg and svg.timestamp
if not hash then
return
end
local pdffile=containers.read(otf.pdfcache,hash)
local pdfshapes=pdffile and pdffile.pdfshapes
if not pdfshapes or pdffile.timestamp~=timestamp then
local svgfile=containers.read(otf.svgcache,hash)
local svgshapes=svgfile and svgfile.svgshapes
pdfshapes=svgshapes and otfsvg.topdf(svgshapes) or {}
containers.write(otf.pdfcache,hash,{
pdfshapes=pdfshapes,
timestamp=timestamp,
})
end
if not pdfshapes or not next(pdfshapes) then
return
end
properties.virtualized=true
tfmdata.fonts={
{ id=0 }
}
local getactualtext=otf.getactualtext
local storepdfdata=otfsvg.storepdfdata
local nop={ "nop" }
for unicode,character in next,characters do
local index=character.index
if index then
local pdf=pdfshapes[index]
if pdf then
local setcode,name,nilcode=storepdfdata(pdf)
if name then
local bt,et=getactualtext(unicode)
local wd=character.width or 0
local ht=character.height or 0
local dp=character.depth or 0
character.commands={
{ "special","pdf:direct:"..bt },
{ "down",dp },
setcode and { "lua",setcode } or nop,
{ "image",{ filename=name,width=wd,height=ht,depth=dp } },
nilcode and { "lua",nilcode } or nop,
{ "special","pdf:direct:"..et },
}
character.svg=true
end
end
end
end
end
end
fonts.handlers.otf.features.register {
name="svg",
description="svg glyphs",
manipulators={
base=initializesvg,
node=initializesvg,
}
}
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-ocl”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-otc” 85d63e257c748c624768aa7c8ec7f0bc] ---
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,sortedkeys,tohash=string.format,table.insert,table.sortedkeys,table.tohash
local type,next=type,next
local lpegmatch=lpeg.match
local utfbyte,utflen=utf.byte,utf.len
local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end)
local report_otf=logs.reporter("fonts","otf loading")
local fonts=fonts
local otf=fonts.handlers.otf
local registerotffeature=otf.features.register
local setmetatableindex=table.setmetatableindex
local normalized={
substitution="substitution",
single="substitution",
ligature="ligature",
alternate="alternate",
multiple="multiple",
kern="kern",
pair="pair",
chainsubstitution="chainsubstitution",
chainposition="chainposition",
}
local types={
substitution="gsub_single",
ligature="gsub_ligature",
alternate="gsub_alternate",
multiple="gsub_multiple",
kern="gpos_pair",
pair="gpos_pair",
chainsubstitution="gsub_contextchain",
chainposition="gpos_contextchain",
}
local names={
gsub_single="gsub",
gsub_multiple="gsub",
gsub_alternate="gsub",
gsub_ligature="gsub",
gsub_context="gsub",
gsub_contextchain="gsub",
gsub_reversecontextchain="gsub",
gpos_single="gpos",
gpos_pair="gpos",
gpos_cursive="gpos",
gpos_mark2base="gpos",
gpos_mark2ligature="gpos",
gpos_mark2mark="gpos",
gpos_context="gpos",
gpos_contextchain="gpos",
}
setmetatableindex(types,function(t,k) t[k]=k return k end)
local everywhere={ ["*"]={ ["*"]=true } }
local noflags={ false,false,false,false }
local function getrange(sequences,category)
local count=#sequences
local first=nil
local last=nil
for i=1,count do
local t=sequences[i].type
if t and names[t]==category then
if not first then
first=i
end
last=i
end
end
return first or 1,last or count
end
local function validspecification(specification,name)
local dataset=specification.dataset
if dataset then
elseif specification[1] then
dataset=specification
specification={ dataset=dataset }
else
dataset={ { data=specification.data } }
specification.data=nil
specification.dataset=dataset
end
local first=dataset[1]
if first then
first=first.data
end
if not first then
report_otf("invalid feature specification, no dataset")
return
end
if type(name)~="string" then
name=specification.name or first.name
end
if type(name)~="string" then
report_otf("invalid feature specification, no name")
return
end
local n=#dataset
if n>0 then
for i=1,n do
setmetatableindex(dataset[i],specification)
end
return specification,name
end
end
local function addfeature(data,feature,specifications)
if not specifications then
report_otf("missing specification")
return
end
local descriptions=data.descriptions
local resources=data.resources
local features=resources.features
local sequences=resources.sequences
if not features or not sequences then
report_otf("missing specification")
return
end
local alreadydone=resources.alreadydone
if not alreadydone then
alreadydone={}
resources.alreadydone=alreadydone
end
if alreadydone[specifications] then
return
else
alreadydone[specifications]=true
end
local fontfeatures=resources.features or everywhere
local unicodes=resources.unicodes
local splitter=lpeg.splitter(" ",unicodes)
local done=0
local skip=0
local aglunicodes=false
local specifications=validspecification(specifications,feature)
if not specifications then
return
end
local function tounicode(code)
if not code then
return
end
if type(code)=="number" then
return code
end
local u=unicodes[code]
if u then
return u
end
if utflen(code)==1 then
u=utfbyte(code)
if u then
return u
end
end
if not aglunicodes then
aglunicodes=fonts.encodings.agl.unicodes
end
return aglunicodes[code]
end
local coverup=otf.coverup
local coveractions=coverup.actions
local stepkey=coverup.stepkey
local register=coverup.register
local function prepare_substitution(list,featuretype)
local coverage={}
local cover=coveractions[featuretype]
for code,replacement in next,list do
local unicode=tounicode(code)
local description=descriptions[unicode]
if description then
if type(replacement)=="table" then
replacement=replacement[1]
end
replacement=tounicode(replacement)
if replacement and descriptions[replacement] then
cover(coverage,unicode,replacement)
done=done+1
else
skip=skip+1
end
else
skip=skip+1
end
end
return coverage
end
local function prepare_alternate(list,featuretype)
local coverage={}
local cover=coveractions[featuretype]
for code,replacement in next,list do
local unicode=tounicode(code)
local description=descriptions[unicode]
if not description then
skip=skip+1
elseif type(replacement)=="table" then
local r={}
for i=1,#replacement do
local u=tounicode(replacement[i])
r[i]=descriptions[u] and u or unicode
end
cover(coverage,unicode,r)
done=done+1
else
local u=tounicode(replacement)
if u then
cover(coverage,unicode,{ u })
done=done+1
else
skip=skip+1
end
end
end
return coverage
end
local function prepare_multiple(list,featuretype)
local coverage={}
local cover=coveractions[featuretype]
for code,replacement in next,list do
local unicode=tounicode(code)
local description=descriptions[unicode]
if not description then
skip=skip+1
elseif type(replacement)=="table" then
local r,n={},0
for i=1,#replacement do
local u=tounicode(replacement[i])
if descriptions[u] then
n=n+1
r[n]=u
end
end
if n>0 then
cover(coverage,unicode,r)
done=done+1
else
skip=skip+1
end
else
local u=tounicode(replacement)
if u then
cover(coverage,unicode,{ u })
done=done+1
else
skip=skip+1
end
end
end
return coverage
end
local function prepare_ligature(list,featuretype)
local coverage={}
local cover=coveractions[featuretype]
for code,ligature in next,list do
local unicode=tounicode(code)
local description=descriptions[unicode]
if description then
if type(ligature)=="string" then
ligature={ lpegmatch(splitter,ligature) }
end
local present=true
for i=1,#ligature do
local l=ligature[i]
local u=tounicode(l)
if descriptions[u] then
ligature[i]=u
else
present=false
break
end
end
if present then
cover(coverage,unicode,ligature)
done=done+1
else
skip=skip+1
end
else
skip=skip+1
end
end
return coverage
end
local function prepare_kern(list,featuretype)
local coverage={}
local cover=coveractions[featuretype]
for code,replacement in next,list do
local unicode=tounicode(code)
local description=descriptions[unicode]
if description and type(replacement)=="table" then
local r={}
for k,v in next,replacement do
local u=tounicode(k)
if u then
r[u]=v
end
end
if next(r) then
cover(coverage,unicode,r)
done=done+1
else
skip=skip+1
end
else
skip=skip+1
end
end
return coverage
end
local function prepare_pair(list,featuretype)
local coverage={}
local cover=coveractions[featuretype]
if cover then
for code,replacement in next,list do
local unicode=tounicode(code)
local description=descriptions[unicode]
if description and type(replacement)=="table" then
local r={}
for k,v in next,replacement do
local u=tounicode(k)
if u then
r[u]=v
end
end
if next(r) then
cover(coverage,unicode,r)
done=done+1
else
skip=skip+1
end
else
skip=skip+1
end
end
else
report_otf("unknown cover type %a",featuretype)
end
return coverage
end
local function prepare_chain(list,featuretype,sublookups)
local rules=list.rules
local coverage={}
if rules then
local rulehash={}
local rulesize=0
local sequence={}
local nofsequences=0
local lookuptype=types[featuretype]
for nofrules=1,#rules do
local rule=rules[nofrules]
local current=rule.current
local before=rule.before
local after=rule.after
local replacements=rule.replacements or false
local sequence={}
local nofsequences=0
if before then
for n=1,#before do
nofsequences=nofsequences+1
sequence[nofsequences]=before[n]
end
end
local start=nofsequences+1
for n=1,#current do
nofsequences=nofsequences+1
sequence[nofsequences]=current[n]
end
local stop=nofsequences
if after then
for n=1,#after do
nofsequences=nofsequences+1
sequence[nofsequences]=after[n]
end
end
local lookups=rule.lookups or false
local subtype=nil
if lookups and sublookups then
for k,v in next,lookups do
local lookup=sublookups[v]
if lookup then
lookups[k]=lookup
if not subtype then
subtype=lookup.type
end
else
end
end
end
if nofsequences>0 then
local hashed={}
for i=1,nofsequences do
local t={}
local s=sequence[i]
for i=1,#s do
local u=tounicode(s[i])
if u then
t[u]=true
end
end
hashed[i]=t
end
sequence=hashed
rulesize=rulesize+1
rulehash[rulesize]={
nofrules,
lookuptype,
sequence,
start,
stop,
lookups,
replacements,
subtype,
}
for unic in next,sequence[start] do
local cu=coverage[unic]
if not cu then
coverage[unic]=rulehash
end
end
end
end
end
return coverage
end
local dataset=specifications.dataset
local function report(name,category,position,first,last,sequences)
report_otf("injecting name %a of category %a at position %i in [%i,%i] of [%i,%i]",
name,category,position,first,last,1,#sequences)
end
local function inject(specification,sequences,sequence,first,last,category,name)
local position=specification.position or false
if not position then
position=specification.prepend
if position==true then
if trace_loading then
report(name,category,first,first,last,sequences)
end
insert(sequences,first,sequence)
return
end
end
if not position then
position=specification.append
if position==true then
if trace_loading then
report(name,category,last+1,first,last,sequences)
end
insert(sequences,last+1,sequence)
return
end
end
local kind=type(position)
if kind=="string" then
local index=false
for i=first,last do
local s=sequences[i]
local f=s.features
if f then
for k in next,f do
if k==position then
index=i
break
end
end
if index then
break
end
end
end
if index then
position=index
else
position=last+1
end
elseif kind=="number" then
if position<0 then
position=last-position+1
end
if position>last then
position=last+1
elseif position<first then
position=first
end
else
position=last+1
end
if trace_loading then
report(name,category,position,first,last,sequences)
end
insert(sequences,position,sequence)
end
for s=1,#dataset do
local specification=dataset[s]
local valid=specification.valid
local feature=specification.name or feature
if not feature or feature=="" then
report_otf("no valid name given for extra feature")
elseif not valid or valid(data,specification,feature) then
local initialize=specification.initialize
if initialize then
specification.initialize=initialize(specification,data) and initialize or nil
end
local askedfeatures=specification.features or everywhere
local askedsteps=specification.steps or specification.subtables or { specification.data } or {}
local featuretype=normalized[specification.type or "substitution"] or "substitution"
local featureflags=specification.flags or noflags
local featureorder=specification.order or { feature }
local featurechain=(featuretype=="chainsubstitution" or featuretype=="chainposition") and 1 or 0
local nofsteps=0
local steps={}
local sublookups=specification.lookups
local category=nil
if sublookups then
local s={}
for i=1,#sublookups do
local specification=sublookups[i]
local askedsteps=specification.steps or specification.subtables or { specification.data } or {}
local featuretype=normalized[specification.type or "substitution"] or "substitution"
local featureflags=specification.flags or noflags
local nofsteps=0
local steps={}
for i=1,#askedsteps do
local list=askedsteps[i]
local coverage=nil
local format=nil
if featuretype=="substitution" then
coverage=prepare_substitution(list,featuretype)
elseif featuretype=="ligature" then
coverage=prepare_ligature(list,featuretype)
elseif featuretype=="alternate" then
coverage=prepare_alternate(list,featuretype)
elseif featuretype=="multiple" then
coverage=prepare_multiple(list,featuretype)
elseif featuretype=="kern" then
format="kern"
coverage=prepare_kern(list,featuretype)
elseif featuretype=="pair" then
format="pair"
coverage=prepare_pair(list,featuretype)
end
if coverage and next(coverage) then
nofsteps=nofsteps+1
steps[nofsteps]=register(coverage,featuretype,format,feature,nofsteps,descriptions,resources)
end
end
s[i]={
[stepkey]=steps,
nofsteps=nofsteps,
type=types[featuretype],
}
end
sublookups=s
end
for i=1,#askedsteps do
local list=askedsteps[i]
local coverage=nil
local format=nil
if featuretype=="substitution" then
category="gsub"
coverage=prepare_substitution(list,featuretype)
elseif featuretype=="ligature" then
category="gsub"
coverage=prepare_ligature(list,featuretype)
elseif featuretype=="alternate" then
category="gsub"
coverage=prepare_alternate(list,featuretype)
elseif featuretype=="multiple" then
category="gsub"
coverage=prepare_multiple(list,featuretype)
elseif featuretype=="kern" then
category="gpos"
format="kern"
coverage=prepare_kern(list,featuretype)
elseif featuretype=="pair" then
category="gpos"
format="pair"
coverage=prepare_pair(list,featuretype)
elseif featuretype=="chainsubstitution" then
category="gsub"
coverage=prepare_chain(list,featuretype,sublookups)
elseif featuretype=="chainposition" then
category="gpos"
coverage=prepare_chain(list,featuretype,sublookups)
else
report_otf("not registering feature %a, unknown category",feature)
return
end
if coverage and next(coverage) then
nofsteps=nofsteps+1
steps[nofsteps]=register(coverage,featuretype,format,feature,nofsteps,descriptions,resources)
end
end
if nofsteps>0 then
for k,v in next,askedfeatures do
if v[1] then
askedfeatures[k]=tohash(v)
end
end
if featureflags[1] then featureflags[1]="mark" end
if featureflags[2] then featureflags[2]="ligature" end
if featureflags[3] then featureflags[3]="base" end
local steptype=types[featuretype]
local sequence={
chain=featurechain,
features={ [feature]=askedfeatures },
flags=featureflags,
name=feature,
order=featureorder,
[stepkey]=steps,
nofsteps=nofsteps,
type=steptype,
}
local first,last=getrange(sequences,category)
inject(specification,sequences,sequence,first,last,category,feature)
local features=fontfeatures[category]
if not features then
features={}
fontfeatures[category]=features
end
local k=features[feature]
if not k then
k={}
features[feature]=k
end
for script,languages in next,askedfeatures do
local kk=k[script]
if not kk then
kk={}
k[script]=kk
end
for language,value in next,languages do
kk[language]=value
end
end
end
end
end
if trace_loading then
report_otf("registering feature %a, affected glyphs %a, skipped glyphs %a",feature,done,skip)
end
end
otf.enhancers.addfeature=addfeature
local extrafeatures={}
local knownfeatures={}
function otf.addfeature(name,specification)
if type(name)=="table" then
specification=name
end
if type(specification)~="table" then
report_otf("invalid feature specification, no valid table")
return
end
specification,name=validspecification(specification,name)
if name and specification then
local slot=knownfeatures[name]
if slot then
else
slot=#extrafeatures+1
knownfeatures[name]=slot
end
specification.name=name
extrafeatures[slot]=specification
end
end
local function enhance(data,filename,raw)
for slot=1,#extrafeatures do
local specification=extrafeatures[slot]
addfeature(data,specification.name,specification)
end
end
otf.enhancers.enhance=enhance
otf.enhancers.register("check extra features",enhance)
local tlig={
[0x2013]={ 0x002D,0x002D },
[0x2014]={ 0x002D,0x002D,0x002D },
}
local tlig_specification={
type="ligature",
features=everywhere,
data=tlig,
order={ "tlig" },
flags=noflags,
prepend=true,
}
otf.addfeature("tlig",tlig_specification)
registerotffeature {
name='tlig',
description='tex ligatures',
}
local trep={
[0x0027]=0x2019,
}
local trep_specification={
type="substitution",
features=everywhere,
data=trep,
order={ "trep" },
flags=noflags,
prepend=true,
}
otf.addfeature("trep",trep_specification)
registerotffeature {
name='trep',
description='tex replacements',
}
local anum_arabic={
[0x0030]=0x0660,
[0x0031]=0x0661,
[0x0032]=0x0662,
[0x0033]=0x0663,
[0x0034]=0x0664,
[0x0035]=0x0665,
[0x0036]=0x0666,
[0x0037]=0x0667,
[0x0038]=0x0668,
[0x0039]=0x0669,
}
local anum_persian={
[0x0030]=0x06F0,
[0x0031]=0x06F1,
[0x0032]=0x06F2,
[0x0033]=0x06F3,
[0x0034]=0x06F4,
[0x0035]=0x06F5,
[0x0036]=0x06F6,
[0x0037]=0x06F7,
[0x0038]=0x06F8,
[0x0039]=0x06F9,
}
local function valid(data)
local features=data.resources.features
if features then
for k,v in next,features do
for k,v in next,v do
if v.arab then
return true
end
end
end
end
end
local anum_specification={
{
type="substitution",
features={ arab={ urd=true,dflt=true } },
order={ "anum" },
data=anum_arabic,
flags=noflags,
valid=valid,
},
{
type="substitution",
features={ arab={ urd=true } },
order={ "anum" },
data=anum_persian,
flags=noflags,
valid=valid,
},
}
otf.addfeature("anum",anum_specification)
registerotffeature {
name='anum',
description='arabic digits',
}
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-otc”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-onr” 205c8bc640715aecf3538a33b842f450] ---
if not modules then modules={} end modules ['font-onr']={
version=1.001,
comment="companion to font-ini.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local fonts,logs,trackers,resolvers=fonts,logs,trackers,resolvers
local next,type,tonumber,rawget,rawset=next,type,tonumber,rawget,rawset
local match,lower,gsub,strip,find=string.match,string.lower,string.gsub,string.strip,string.find
local char,byte,sub=string.char,string.byte,string.sub
local abs=math.abs
local bxor,rshift=bit32.bxor,bit32.rshift
local P,S,R,Cmt,C,Ct,Cs,Carg,Cf,Cg=lpeg.P,lpeg.S,lpeg.R,lpeg.Cmt,lpeg.C,lpeg.Ct,lpeg.Cs,lpeg.Carg,lpeg.Cf,lpeg.Cg
local lpegmatch,patterns=lpeg.match,lpeg.patterns
local trace_indexing=false trackers.register("afm.indexing",function(v) trace_indexing=v end)
local trace_loading=false trackers.register("afm.loading",function(v) trace_loading=v end)
local report_afm=logs.reporter("fonts","afm loading")
local report_pfb=logs.reporter("fonts","pfb loading")
local handlers=fonts.handlers
local afm=handlers.afm or {}
handlers.afm=afm
local readers=afm.readers or {}
afm.readers=readers
afm.version=1.512
local get_indexes,get_shapes
do
local decrypt
do
local r,c1,c2,n=0,0,0,0
local function step(c)
local cipher=byte(c)
local plain=bxor(cipher,rshift(r,8))
r=((cipher+r)*c1+c2)%65536
return char(plain)
end
decrypt=function(binary,initial,seed)
r,c1,c2,n=initial,52845,22719,seed
binary=gsub(binary,".",step)
return sub(binary,n+1)
end
end
local charstrings=P("/CharStrings")
local subroutines=P("/Subrs")
local encoding=P("/Encoding")
local dup=P("dup")
local put=P("put")
local array=P("array")
local name=P("/")*C((R("az")+R("AZ")+R("09")+S("-_."))^1)
local digits=R("09")^1
local cardinal=digits/tonumber
local spaces=P(" ")^1
local spacing=patterns.whitespace^0
local routines,vector,chars,n,m
local initialize=function(str,position,size)
n=0
m=size
return position+1
end
local setroutine=function(str,position,index,size)
local forward=position+tonumber(size)
local stream=sub(str,position+1,forward)
routines[index]=decrypt(stream,4330,4)
return forward
end
local setvector=function(str,position,name,size)
local forward=position+tonumber(size)
if n>=m then
return #str
elseif forward<#str then
vector[n]=name
n=n+1
return forward
else
return #str
end
end
local setshapes=function(str,position,name,size)
local forward=position+tonumber(size)
local stream=sub(str,position+1,forward)
if n>m then
return #str
elseif forward<#str then
vector[n]=name
n=n+1
chars [n]=decrypt(stream,4330,4)
return forward
else
return #str
end
end
local p_rd=spacing*(P("RD")+P("-|"))
local p_np=spacing*(P("NP")+P("|"))
local p_nd=spacing*(P("ND")+P("|"))
local p_filterroutines=
(1-subroutines)^0*subroutines*spaces*Cmt(cardinal,initialize)*(Cmt(cardinal*spaces*cardinal*p_rd,setroutine)*p_np+P(1))^1
local p_filtershapes=
(1-charstrings)^0*charstrings*spaces*Cmt(cardinal,initialize)*(Cmt(name*spaces*cardinal*p_rd,setshapes)*p_nd+P(1))^1
local p_filternames=Ct (
(1-charstrings)^0*charstrings*spaces*Cmt(cardinal,initialize)*(Cmt(name*spaces*cardinal,setvector)+P(1))^1
)
local p_filterencoding=(1-encoding)^0*encoding*spaces*digits*spaces*array*(1-dup)^0*Cf(
Ct("")*Cg(spacing*dup*spaces*cardinal*spaces*name*spaces*put)^1
,rawset)
local function loadpfbvector(filename,shapestoo)
local data=io.loaddata(resolvers.findfile(filename))
if not data then
report_pfb("no data in %a",filename)
return
end
if not (find(data,"!PS%-AdobeFont%-") or find(data,"%%!FontType1")) then
report_pfb("no font in %a",filename)
return
end
local ascii,binary=match(data,"(.*)eexec%s+......(.*)")
if not binary then
report_pfb("no binary data in %a",filename)
return
end
binary=decrypt(binary,55665,4)
local names={}
local encoding=lpegmatch(p_filterencoding,ascii)
local glyphs={}
routines,vector,chars={},{},{}
if shapestoo then
lpegmatch(p_filterroutines,binary)
lpegmatch(p_filtershapes,binary)
local data={
dictionaries={
{
charstrings=chars,
charset=vector,
subroutines=routines,
}
},
}
fonts.handlers.otf.readers.parsecharstrings(data,glyphs,true,true)
else
lpegmatch(p_filternames,binary)
end
names=vector
routines,vector,chars=nil,nil,nil
return names,encoding,glyphs
end
local pfb=handlers.pfb or {}
handlers.pfb=pfb
pfb.loadvector=loadpfbvector
get_indexes=function(data,pfbname)
local vector=loadpfbvector(pfbname)
if vector then
local characters=data.characters
if trace_loading then
report_afm("getting index data from %a",pfbname)
end
for index=1,#vector do
local name=vector[index]
local char=characters[name]
if char then
if trace_indexing then
report_afm("glyph %a has index %a",name,index)
end
char.index=index
end
end
end
end
get_shapes=function(pfbname)
local vector,encoding,glyphs=loadpfbvector(pfbname,true)
return glyphs
end
end
local spacer=patterns.spacer
local whitespace=patterns.whitespace
local lineend=patterns.newline
local spacing=spacer^0
local number=spacing*S("+-")^-1*(R("09")+S("."))^1/tonumber
local name=spacing*C((1-whitespace)^1)
local words=spacing*((1-lineend)^1/strip)
local rest=(1-lineend)^0
local fontdata=Carg(1)
local semicolon=spacing*P(";")
local plus=spacing*P("plus")*number
local minus=spacing*P("minus")*number
local function addkernpair(data,one,two,value)
local chr=data.characters[one]
if chr then
local kerns=chr.kerns
if kerns then
kerns[two]=tonumber(value)
else
chr.kerns={ [two]=tonumber(value) }
end
end
end
local p_kernpair=(fontdata*P("KPX")*name*name*number)/addkernpair
local chr=false
local ind=0
local function start(data,version)
data.metadata.afmversion=version
ind=0
chr={}
end
local function stop()
ind=0
chr=false
end
local function setindex(i)
if i<0 then
ind=ind+1
else
ind=i
end
chr={
index=ind
}
end
local function setwidth(width)
chr.width=width
end
local function setname(data,name)
data.characters[name]=chr
end
local function setboundingbox(boundingbox)
chr.boundingbox=boundingbox
end
local function setligature(plus,becomes)
local ligatures=chr.ligatures
if ligatures then
ligatures[plus]=becomes
else
chr.ligatures={ [plus]=becomes }
end
end
local p_charmetric=((
P("C")*number/setindex+P("WX")*number/setwidth+P("N")*fontdata*name/setname+P("B")*Ct((number)^4)/setboundingbox+P("L")*(name)^2/setligature
)*semicolon )^1
local p_charmetrics=P("StartCharMetrics")*number*(p_charmetric+(1-P("EndCharMetrics")))^0*P("EndCharMetrics")
local p_kernpairs=P("StartKernPairs")*number*(p_kernpair+(1-P("EndKernPairs" )))^0*P("EndKernPairs" )
local function set_1(data,key,a) data.metadata[lower(key)]=a end
local function set_2(data,key,a,b) data.metadata[lower(key)]={ a,b } end
local function set_3(data,key,a,b,c) data.metadata[lower(key)]={ a,b,c } end
local p_parameters=P(false)+fontdata*((P("FontName")+P("FullName")+P("FamilyName"))/lower)*words/function(data,key,value)
data.metadata[key]=value
end+fontdata*((P("Weight")+P("Version"))/lower)*name/function(data,key,value)
data.metadata[key]=value
end+fontdata*P("IsFixedPitch")*name/function(data,pitch)
data.metadata.monospaced=toboolean(pitch,true)
end+fontdata*P("FontBBox")*Ct(number^4)/function(data,boundingbox)
data.metadata.boundingbox=boundingbox
end+fontdata*((P("CharWidth")+P("CapHeight")+P("XHeight")+P("Descender")+P("Ascender")+P("ItalicAngle"))/lower)*number/function(data,key,value)
data.metadata[key]=value
end+P("Comment")*spacing*(P(false)+(fontdata*C("DESIGNSIZE")*number*rest)/set_1
+(fontdata*C("TFM designsize")*number*rest)/set_1+(fontdata*C("DesignSize")*number*rest)/set_1+(fontdata*C("CODINGSCHEME")*words*rest)/set_1
+(fontdata*C("CHECKSUM")*number*words*rest)/set_1
+(fontdata*C("SPACE")*number*plus*minus*rest)/set_3
+(fontdata*C("QUAD")*number*rest)/set_1
+(fontdata*C("EXTRASPACE")*number*rest)/set_1
+(fontdata*C("NUM")*number*number*number*rest)/set_3
+(fontdata*C("DENOM")*number*number*rest)/set_2
+(fontdata*C("SUP")*number*number*number*rest)/set_3
+(fontdata*C("SUB")*number*number*rest)/set_2
+(fontdata*C("SUPDROP")*number*rest)/set_1
+(fontdata*C("SUBDROP")*number*rest)/set_1
+(fontdata*C("DELIM")*number*number*rest)/set_2
+(fontdata*C("AXISHEIGHT")*number*rest)/set_1
)
local fullparser=(P("StartFontMetrics")*fontdata*name/start )*(p_charmetrics+p_kernpairs+p_parameters+(1-P("EndFontMetrics")) )^0*(P("EndFontMetrics")/stop )
local fullparser=(P("StartFontMetrics")*fontdata*name/start )*(p_charmetrics+p_kernpairs+p_parameters+(1-P("EndFontMetrics")) )^0*(P("EndFontMetrics")/stop )
local infoparser=(P("StartFontMetrics")*fontdata*name/start )*(p_parameters+(1-P("EndFontMetrics")) )^0*(P("EndFontMetrics")/stop )
local function read(filename,parser)
local afmblob=io.loaddata(filename)
if afmblob then
local data={
resources={
filename=resolvers.unresolve(filename),
version=afm.version,
creator="context mkiv",
},
properties={
hasitalics=false,
},
goodies={},
metadata={
filename=file.removesuffix(file.basename(filename))
},
characters={
},
descriptions={
},
}
if trace_loading then
report_afm("parsing afm file %a",filename)
end
lpegmatch(parser,afmblob,1,data)
return data
else
if trace_loading then
report_afm("no valid afm file %a",filename)
end
return nil
end
end
function readers.loadfont(afmname,pfbname)
local data=read(resolvers.findfile(afmname),fullparser)
if data then
if not pfbname or pfbname=="" then
pfbname=file.replacesuffix(file.nameonly(afmname),"pfb")
pfbname=resolvers.findfile(pfbname)
end
if pfbname and pfbname~="" then
data.resources.filename=resolvers.unresolve(pfbname)
get_indexes(data,pfbname)
elseif trace_loading then
report_afm("no pfb file for %a",afmname)
end
return data
end
end
function readers.loadshapes(filename)
local fullname=resolvers.findfile(filename) or ""
if fullname=="" then
return {
filename="not found: "..filename,
glyphs={}
}
else
return {
filename=fullname,
format="opentype",
glyphs=get_shapes(fullname) or {},
units=1000,
}
end
end
function readers.getinfo(filename)
local data=read(resolvers.findfile(filename),infoparser)
if data then
return data.metadata
end
end
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-onr”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-one” 6fbf6b9e219a944cd1ad5933d77cc488] ---
if not modules then modules={} end modules ['font-one']={
version=1.001,
comment="companion to font-ini.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local fonts,logs,trackers,containers,resolvers=fonts,logs,trackers,containers,resolvers
local next,type,tonumber,rawget=next,type,tonumber,rawget
local match,gmatch,lower,gsub,strip,find=string.match,string.gmatch,string.lower,string.gsub,string.strip,string.find
local char,byte,sub=string.char,string.byte,string.sub
local abs=math.abs
local bxor,rshift=bit32.bxor,bit32.rshift
local P,S,R,Cmt,C,Ct,Cs,Carg=lpeg.P,lpeg.S,lpeg.R,lpeg.Cmt,lpeg.C,lpeg.Ct,lpeg.Cs,lpeg.Carg
local lpegmatch,patterns=lpeg.match,lpeg.patterns
local trace_features=false trackers.register("afm.features",function(v) trace_features=v end)
local trace_indexing=false trackers.register("afm.indexing",function(v) trace_indexing=v end)
local trace_loading=false trackers.register("afm.loading",function(v) trace_loading=v end)
local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end)
local report_afm=logs.reporter("fonts","afm loading")
local setmetatableindex=table.setmetatableindex
local derivetable=table.derive
local findbinfile=resolvers.findbinfile
local definers=fonts.definers
local readers=fonts.readers
local constructors=fonts.constructors
local afm=constructors.handlers.afm
local pfb=constructors.handlers.pfb
local otf=fonts.handlers.otf
local otfreaders=otf.readers
local otfenhancers=otf.enhancers
local afmfeatures=constructors.features.afm
local registerafmfeature=afmfeatures.register
local afmenhancers=constructors.enhancers.afm
local registerafmenhancer=afmenhancers.register
afm.version=1.512
afm.cache=containers.define("fonts","one",afm.version,true)
afm.autoprefixed=true
afm.helpdata={}
afm.syncspace=true
local overloads=fonts.mappings.overloads
local applyruntimefixes=fonts.treatments and fonts.treatments.applyfixes
function afm.load(filename)
filename=resolvers.findfile(filename,'afm') or ""
if filename~="" and not fonts.names.ignoredfile(filename) then
local name=file.removesuffix(file.basename(filename))
local data=containers.read(afm.cache,name)
local attr=lfs.attributes(filename)
local size,time=attr.size or 0,attr.modification or 0
local pfbfile=file.replacesuffix(name,"pfb")
local pfbname=resolvers.findfile(pfbfile,"pfb") or ""
if pfbname=="" then
pfbname=resolvers.findfile(file.basename(pfbfile),"pfb") or ""
end
local pfbsize,pfbtime=0,0
if pfbname~="" then
local attr=lfs.attributes(pfbname)
pfbsize=attr.size or 0
pfbtime=attr.modification or 0
end
if not data or data.size~=size or data.time~=time or data.pfbsize~=pfbsize or data.pfbtime~=pfbtime then
report_afm("reading %a",filename)
data=afm.readers.loadfont(filename,pfbname)
if data then
afmenhancers.apply(data,filename)
fonts.mappings.addtounicode(data,filename)
otfreaders.pack(data)
data.size=size
data.time=time
data.pfbsize=pfbsize
data.pfbtime=pfbtime
report_afm("saving %a in cache",name)
data=containers.write(afm.cache,name,data)
data=containers.read(afm.cache,name)
end
end
if data then
otfreaders.unpack(data)
otfreaders.expand(data)
otfreaders.addunicodetable(data)
otfenhancers.apply(data,filename,data)
if applyruntimefixes then
applyruntimefixes(filename,data)
end
end
return data
end
end
local uparser=fonts.mappings.makenameparser()
local function enhance_unify_names(data,filename)
local unicodevector=fonts.encodings.agl.unicodes
local unicodes={}
local names={}
local private=constructors.privateoffset
local descriptions=data.descriptions
for name,blob in next,data.characters do
local code=unicodevector[name]
if not code then
code=lpegmatch(uparser,name)
if type(code)~="number" then
code=private
private=private+1
report_afm("assigning private slot %U for unknown glyph name %a",code,name)
end
end
local index=blob.index
unicodes[name]=code
names[name]=index
blob.name=name
descriptions[code]={
boundingbox=blob.boundingbox,
width=blob.width,
kerns=blob.kerns,
index=index,
name=name,
}
end
for unicode,description in next,descriptions do
local kerns=description.kerns
if kerns then
local krn={}
for name,kern in next,kerns do
local unicode=unicodes[name]
if unicode then
krn[unicode]=kern
else
end
end
description.kerns=krn
end
end
data.characters=nil
local resources=data.resources
local filename=resources.filename or file.removesuffix(file.basename(filename))
resources.filename=resolvers.unresolve(filename)
resources.unicodes=unicodes
resources.marks={}
resources.private=private
end
local everywhere={ ["*"]={ ["*"]=true } }
local noflags={ false,false,false,false }
local function enhance_normalize_features(data)
local ligatures=setmetatableindex("table")
local kerns=setmetatableindex("table")
local extrakerns=setmetatableindex("table")
for u,c in next,data.descriptions do
local l=c.ligatures
local k=c.kerns
local e=c.extrakerns
if l then
ligatures[u]=l
for u,v in next,l do
l[u]={ ligature=v }
end
c.ligatures=nil
end
if k then
kerns[u]=k
for u,v in next,k do
k[u]=v
end
c.kerns=nil
end
if e then
extrakerns[u]=e
for u,v in next,e do
e[u]=v
end
c.extrakerns=nil
end
end
local features={
gpos={},
gsub={},
}
local sequences={
}
if next(ligatures) then
features.gsub.liga=everywhere
data.properties.hasligatures=true
sequences[#sequences+1]={
features={
liga=everywhere,
},
flags=noflags,
name="s_s_0",
nofsteps=1,
order={ "liga" },
type="gsub_ligature",
steps={
{
coverage=ligatures,
},
},
}
end
if next(kerns) then
features.gpos.kern=everywhere
data.properties.haskerns=true
sequences[#sequences+1]={
features={
kern=everywhere,
},
flags=noflags,
name="p_s_0",
nofsteps=1,
order={ "kern" },
type="gpos_pair",
steps={
{
format="kern",
coverage=kerns,
},
},
}
end
if next(extrakerns) then
features.gpos.extrakerns=everywhere
data.properties.haskerns=true
sequences[#sequences+1]={
features={
extrakerns=everywhere,
},
flags=noflags,
name="p_s_1",
nofsteps=1,
order={ "extrakerns" },
type="gpos_pair",
steps={
{
format="kern",
coverage=extrakerns,
},
},
}
end
data.resources.features=features
data.resources.sequences=sequences
end
local function enhance_fix_names(data)
for k,v in next,data.descriptions do
local n=v.name
local r=overloads[n]
if r then
local name=r.name
if trace_indexing then
report_afm("renaming characters %a to %a",n,name)
end
v.name=name
v.unicode=r.unicode
end
end
end
local addthem=function(rawdata,ligatures)
if ligatures then
local descriptions=rawdata.descriptions
local resources=rawdata.resources
local unicodes=resources.unicodes
for ligname,ligdata in next,ligatures do
local one=descriptions[unicodes[ligname]]
if one then
for _,pair in next,ligdata do
local two,three=unicodes[pair[1]],unicodes[pair[2]]
if two and three then
local ol=one.ligatures
if ol then
if not ol[two] then
ol[two]=three
end
else
one.ligatures={ [two]=three }
end
end
end
end
end
end
end
local function enhance_add_ligatures(rawdata)
addthem(rawdata,afm.helpdata.ligatures)
end
local function enhance_add_extra_kerns(rawdata)
local descriptions=rawdata.descriptions
local resources=rawdata.resources
local unicodes=resources.unicodes
local function do_it_left(what)
if what then
for unicode,description in next,descriptions do
local kerns=description.kerns
if kerns then
local extrakerns
for complex,simple in next,what do
complex=unicodes[complex]
simple=unicodes[simple]
if complex and simple then
local ks=kerns[simple]
if ks and not kerns[complex] then
if extrakerns then
extrakerns[complex]=ks
else
extrakerns={ [complex]=ks }
end
end
end
end
if extrakerns then
description.extrakerns=extrakerns
end
end
end
end
end
local function do_it_copy(what)
if what then
for complex,simple in next,what do
complex=unicodes[complex]
simple=unicodes[simple]
if complex and simple then
local complexdescription=descriptions[complex]
if complexdescription then
local simpledescription=descriptions[complex]
if simpledescription then
local extrakerns
local kerns=simpledescription.kerns
if kerns then
for unicode,kern in next,kerns do
if extrakerns then
extrakerns[unicode]=kern
else
extrakerns={ [unicode]=kern }
end
end
end
local extrakerns=simpledescription.extrakerns
if extrakerns then
for unicode,kern in next,extrakerns do
if extrakerns then
extrakerns[unicode]=kern
else
extrakerns={ [unicode]=kern }
end
end
end
if extrakerns then
complexdescription.extrakerns=extrakerns
end
end
end
end
end
end
end
do_it_left(afm.helpdata.leftkerned)
do_it_left(afm.helpdata.bothkerned)
do_it_copy(afm.helpdata.bothkerned)
do_it_copy(afm.helpdata.rightkerned)
end
local function adddimensions(data)
if data then
for unicode,description in next,data.descriptions do
local bb=description.boundingbox
if bb then
local ht,dp=bb[4],-bb[2]
if ht==0 or ht<0 then
else
description.height=ht
end
if dp==0 or dp<0 then
else
description.depth=dp
end
end
end
end
end
local function copytotfm(data)
if data and data.descriptions then
local metadata=data.metadata
local resources=data.resources
local properties=derivetable(data.properties)
local descriptions=derivetable(data.descriptions)
local goodies=derivetable(data.goodies)
local characters={}
local parameters={}
local unicodes=resources.unicodes
for unicode,description in next,data.descriptions do
characters[unicode]={}
end
local filename=constructors.checkedfilename(resources)
local fontname=metadata.fontname or metadata.fullname
local fullname=metadata.fullname or metadata.fontname
local endash=0x0020
local emdash=0x2014
local spacer="space"
local spaceunits=500
local monospaced=metadata.monospaced
local charwidth=metadata.charwidth
local italicangle=metadata.italicangle
local charxheight=metadata.xheight and metadata.xheight>0 and metadata.xheight
properties.monospaced=monospaced
parameters.italicangle=italicangle
parameters.charwidth=charwidth
parameters.charxheight=charxheight
if properties.monospaced then
if descriptions[endash] then
spaceunits,spacer=descriptions[endash].width,"space"
end
if not spaceunits and descriptions[emdash] then
spaceunits,spacer=descriptions[emdash].width,"emdash"
end
if not spaceunits and charwidth then
spaceunits,spacer=charwidth,"charwidth"
end
else
if descriptions[endash] then
spaceunits,spacer=descriptions[endash].width,"space"
end
if not spaceunits and charwidth then
spaceunits,spacer=charwidth,"charwidth"
end
end
spaceunits=tonumber(spaceunits)
if spaceunits<200 then
end
parameters.slant=0
parameters.space=spaceunits
parameters.space_stretch=500
parameters.space_shrink=333
parameters.x_height=400
parameters.quad=1000
if italicangle and italicangle~=0 then
parameters.italicangle=italicangle
parameters.italicfactor=math.cos(math.rad(90+italicangle))
parameters.slant=- math.tan(italicangle*math.pi/180)
end
if monospaced then
parameters.space_stretch=0
parameters.space_shrink=0
elseif afm.syncspace then
parameters.space_stretch=spaceunits/2
parameters.space_shrink=spaceunits/3
end
parameters.extra_space=parameters.space_shrink
if charxheight then
parameters.x_height=charxheight
else
local x=0x0078
if x then
local x=descriptions[x]
if x then
parameters.x_height=x.height
end
end
end
if metadata.sup then
local dummy={ 0,0,0 }
parameters[ 1]=metadata.designsize or 0
parameters[ 2]=metadata.checksum or 0
parameters[ 3],
parameters[ 4],
parameters[ 5]=unpack(metadata.space or dummy)
parameters[ 6]=metadata.quad or 0
parameters[ 7]=metadata.extraspace or 0
parameters[ 8],
parameters[ 9],
parameters[10]=unpack(metadata.num or dummy)
parameters[11],
parameters[12]=unpack(metadata.denom or dummy)
parameters[13],
parameters[14],
parameters[15]=unpack(metadata.sup or dummy)
parameters[16],
parameters[17]=unpack(metadata.sub or dummy)
parameters[18]=metadata.supdrop or 0
parameters[19]=metadata.subdrop or 0
parameters[20],
parameters[21]=unpack(metadata.delim or dummy)
parameters[22]=metadata.axisheight or 0
end
parameters.designsize=(metadata.designsize or 10)*65536
parameters.ascender=abs(metadata.ascender or 0)
parameters.descender=abs(metadata.descender or 0)
parameters.units=1000
properties.spacer=spacer
properties.encodingbytes=2
properties.format=fonts.formats[filename] or "type1"
properties.filename=filename
properties.fontname=fontname
properties.fullname=fullname
properties.psname=fullname
properties.name=filename or fullname or fontname
if next(characters) then
return {
characters=characters,
descriptions=descriptions,
parameters=parameters,
resources=resources,
properties=properties,
goodies=goodies,
}
end
end
return nil
end
function afm.setfeatures(tfmdata,features)
local okay=constructors.initializefeatures("afm",tfmdata,features,trace_features,report_afm)
if okay then
return constructors.collectprocessors("afm",tfmdata,features,trace_features,report_afm)
else
return {}
end
end
local function addtables(data)
local resources=data.resources
local lookuptags=resources.lookuptags
local unicodes=resources.unicodes
if not lookuptags then
lookuptags={}
resources.lookuptags=lookuptags
end
setmetatableindex(lookuptags,function(t,k)
local v=type(k)=="number" and ("lookup "..k) or k
t[k]=v
return v
end)
if not unicodes then
unicodes={}
resources.unicodes=unicodes
setmetatableindex(unicodes,function(t,k)
setmetatableindex(unicodes,nil)
for u,d in next,data.descriptions do
local n=d.name
if n then
t[n]=u
end
end
return rawget(t,k)
end)
end
constructors.addcoreunicodes(unicodes)
end
local function afmtotfm(specification)
local afmname=specification.filename or specification.name
if specification.forced=="afm" or specification.format=="afm" then
if trace_loading then
report_afm("forcing afm format for %a",afmname)
end
else
local tfmname=findbinfile(afmname,"ofm") or ""
if tfmname~="" then
if trace_loading then
report_afm("fallback from afm to tfm for %a",afmname)
end
return
end
end
if afmname~="" then
local features=constructors.checkedfeatures("afm",specification.features.normal)
specification.features.normal=features
constructors.hashinstance(specification,true)
specification=definers.resolve(specification)
local cache_id=specification.hash
local tfmdata=containers.read(constructors.cache,cache_id)
if not tfmdata then
local rawdata=afm.load(afmname)
if rawdata and next(rawdata) then
addtables(rawdata)
adddimensions(rawdata)
tfmdata=copytotfm(rawdata)
if tfmdata and next(tfmdata) then
local shared=tfmdata.shared
if not shared then
shared={}
tfmdata.shared=shared
end
shared.rawdata=rawdata
shared.dynamics={}
tfmdata.changed={}
shared.features=features
shared.processes=afm.setfeatures(tfmdata,features)
end
elseif trace_loading then
report_afm("no (valid) afm file found with name %a",afmname)
end
tfmdata=containers.write(constructors.cache,cache_id,tfmdata)
end
return tfmdata
end
end
local function read_from_afm(specification)
local tfmdata=afmtotfm(specification)
if tfmdata then
tfmdata.properties.name=specification.name
tfmdata=constructors.scale(tfmdata,specification)
local allfeatures=tfmdata.shared.features or specification.features.normal
constructors.applymanipulators("afm",tfmdata,allfeatures,trace_features,report_afm)
fonts.loggers.register(tfmdata,'afm',specification)
end
return tfmdata
end
registerafmfeature {
name="mode",
description="mode",
initializers={
base=otf.modeinitializer,
node=otf.modeinitializer,
}
}
registerafmfeature {
name="features",
description="features",
default=true,
initializers={
node=otf.nodemodeinitializer,
base=otf.basemodeinitializer,
},
processors={
node=otf.featuresprocessor,
}
}
fonts.formats.afm="type1"
fonts.formats.pfb="type1"
local function check_afm(specification,fullname)
local foundname=findbinfile(fullname,'afm') or ""
if foundname=="" then
foundname=fonts.names.getfilename(fullname,"afm") or ""
end
if foundname=="" and afm.autoprefixed then
local encoding,shortname=match(fullname,"^(.-)%-(.*)$")
if encoding and shortname and fonts.encodings.known[encoding] then
shortname=findbinfile(shortname,'afm') or ""
if shortname~="" then
foundname=shortname
if trace_defining then
report_afm("stripping encoding prefix from filename %a",afmname)
end
end
end
end
if foundname~="" then
specification.filename=foundname
specification.format="afm"
return read_from_afm(specification)
end
end
function readers.afm(specification,method)
local fullname=specification.filename or ""
local tfmdata=nil
if fullname=="" then
local forced=specification.forced or ""
if forced~="" then
tfmdata=check_afm(specification,specification.name.."."..forced)
end
if not tfmdata then
local check_tfm=readers.check_tfm
method=(check_tfm and (method or definers.method or "afm or tfm")) or "afm"
if method=="tfm" then
tfmdata=check_tfm(specification,specification.name)
elseif method=="afm" then
tfmdata=check_afm(specification,specification.name)
elseif method=="tfm or afm" then
tfmdata=check_tfm(specification,specification.name) or check_afm(specification,specification.name)
else
tfmdata=check_afm(specification,specification.name) or check_tfm(specification,specification.name)
end
end
else
tfmdata=check_afm(specification,fullname)
end
return tfmdata
end
function readers.pfb(specification,method)
local original=specification.specification
if trace_defining then
report_afm("using afm reader for %a",original)
end
specification.forced="afm"
local function swap(name)
local value=specification[swap]
if value then
specification[swap]=gsub("%.pfb",".afm",1)
end
end
swap("filename")
swap("fullname")
swap("forcedname")
swap("specification")
return readers.afm(specification,method)
end
registerafmenhancer("unify names",enhance_unify_names)
registerafmenhancer("add ligatures",enhance_add_ligatures)
registerafmenhancer("add extra kerns",enhance_add_extra_kerns)
registerafmenhancer("normalize features",enhance_normalize_features)
registerafmenhancer("check extra features",otfenhancers.enhance)
registerafmenhancer("fix names",enhance_fix_names)
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-one”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-afk” b36a76ceb835f41f8c05b471000ddc14] ---
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-afk”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-tfm” 3d813578dbf6c447e4b859c2bf0618f7] ---
if not modules then modules={} end modules ['font-tfm']={
version=1.001,
comment="companion to font-ini.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local next,type=next,type
local match,format=string.match,string.format
local concat,sortedhash=table.concat,table.sortedhash
local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end)
local trace_features=false trackers.register("tfm.features",function(v) trace_features=v end)
local report_defining=logs.reporter("fonts","defining")
local report_tfm=logs.reporter("fonts","tfm loading")
local findbinfile=resolvers.findbinfile
local setmetatableindex=table.setmetatableindex
local fonts=fonts
local handlers=fonts.handlers
local readers=fonts.readers
local constructors=fonts.constructors
local encodings=fonts.encodings
local tfm=constructors.handlers.tfm
tfm.version=1.000
tfm.maxnestingdepth=5
tfm.maxnestingsize=65536*1024
local otf=fonts.handlers.otf
local otfenhancers=otf.enhancers
local tfmfeatures=constructors.features.tfm
local registertfmfeature=tfmfeatures.register
local tfmenhancers=constructors.enhancers.tfm
local registertfmenhancer=tfmenhancers.register
constructors.resolvevirtualtoo=false
fonts.formats.tfm="type1"
fonts.formats.ofm="type1"
function tfm.setfeatures(tfmdata,features)
local okay=constructors.initializefeatures("tfm",tfmdata,features,trace_features,report_tfm)
if okay then
return constructors.collectprocessors("tfm",tfmdata,features,trace_features,report_tfm)
else
return {}
end
end
local depth={}
local function read_from_tfm(specification)
local filename=specification.filename
local size=specification.size
depth[filename]=(depth[filename] or 0)+1
if trace_defining then
report_defining("loading tfm file %a at size %s",filename,size)
end
local tfmdata=font.read_tfm(filename,size)
if tfmdata then
local features=specification.features and specification.features.normal or {}
local features=constructors.checkedfeatures("tfm",features)
specification.features.normal=features
local newtfmdata=(depth[filename]==1) and tfm.reencode(tfmdata,specification)
if newtfmdata then
tfmdata=newtfmdata
end
local resources=tfmdata.resources or {}
local properties=tfmdata.properties or {}
local parameters=tfmdata.parameters or {}
local shared=tfmdata.shared or {}
shared.features=features
shared.resources=resources
properties.name=tfmdata.name
properties.fontname=tfmdata.fontname
properties.psname=tfmdata.psname
properties.fullname=tfmdata.fullname
properties.filename=specification.filename
properties.format=fonts.formats.tfm
tfmdata.properties=properties
tfmdata.resources=resources
tfmdata.parameters=parameters
tfmdata.shared=shared
shared.rawdata={ resources=resources }
shared.features=features
if newtfmdata then
if not resources.marks then
resources.marks={}
end
if not resources.sequences then
resources.sequences={}
end
if not resources.features then
resources.features={
gsub={},
gpos={},
}
end
if not tfmdata.changed then
tfmdata.changed={}
end
if not tfmdata.descriptions then
tfmdata.descriptions=tfmdata.characters
end
otf.readers.addunicodetable(tfmdata)
tfmenhancers.apply(tfmdata,filename)
constructors.applymanipulators("tfm",tfmdata,features,trace_features,report_tfm)
otf.readers.unifymissing(tfmdata)
fonts.mappings.addtounicode(tfmdata,filename)
tfmdata.tounicode=1
local tounicode=fonts.mappings.tounicode
for unicode,v in next,tfmdata.characters do
local u=v.unicode
if u then
v.tounicode=tounicode(u)
end
end
if tfmdata.usedbitmap then
tfm.addtounicode(tfmdata)
end
end
shared.processes=next(features) and tfm.setfeatures(tfmdata,features) or nil
parameters.factor=1
parameters.size=size
parameters.slant=parameters.slant or parameters[1] or 0
parameters.space=parameters.space or parameters[2] or 0
parameters.space_stretch=parameters.space_stretch or parameters[3] or 0
parameters.space_shrink=parameters.space_shrink or parameters[4] or 0
parameters.x_height=parameters.x_height or parameters[5] or 0
parameters.quad=parameters.quad or parameters[6] or 0
parameters.extra_space=parameters.extra_space or parameters[7] or 0
constructors.enhanceparameters(parameters)
if newtfmdata then
elseif constructors.resolvevirtualtoo then
fonts.loggers.register(tfmdata,file.suffix(filename),specification)
local vfname=findbinfile(specification.name,'ovf')
if vfname and vfname~="" then
local vfdata=font.read_vf(vfname,size)
if vfdata then
local chars=tfmdata.characters
for k,v in next,vfdata.characters do
chars[k].commands=v.commands
end
properties.virtualized=true
tfmdata.fonts=vfdata.fonts
tfmdata.type="virtual"
local fontlist=vfdata.fonts
local name=file.nameonly(filename)
for i=1,#fontlist do
local n=fontlist[i].name
local s=fontlist[i].size
local d=depth[filename]
s=constructors.scaled(s,vfdata.designsize)
if d>tfm.maxnestingdepth then
report_defining("too deeply nested virtual font %a with size %a, max nesting depth %s",n,s,tfm.maxnestingdepth)
fontlist[i]={ id=0 }
elseif (d>1) and (s>tfm.maxnestingsize) then
report_defining("virtual font %a exceeds size %s",n,s)
fontlist[i]={ id=0 }
else
local t,id=fonts.constructors.readanddefine(n,s)
fontlist[i]={ id=id }
end
end
end
end
end
properties.haskerns=true
properties.hasligatures=true
resources.unicodes={}
resources.lookuptags={}
depth[filename]=depth[filename]-1
return tfmdata
else
depth[filename]=depth[filename]-1
end
end
local function check_tfm(specification,fullname)
local foundname=findbinfile(fullname,'tfm') or ""
if foundname=="" then
foundname=findbinfile(fullname,'ofm') or ""
end
if foundname=="" then
foundname=fonts.names.getfilename(fullname,"tfm") or ""
end
if foundname~="" then
specification.filename=foundname
specification.format="ofm"
return read_from_tfm(specification)
elseif trace_defining then
report_defining("loading tfm with name %a fails",specification.name)
end
end
readers.check_tfm=check_tfm
function readers.tfm(specification)
local fullname=specification.filename or ""
if fullname=="" then
local forced=specification.forced or ""
if forced~="" then
fullname=specification.name.."."..forced
else
fullname=specification.name
end
end
return check_tfm(specification,fullname)
end
readers.ofm=readers.tfm
do
local outfiles={}
local tfmcache=table.setmetatableindex(function(t,tfmdata)
local id=font.define(tfmdata)
t[tfmdata]=id
return id
end)
local encdone=table.setmetatableindex("table")
function tfm.reencode(tfmdata,specification)
local features=specification.features
if not features then
return
end
local features=features.normal
if not features then
return
end
local tfmfile=file.basename(tfmdata.name)
local encfile=features.reencode
local pfbfile=features.pfbfile
local bitmap=features.bitmap
if not encfile then
return
end
local pfbfile=outfiles[tfmfile]
if pfbfile==nil then
if bitmap then
pfbfile=false
elseif type(pfbfile)~="string" then
pfbfile=tfmfile
end
if type(pfbfile)=="string" then
pfbfile=file.addsuffix(pfbfile,"pfb")
report_tfm("using type1 shapes from %a for %a",pfbfile,tfmfile)
else
report_tfm("using bitmap shapes for %a",tfmfile)
pfbfile=false
end
outfiles[tfmfile]=pfbfile
end
local encoding=false
local vector=false
if type(pfbfile)=="string" then
local pfb=fonts.constructors.handlers.pfb
if pfb and pfb.loadvector then
local v,e=pfb.loadvector(pfbfile)
if v then
vector=v
end
if e then
encoding=e
end
end
end
if type(encfile)=="string" and encfile~="auto" then
encoding=fonts.encodings.load(file.addsuffix(encfile,"enc"))
if encoding then
encoding=encoding.vector
end
end
if not encoding then
report_tfm("bad encoding for %a, quitting",tfmfile)
return
end
local unicoding=fonts.encodings.agl and fonts.encodings.agl.unicodes
local virtualid=tfmcache[tfmdata]
local tfmdata=table.copy(tfmdata)
local characters={}
local originals=tfmdata.characters
local indices={}
local parentfont={ "font",1 }
local private=fonts.constructors.privateoffset
local reported=encdone[tfmfile][encfile]
local backmap=vector and table.swapped(vector)
local done={}
for index,name in sortedhash(encoding) do
local unicode=unicoding[name]
local original=originals[index]
if original then
if unicode then
original.unicode=unicode
else
unicode=private
private=private+1
if not reported then
report_tfm("glyph %a in font %a with encoding %a gets unicode %U",name,tfmfile,encfile,unicode)
end
end
characters[unicode]=original
indices[index]=unicode
original.name=name
if backmap then
original.index=backmap[name]
else
original.commands={ parentfont,{ "char",index } }
original.oindex=index
end
done[name]=true
elseif not done[name] then
report_tfm("bad index %a in font %a with name %a",index,tfmfile,name)
end
end
encdone[tfmfile][encfile]=true
for k,v in next,characters do
local kerns=v.kerns
if kerns then
local t={}
for k,v in next,kerns do
local i=indices[k]
if i then
t[i]=v
end
end
v.kerns=next(t) and t or nil
end
local ligatures=v.ligatures
if ligatures then
local t={}
for k,v in next,ligatures do
local i=indices[k]
if i then
t[i]=v
v.char=indices[v.char]
end
end
v.ligatures=next(t) and t or nil
end
end
tfmdata.fonts={ { id=virtualid } }
tfmdata.characters=characters
tfmdata.fullname=tfmdata.fullname or tfmdata.name
tfmdata.psname=file.nameonly(pfbfile or tfmdata.name)
tfmdata.filename=pfbfile
tfmdata.encodingbytes=2
tfmdata.format="type1"
tfmdata.tounicode=1
tfmdata.embedding="subset"
tfmdata.usedbitmap=bitmap and virtualid
return tfmdata
end
end
do
local template=[[
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo << /Registry (TeX) /Ordering (bitmap-%s) /Supplement 0 >> def
/CMapName /TeX-bitmap-%s def
/CMapType 2 def
1 begincodespacerange
<00> <FF>
endcodespacerange
%s beginbfchar
%s
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop end
end
end
]]
local flushstreamobject=lpdf and lpdf.flushstreamobject
local setfontattributes=pdf.setfontattributes
if not flushstreamobject then
flushstreamobject=function(data)
return pdf.obj {
immediate=true,
type="stream",
string=data,
}
end
end
if not setfontattributes then
setfontattributes=function(id,data)
print(format("your luatex is too old so no tounicode bitmap font%i",id))
end
end
function tfm.addtounicode(tfmdata)
local id=tfmdata.usedbitmap
local map={}
local char={}
for k,v in next,tfmdata.characters do
local index=v.oindex
local tounicode=v.tounicode
if index and tounicode then
map[index]=tounicode
end
end
for k,v in sortedhash(map) do
char[#char+1]=format("<%02X> <%s>",k,v)
end
char=concat(char,"\n")
local stream=format(template,id,id,#char,char)
local reference=flushstreamobject(stream,nil,true)
setfontattributes(id,format("/ToUnicode %i 0 R",reference))
end
end
do
local everywhere={ ["*"]={ ["*"]=true } }
local noflags={ false,false,false,false }
local function enhance_normalize_features(data)
local ligatures=setmetatableindex("table")
local kerns=setmetatableindex("table")
local characters=data.characters
for u,c in next,characters do
local l=c.ligatures
local k=c.kerns
if l then
ligatures[u]=l
for u,v in next,l do
l[u]={ ligature=v.char }
end
c.ligatures=nil
end
if k then
kerns[u]=k
for u,v in next,k do
k[u]=v
end
c.kerns=nil
end
end
for u,l in next,ligatures do
for k,v in next,l do
local vl=v.ligature
local dl=ligatures[vl]
if dl then
for kk,vv in next,dl do
v[kk]=vv
end
end
end
end
local features={
gpos={},
gsub={},
}
local sequences={
}
if next(ligatures) then
features.gsub.liga=everywhere
data.properties.hasligatures=true
sequences[#sequences+1]={
features={
liga=everywhere,
},
flags=noflags,
name="s_s_0",
nofsteps=1,
order={ "liga" },
type="gsub_ligature",
steps={
{
coverage=ligatures,
},
},
}
end
if next(kerns) then
features.gpos.kern=everywhere
data.properties.haskerns=true
sequences[#sequences+1]={
features={
kern=everywhere,
},
flags=noflags,
name="p_s_0",
nofsteps=1,
order={ "kern" },
type="gpos_pair",
steps={
{
format="kern",
coverage=kerns,
},
},
}
end
data.resources.features=features
data.resources.sequences=sequences
data.shared.resources=data.shared.resources or resources
end
registertfmenhancer("normalize features",enhance_normalize_features)
registertfmenhancer("check extra features",otfenhancers.enhance)
end
registertfmfeature {
name="mode",
description="mode",
initializers={
base=otf.modeinitializer,
node=otf.modeinitializer,
}
}
registertfmfeature {
name="features",
description="features",
default=true,
initializers={
base=otf.basemodeinitializer,
node=otf.nodemodeinitializer,
},
processors={
node=otf.featuresprocessor,
}
}
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-tfm”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-lua” 1fbfdf7b689b2bdfd0e3bb9bf74ce136] ---
if not modules then modules={} end modules ['font-lua']={
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_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end)
local report_lua=logs.reporter("fonts","lua loading")
local fonts=fonts
local readers=fonts.readers
fonts.formats.lua="lua"
local function check_lua(specification,fullname)
local fullname=resolvers.findfile(fullname) or ""
if fullname~="" then
local loader=loadfile(fullname)
loader=loader and loader()
return loader and loader(specification)
end
end
readers.check_lua=check_lua
function readers.lua(specification)
local original=specification.specification
if trace_defining then
report_lua("using lua reader for %a",original)
end
local fullname=specification.filename or ""
if fullname=="" then
local forced=specification.forced or ""
if forced~="" then
fullname=specification.name.."."..forced
else
fullname=specification.name
end
end
return check_lua(specification,fullname)
end
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-lua”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-def” 49fa2b50d8d2a1bb70b08b72f858ecd0] ---
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 lower,gsub=string.lower,string.gsub
local tostring,next=tostring,next
local lpegmatch=lpeg.match
local suffixonly,removesuffix=file.suffix,file.removesuffix
local formatters=string.formatters
local allocate=utilities.storage.allocate
local trace_defining=false trackers .register("fonts.defining",function(v) trace_defining=v end)
local directive_embedall=false directives.register("fonts.embedall",function(v) directive_embedall=v end)
trackers.register("fonts.loading","fonts.defining","otf.loading","afm.loading","tfm.loading")
trackers.register("fonts.all","fonts.*","otf.*","afm.*","tfm.*")
local report_defining=logs.reporter("fonts","defining")
local fonts=fonts
local fontdata=fonts.hashes.identifiers
local readers=fonts.readers
local definers=fonts.definers
local specifiers=fonts.specifiers
local constructors=fonts.constructors
local fontgoodies=fonts.goodies
readers.sequence=allocate { 'otf','ttf','afm','tfm','lua' }
local variants=allocate()
specifiers.variants=variants
definers.methods=definers.methods or {}
local internalized=allocate()
local lastdefined=nil
local loadedfonts=constructors.loadedfonts
local designsizes=constructors.designsizes
local resolvefile=fontgoodies and fontgoodies.filenames and fontgoodies.filenames.resolve or function(s) return s end
local splitter,splitspecifiers=nil,""
local P,C,S,Cc=lpeg.P,lpeg.C,lpeg.S,lpeg.Cc
local left=P("(")
local right=P(")")
local colon=P(":")
local space=P(" ")
definers.defaultlookup="file"
local prefixpattern=P(false)
local function addspecifier(symbol)
splitspecifiers=splitspecifiers..symbol
local method=S(splitspecifiers)
local lookup=C(prefixpattern)*colon
local sub=left*C(P(1-left-right-method)^1)*right
local specification=C(method)*C(P(1)^1)
local name=C((1-sub-specification)^1)
splitter=P((lookup+Cc(""))*name*(sub+Cc(""))*(specification+Cc("")))
end
local function addlookup(str,default)
prefixpattern=prefixpattern+P(str)
end
definers.addlookup=addlookup
addlookup("file")
addlookup("name")
addlookup("spec")
local function getspecification(str)
return lpegmatch(splitter,str or "")
end
definers.getspecification=getspecification
function definers.registersplit(symbol,action,verbosename)
addspecifier(symbol)
variants[symbol]=action
if verbosename then
variants[verbosename]=action
end
end
local function makespecification(specification,lookup,name,sub,method,detail,size)
size=size or 655360
if not lookup or lookup=="" then
lookup=definers.defaultlookup
end
if trace_defining then
report_defining("specification %a, lookup %a, name %a, sub %a, method %a, detail %a",
specification,lookup,name,sub,method,detail)
end
local t={
lookup=lookup,
specification=specification,
size=size,
name=name,
sub=sub,
method=method,
detail=detail,
resolved="",
forced="",
features={},
}
return t
end
definers.makespecification=makespecification
function definers.analyze(specification,size)
local lookup,name,sub,method,detail=getspecification(specification or "")
return makespecification(specification,lookup,name,sub,method,detail,size)
end
definers.resolvers=definers.resolvers or {}
local resolvers=definers.resolvers
function resolvers.file(specification)
local name=resolvefile(specification.name)
local suffix=lower(suffixonly(name))
if fonts.formats[suffix] then
specification.forced=suffix
specification.forcedname=name
specification.name=removesuffix(name)
else
specification.name=name
end
end
function resolvers.name(specification)
local resolve=fonts.names.resolve
if resolve then
local resolved,sub,subindex=resolve(specification.name,specification.sub,specification)
if resolved then
specification.resolved=resolved
specification.sub=sub
specification.subindex=subindex
local suffix=lower(suffixonly(resolved))
if fonts.formats[suffix] then
specification.forced=suffix
specification.forcedname=resolved
specification.name=removesuffix(resolved)
else
specification.name=resolved
end
end
else
resolvers.file(specification)
end
end
function resolvers.spec(specification)
local resolvespec=fonts.names.resolvespec
if resolvespec then
local resolved,sub,subindex=resolvespec(specification.name,specification.sub,specification)
if resolved then
specification.resolved=resolved
specification.sub=sub
specification.subindex=subindex
specification.forced=lower(suffixonly(resolved))
specification.forcedname=resolved
specification.name=removesuffix(resolved)
end
else
resolvers.name(specification)
end
end
function definers.resolve(specification)
if not specification.resolved or specification.resolved=="" then
local r=resolvers[specification.lookup]
if r then
r(specification)
end
end
if specification.forced=="" then
specification.forced=nil
specification.forcedname=nil
end
specification.hash=lower(specification.name..' @ '..constructors.hashfeatures(specification))
if specification.sub and specification.sub~="" then
specification.hash=specification.sub..' @ '..specification.hash
end
return specification
end
function definers.applypostprocessors(tfmdata)
local postprocessors=tfmdata.postprocessors
if postprocessors then
local properties=tfmdata.properties
for i=1,#postprocessors do
local extrahash=postprocessors[i](tfmdata)
if type(extrahash)=="string" and extrahash~="" then
extrahash=gsub(lower(extrahash),"[^a-z]","-")
properties.fullname=formatters["%s-%s"](properties.fullname,extrahash)
end
end
end
return tfmdata
end
local function checkembedding(tfmdata)
local properties=tfmdata.properties
local embedding
if directive_embedall then
embedding="full"
elseif properties and properties.filename and constructors.dontembed[properties.filename] then
embedding="no"
else
embedding="subset"
end
if properties then
properties.embedding=embedding
else
tfmdata.properties={ embedding=embedding }
end
tfmdata.embedding=embedding
end
function definers.loadfont(specification)
local hash=constructors.hashinstance(specification)
local tfmdata=loadedfonts[hash]
if not tfmdata then
local forced=specification.forced or ""
if forced~="" then
local reader=readers[lower(forced)]
tfmdata=reader and reader(specification)
if not tfmdata then
report_defining("forced type %a of %a not found",forced,specification.name)
end
else
local sequence=readers.sequence
for s=1,#sequence do
local reader=sequence[s]
if readers[reader] then
if trace_defining then
report_defining("trying (reader sequence driven) type %a for %a with file %a",reader,specification.name,specification.filename)
end
tfmdata=readers[reader](specification)
if tfmdata then
break
else
specification.filename=nil
end
end
end
end
if tfmdata then
tfmdata=definers.applypostprocessors(tfmdata)
checkembedding(tfmdata)
loadedfonts[hash]=tfmdata
designsizes[specification.hash]=tfmdata.parameters.designsize
end
end
if not tfmdata then
report_defining("font with asked name %a is not found using lookup %a",specification.name,specification.lookup)
end
return tfmdata
end
function constructors.checkvirtualids()
end
function constructors.readanddefine(name,size)
local specification=definers.analyze(name,size)
local method=specification.method
if method and variants[method] then
specification=variants[method](specification)
end
specification=definers.resolve(specification)
local hash=constructors.hashinstance(specification)
local id=definers.registered(hash)
if not id then
local tfmdata=definers.loadfont(specification)
if tfmdata then
tfmdata.properties.hash=hash
constructors.checkvirtualids(tfmdata)
id=font.define(tfmdata)
definers.register(tfmdata,id)
else
id=0
end
end
return fontdata[id],id
end
function definers.current()
return lastdefined
end
function definers.registered(hash)
local id=internalized[hash]
return id,id and fontdata[id]
end
function definers.register(tfmdata,id)
if tfmdata and id then
local hash=tfmdata.properties.hash
if not hash then
report_defining("registering font, id %a, name %a, invalid hash",id,tfmdata.properties.filename or "?")
elseif not internalized[hash] then
internalized[hash]=id
if trace_defining then
report_defining("registering font, id %s, hash %a",id,hash)
end
fontdata[id]=tfmdata
end
end
end
function definers.read(specification,size,id)
statistics.starttiming(fonts)
if type(specification)=="string" then
specification=definers.analyze(specification,size)
end
local method=specification.method
if method and variants[method] then
specification=variants[method](specification)
end
specification=definers.resolve(specification)
local hash=constructors.hashinstance(specification)
local tfmdata=definers.registered(hash)
if tfmdata then
if trace_defining then
report_defining("already hashed: %s",hash)
end
else
tfmdata=definers.loadfont(specification)
if tfmdata then
if trace_defining then
report_defining("loaded and hashed: %s",hash)
end
tfmdata.properties.hash=hash
if id then
definers.register(tfmdata,id)
end
else
if trace_defining then
report_defining("not loaded and hashed: %s",hash)
end
end
end
lastdefined=tfmdata or id
if not tfmdata then
report_defining("unknown font %a, loading aborted",specification.name)
elseif trace_defining and type(tfmdata)=="table" then
local properties=tfmdata.properties or {}
local parameters=tfmdata.parameters or {}
report_defining("using %a font with id %a, name %a, size %a, bytes %a, encoding %a, fullname %a, filename %a",
properties.format or "unknown",id,properties.name,parameters.size,properties.encodingbytes,
properties.encodingname,properties.fullname,file.basename(properties.filename))
end
statistics.stoptiming(fonts)
return tfmdata
end
function font.getfont(id)
return fontdata[id]
end
callbacks.register('define_font',definers.read,"definition of fonts (tfmdata preparation)")
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-def”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “fonts-ext” aff3846f4c1f15de0a9f4fd7081e0c68] ---
if not modules then modules={} end modules ['luatex-fonts-ext']={
version=1.001,
comment="companion to luatex-*.tex",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
if context then
texio.write_nl("fatal error: this module is not for context")
os.exit()
end
local fonts=fonts
local otffeatures=fonts.constructors.features.otf
local function initializeitlc(tfmdata,value)
if value then
local parameters=tfmdata.parameters
local italicangle=parameters.italicangle
if italicangle and italicangle~=0 then
local properties=tfmdata.properties
local factor=tonumber(value) or 1
properties.hasitalics=true
properties.autoitalicamount=factor*(parameters.uwidth or 40)/2
end
end
end
otffeatures.register {
name="itlc",
description="italic correction",
initializers={
base=initializeitlc,
node=initializeitlc,
}
}
local function initializeslant(tfmdata,value)
value=tonumber(value)
if not value then
value=0
elseif value>1 then
value=1
elseif value<-1 then
value=-1
end
tfmdata.parameters.slantfactor=value
end
otffeatures.register {
name="slant",
description="slant glyphs",
initializers={
base=initializeslant,
node=initializeslant,
}
}
local function initializeextend(tfmdata,value)
value=tonumber(value)
if not value then
value=0
elseif value>10 then
value=10
elseif value<-10 then
value=-10
end
tfmdata.parameters.extendfactor=value
end
otffeatures.register {
name="extend",
description="scale glyphs horizontally",
initializers={
base=initializeextend,
node=initializeextend,
}
}
fonts.protrusions=fonts.protrusions or {}
fonts.protrusions.setups=fonts.protrusions.setups or {}
local setups=fonts.protrusions.setups
local function initializeprotrusion(tfmdata,value)
if value then
local setup=setups[value]
if setup then
local factor,left,right=setup.factor or 1,setup.left or 1,setup.right or 1
local emwidth=tfmdata.parameters.quad
tfmdata.parameters.protrusion={
auto=true,
}
for i,chr in next,tfmdata.characters do
local v,pl,pr=setup[i],nil,nil
if v then
pl,pr=v[1],v[2]
end
if pl and pl~=0 then chr.left_protruding=left*pl*factor end
if pr and pr~=0 then chr.right_protruding=right*pr*factor end
end
end
end
end
otffeatures.register {
name="protrusion",
description="shift characters into the left and or right margin",
initializers={
base=initializeprotrusion,
node=initializeprotrusion,
}
}
fonts.expansions=fonts.expansions or {}
fonts.expansions.setups=fonts.expansions.setups or {}
local setups=fonts.expansions.setups
local function initializeexpansion(tfmdata,value)
if value then
local setup=setups[value]
if setup then
local factor=setup.factor or 1
tfmdata.parameters.expansion={
stretch=10*(setup.stretch or 0),
shrink=10*(setup.shrink or 0),
step=10*(setup.step or 0),
auto=true,
}
for i,chr in next,tfmdata.characters do
local v=setup[i]
if v and v~=0 then
chr.expansion_factor=v*factor
else
chr.expansion_factor=factor
end
end
end
end
end
otffeatures.register {
name="expansion",
description="apply hz optimization",
initializers={
base=initializeexpansion,
node=initializeexpansion,
}
}
function fonts.loggers.onetimemessage() end
local byte=string.byte
fonts.expansions.setups['default']={
stretch=2,shrink=2,step=.5,factor=1,
[byte('A')]=0.5,[byte('B')]=0.7,[byte('C')]=0.7,[byte('D')]=0.5,[byte('E')]=0.7,
[byte('F')]=0.7,[byte('G')]=0.5,[byte('H')]=0.7,[byte('K')]=0.7,[byte('M')]=0.7,
[byte('N')]=0.7,[byte('O')]=0.5,[byte('P')]=0.7,[byte('Q')]=0.5,[byte('R')]=0.7,
[byte('S')]=0.7,[byte('U')]=0.7,[byte('W')]=0.7,[byte('Z')]=0.7,
[byte('a')]=0.7,[byte('b')]=0.7,[byte('c')]=0.7,[byte('d')]=0.7,[byte('e')]=0.7,
[byte('g')]=0.7,[byte('h')]=0.7,[byte('k')]=0.7,[byte('m')]=0.7,[byte('n')]=0.7,
[byte('o')]=0.7,[byte('p')]=0.7,[byte('q')]=0.7,[byte('s')]=0.7,[byte('u')]=0.7,
[byte('w')]=0.7,[byte('z')]=0.7,
[byte('2')]=0.7,[byte('3')]=0.7,[byte('6')]=0.7,[byte('8')]=0.7,[byte('9')]=0.7,
}
fonts.protrusions.setups['default']={
factor=1,left=1,right=1,
[0x002C]={ 0,1 },
[0x002E]={ 0,1 },
[0x003A]={ 0,1 },
[0x003B]={ 0,1 },
[0x002D]={ 0,1 },
[0x2013]={ 0,0.50 },
[0x2014]={ 0,0.33 },
[0x3001]={ 0,1 },
[0x3002]={ 0,1 },
[0x060C]={ 0,1 },
[0x061B]={ 0,1 },
[0x06D4]={ 0,1 },
}
fonts.handlers.otf.features.normalize=function(t)
if t.rand then
t.rand="random"
end
return t
end
function fonts.helpers.nametoslot(name)
local t=type(name)
if t=="string" then
local tfmdata=fonts.hashes.identifiers[currentfont()]
local shared=tfmdata and tfmdata.shared
local fntdata=shared and shared.rawdata
return fntdata and fntdata.resources.unicodes[name]
elseif t=="number" then
return n
end
end
fonts.encodings=fonts.encodings or {}
local reencodings={}
fonts.encodings.reencodings=reencodings
local function specialreencode(tfmdata,value)
local encoding=value and reencodings[value]
if encoding then
local temp={}
local char=tfmdata.characters
for k,v in next,encoding do
temp[k]=char[v]
end
for k,v in next,temp do
char[k]=temp[k]
end
return string.format("reencoded:%s",value)
end
end
local function reencode(tfmdata,value)
tfmdata.postprocessors=tfmdata.postprocessors or {}
table.insert(tfmdata.postprocessors,
function(tfmdata)
return specialreencode(tfmdata,value)
end
)
end
otffeatures.register {
name="reencode",
description="reencode characters",
manipulators={
base=reencode,
node=reencode,
}
}
local function ignore(tfmdata,key,value)
if value then
tfmdata.mathparameters=nil
end
end
otffeatures.register {
name="ignoremathconstants",
description="ignore math constants table",
initializers={
base=ignore,
node=ignore,
}
}
end --- [luaotfload, fontloader-2017-02-11.lua scope for “fonts-ext”] ---
do --- [luaotfload, fontloader-2017-02-11.lua scope for “font-gbn” 850f31ba73ff8de96371d0aed2b2b4cb] ---
if not modules then modules={} end modules ['font-gbn']={
version=1.001,
comment="companion to luatex-*.tex",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
if context then
texio.write_nl("fatal error: this module is not for context")
os.exit()
end
local fonts=fonts
local nodes=nodes
local nuts=nodes.nuts
local traverse_id=nuts.traverse_id
local flush_node=nuts.flush_node
local glyph_code=nodes.nodecodes.glyph
local disc_code=nodes.nodecodes.disc
local tonode=nuts.tonode
local tonut=nuts.tonut
local getfont=nuts.getfont
local getchar=nuts.getchar
local getid=nuts.getid
local getboth=nuts.getboth
local getprev=nuts.getprev
local getnext=nuts.getnext
local getdisc=nuts.getdisc
local setchar=nuts.setchar
local setlink=nuts.setlink
local setprev=nuts.setprev
local nodetail=nuts.tail
local n_ligaturing=node.ligaturing
local n_kerning=node.kerning
local ligaturing=nuts.ligaturing
local kerning=nuts.kerning
local basemodepass=true
local function l_warning() texio.write_nl("warning: node.ligaturing called directly") l_warning=nil end
local function k_warning() texio.write_nl("warning: node.kerning called directly") k_warning=nil end
function node.ligaturing(...)
if basemodepass and l_warning then
l_warning()
end
return n_ligaturing(...)
end
function node.kerning(...)
if basemodepass and k_warning then
k_warning()
end
return n_kerning(...)
end
function nodes.handlers.setbasemodepass(v)
basemodepass=v
end
function nodes.handlers.nodepass(head)
local fontdata=fonts.hashes.identifiers
if fontdata then
local nuthead=tonut(head)
local usedfonts={}
local basefonts={}
local prevfont=nil
local basefont=nil
local variants=nil
local redundant=nil
for n in traverse_id(glyph_code,nuthead) do
local font=getfont(n)
if font~=prevfont then
if basefont then
basefont[2]=getprev(n)
end
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
elseif basemodepass then
basefont={ n,nil }
basefonts[#basefonts+1]=basefont
end
end
local resources=tfmdata.resources
variants=resources and resources.variants
variants=variants and next(variants) and variants or false
end
else
local tfmdata=fontdata[prevfont]
if tfmdata then
local resources=tfmdata.resources
variants=resources and resources.variants
variants=variants and next(variants) and variants or false
end
end
end
if variants then
local char=getchar(n)
if char>=0xFE00 and (char<=0xFE0F or (char>=0xE0100 and char<=0xE01EF)) then
local hash=variants[char]
if hash then
local p=getprev(n)
if p and getid(p)==glyph_code then
local variant=hash[getchar(p)]
if variant then
setchar(p,variant)
end
end
end
if not redundant then
redundant={ n }
else
redundant[#redundant+1]=n
end
end
end
end
local nofbasefonts=#basefonts
if redundant then
for i=1,#redundant do
local r=redundant[i]
local p,n=getboth(r)
if r==nuthead then
nuthead=n
setprev(n)
else
setlink(p,n)
end
if nofbasefonts>0 then
for i=1,nofbasefonts do
local bi=basefonts[i]
if r==bi[1] then
bi[1]=n
end
if r==bi[2] then
bi[2]=n
end
end
end
flush_node(r)
end
end
for d in traverse_id(disc_code,nuthead) do
local _,_,r=getdisc(d)
if r then
for n in traverse_id(glyph_code,r) do
local font=getfont(n)
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
end
end
end
end
end
end
end
end
if next(usedfonts) then
for font,processors in next,usedfonts do
for i=1,#processors do
head=processors[i](head,font,0) or head
end
end
end
if basemodepass and nofbasefonts>0 then
for i=1,nofbasefonts do
local range=basefonts[i]
local start=range[1]
local stop=range[2]
if start then
local front=nuthead==start
if not stop then
stop=nodetail(start)
end
if stop then
start,stop=ligaturing(start,stop)
start,stop=kerning(start,stop)
elseif start then
start,stop=ligaturing(start,stop)
start,stop=kerning(start,stop)
end
if front and nuthead~=start then
head=tonode(start)
end
end
end
end
return head,true
else
return head,false
end
end
function nodes.handlers.basepass(head)
if not basemodepass then
head=n_ligaturing(head)
head=n_kerning(head)
end
return head,true
end
local nodepass=nodes.handlers.nodepass
local basepass=nodes.handlers.basepass
local injectpass=nodes.injections.handler
local protectpass=nodes.handlers.protectglyphs
function nodes.simple_font_handler(head)
if head then
head=nodepass(head)
head=injectpass(head)
if not basemodepass then
head=basepass(head)
end
protectpass(head)
return head,true
else
return head,false
end
end
end --- [luaotfload, fontloader-2017-02-11.lua scope for “font-gbn”] ---